pytrendy 1.2.0.dev5__tar.gz → 1.2.0.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/PKG-INFO +1 -1
  2. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pyproject.toml +1 -1
  3. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_get.py +2 -0
  4. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/gradual_expand_contract.py +27 -0
  5. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/LICENSE +0 -0
  6. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/README.md +0 -0
  7. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/__init__.py +0 -0
  8. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/detect_trends.py +0 -0
  9. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/__init__.py +0 -0
  10. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/data/classes_signals.csv +0 -0
  11. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/data/series_synthetic.csv +0 -0
  12. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/data_loader.py +0 -0
  13. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/plot_pytrendy.py +0 -0
  14. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/io/results_pytrendy.py +0 -0
  15. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/__init__.py +0 -0
  16. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_analyse.py +0 -0
  17. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/__init__.py +0 -0
  18. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/abrupt_shaving.py +0 -0
  19. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/artifact_cleanup.py +0 -0
  20. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/segment_grouping.py +0 -0
  21. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/trend_classify.py +0 -0
  22. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/post_processing/segments_refine/update_neighbours.py +0 -0
  23. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/process_signals.py +0 -0
  24. {pytrendy-1.2.0.dev5 → pytrendy-1.2.0.dev6}/pytrendy/simpledtw.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrendy
3
- Version: 1.2.0.dev5
3
+ Version: 1.2.0.dev6
4
4
  Summary: Trend Detection in Python. Applicable for real-world industry use cases in time series.
5
5
  License: MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pytrendy"
3
- version = "1.2.0.dev5"
3
+ version = "1.2.0.dev6"
4
4
  description = "Trend Detection in Python. Applicable for real-world industry use cases in time series."
5
5
  authors = [
6
6
  { name = "Russell Sammut Bonnici", email = "r.sammutbonnici@gmail.com" },
@@ -72,6 +72,8 @@ def get_segments(df: pd.DataFrame) -> list[dict]:
72
72
  , 'end': end.strftime('%Y-%m-%d')
73
73
  })
74
74
  segment_length=0
75
+ else:
76
+ segment_length = 0 # Reset on fail: prevent length bleed into next segment
75
77
 
76
78
  direction_prev = direction
77
79
  segment_length_prev = segment_length
@@ -71,6 +71,33 @@ def expand_contract_segments(df: pd.DataFrame, value_col: str, segments: list[di
71
71
  else:
72
72
  continue
73
73
 
74
+ # Avoid orphaning a peak/trough that no adjacent segment covers.
75
+ # The +1 day pushes start past the extremum, assuming it belongs to
76
+ # the neighbour. When no neighbour is near and the extremum holds a
77
+ # significantly different value, the +1 day skips a genuine drop/rise.
78
+ # Skip this when the previous segment is Noise — noise boundaries are
79
+ # deliberately fuzzy and shouldn't anchor the orphan test.
80
+ prev_seg = segments_refined[i - 1] if i > 0 else None
81
+ prev_is_noise = prev_seg is not None and prev_seg.get('direction') == 'Noise'
82
+ if segment['direction'] in ('Up', 'Down') and i > 0:
83
+ prev_end = pd.to_datetime(segments_refined[i - 1]['end'])
84
+ extremum = pd.to_datetime(new_start) - pd.Timedelta(days=1)
85
+ distance = (extremum - prev_end).days
86
+ # Skip orphan check when previous segment is Noise AND the Noise
87
+ # is close (within 3 days) to the extremum — noise boundaries are
88
+ # deliberately fuzzy in that case. When Noise is far away, the
89
+ # extremum is genuinely orphaned and should be captured.
90
+ # For non-Noise neighbours, keep the original distance > 1 gate.
91
+ if prev_is_noise and distance <= 3:
92
+ pass
93
+ elif (not prev_is_noise and distance > 1) or (prev_is_noise and distance > 3):
94
+ if extremum in df.index and new_start in df.index:
95
+ extremum_val = df.loc[extremum, value_col]
96
+ start_val = df.loc[new_start, value_col]
97
+ max_abs = df[value_col].abs().max()
98
+ if max_abs > 0 and abs(extremum_val - start_val) > 0.2 * max_abs:
99
+ new_start -= pd.Timedelta(days=1)
100
+
74
101
  # Check for any inversions
75
102
  start_inverted = (new_start >= pd.to_datetime(segment['end']))
76
103
  end_inverted = (new_end <= pd.to_datetime(segment['start']))
File without changes
File without changes