pytrendy 1.3.1__tar.gz → 1.4.0.dev2__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.3.1 → pytrendy-1.4.0.dev2}/PKG-INFO +1 -1
  2. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pyproject.toml +1 -1
  3. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/detect_trends.py +14 -2
  4. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/plot_pytrendy.py +63 -21
  5. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/LICENSE +0 -0
  6. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/README.md +0 -0
  7. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/__init__.py +0 -0
  8. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/__init__.py +0 -0
  9. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/data/classes_signals.csv +0 -0
  10. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/data/series_synthetic.csv +0 -0
  11. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/data_loader.py +0 -0
  12. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/io/results_pytrendy.py +0 -0
  13. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/__init__.py +0 -0
  14. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_analyse.py +0 -0
  15. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_get.py +0 -0
  16. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/__init__.py +0 -0
  17. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/abrupt_shaving.py +0 -0
  18. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/artifact_cleanup.py +0 -0
  19. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/gradual_expand_contract.py +0 -0
  20. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/segment_grouping.py +0 -0
  21. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/trend_classify.py +0 -0
  22. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/post_processing/segments_refine/update_neighbours.py +0 -0
  23. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/process_signals.py +0 -0
  24. {pytrendy-1.3.1 → pytrendy-1.4.0.dev2}/pytrendy/simpledtw.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrendy
3
- Version: 1.3.1
3
+ Version: 1.4.0.dev2
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.3.1"
3
+ version = "1.4.0.dev2"
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" },
@@ -9,7 +9,7 @@ from .post_processing.segments_analyse import analyse_segments
9
9
  from .io.plot_pytrendy import plot_pytrendy
10
10
  from .io.results_pytrendy import PyTrendyResults
11
11
 
12
- def detect_trends(df: pd.DataFrame, date_col: str, value_col: str, plot=True, method_params: dict=None, debug: bool=False ) -> PyTrendyResults:
12
+ def detect_trends(df: pd.DataFrame, date_col: str, value_col: str, plot=True, method_params: dict=None, plot_params: dict=None, debug: bool=False ) -> PyTrendyResults:
13
13
  """
14
14
  This is the main function that runs trend detection end-to-end.
15
15
 
@@ -42,6 +42,18 @@ def detect_trends(df: pd.DataFrame, date_col: str, value_col: str, plot=True, me
42
42
 
43
43
  - **abrupt_padding** (`int`): Number of days to pad around abrupt transitions. Defaults to `0`.
44
44
  - **avoid_noise** (`bool`): Whether to avoid noisy segments in trend detection. Defaults to `True`.
45
+ plot_params (dict, optional):
46
+ Optional dict to customise plot appearance. Only used when `plot` is `True`. Supported keys:
47
+
48
+ - **figsize** (`tuple`): Figure size as (width, height). Defaults to (20, 5).
49
+ - **title** (`str`): Plot title. Defaults to "PyTrendy Detection".
50
+ - **xlabel** (`str`): X-axis label. Defaults to "Date".
51
+ - **ylabel** (`str`): Y-axis label. Defaults to "Value".
52
+ - **colors** (`dict`): Dictionary mapping direction ('Up', 'Down', 'Flat', 'Noise') to matplotlib colors. Defaults to light variants.
53
+ - **alpha** (`float`): Transparency level for shaded regions. Defaults to 0.4.
54
+ - **grid** (`dict`): Grid configuration with keys 'visible' (bool), 'which' (str), 'color' (str), 'alpha' (float).
55
+ - **legend_loc** (`str`): Legend location. Defaults to "upper right".
56
+ - **legend_bbox_to_anchor** (`tuple`): Legend box anchor position. Defaults to (1, 1.15).
45
57
  debug (bool, optional):
46
58
  If `True` will run in debug mode, outputting various additional plots and print statements. Only recommended for developers of pytrendy.
47
59
  Defaults to `False`.
@@ -79,7 +91,7 @@ def detect_trends(df: pd.DataFrame, date_col: str, value_col: str, plot=True, me
79
91
  segments = get_segments(df)
80
92
  segments = refine_segments(df, value_col, segments, method_params)
81
93
  segments = analyse_segments(df, value_col, segments)
82
- if plot: plot_pytrendy(df, value_col, segments)
94
+ if plot: plot_pytrendy(df, value_col, segments, plot_params=plot_params)
83
95
 
84
96
  results = PyTrendyResults(segments)
85
97
  return results
@@ -5,7 +5,7 @@ import matplotlib.pyplot as plt
5
5
  import matplotlib.dates as mdates
6
6
  import matplotlib.patches as mpatches
7
7
 
8
- def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict], suppress_show: bool = False) -> plt.Figure:
8
+ def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict], suppress_show: bool = False, plot_params: dict = None) -> plt.Figure:
9
9
  """
10
10
  Visualizes detected trend segments over the original time series signal.
11
11
 
@@ -21,21 +21,59 @@ def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict
21
21
  List of segment dictionaries containing keys like `'start'`, `'end'`, `'direction'`, `'trend_class'`, and `'change_rank'`.
22
22
  suppress_show (bool, optional):
23
23
  If True, suppresses the automatic display of the plot with plt.show(). Defaults to False.
24
+ plot_params (dict, optional):
25
+ Optional dict to customise plot appearance. Supported keys:
26
+
27
+ - **figsize** (`tuple`): Figure size as (width, height). Defaults to (20, 5).
28
+ - **title** (`str`): Plot title. Defaults to "PyTrendy Detection".
29
+ - **xlabel** (`str`): X-axis label. Defaults to "Date".
30
+ - **ylabel** (`str`): Y-axis label. Defaults to "Value".
31
+ - **colors** (`dict`): Dictionary mapping direction ('Up', 'Down', 'Flat', 'Noise') to matplotlib colors. Defaults to light variants.
32
+ - **alpha** (`float`): Transparency level for shaded regions. Defaults to 0.4.
33
+ - **grid** (`dict`): Grid configuration with keys 'visible' (bool), 'which' (str), 'color' (str), 'alpha' (float).
34
+ - **legend_loc** (`str`): Legend location. Defaults to "upper right".
35
+ - **legend_bbox_to_anchor** (`tuple`): Legend box anchor position. Defaults to (1, 1.15).
24
36
 
25
37
  Returns:
26
38
  matplotlib.figure.Figure:
27
39
  The figure object containing the plot. Can be displayed with `plt.show()` or saved.
28
40
  """
29
41
 
30
- # Define colours
31
- color_map = {
32
- 'Up': 'lightgreen',
33
- 'Down': 'lightcoral',
34
- 'Flat': 'lightblue',
35
- 'Noise': 'lightgray',
42
+ # Default plotting params
43
+ default_params = {
44
+ 'figsize': (20, 5),
45
+ 'title': "PyTrendy Detection",
46
+ 'xlabel': "Date",
47
+ 'ylabel': "Value",
48
+ 'colors': {
49
+ 'Up': 'lightgreen',
50
+ 'Down': 'lightcoral',
51
+ 'Flat': 'lightblue',
52
+ 'Noise': 'lightgray',
53
+ },
54
+ 'alpha': 0.4,
55
+ 'grid': {'visible': True, 'which': 'major', 'color': 'gray', 'alpha': 0.3},
56
+ 'legend_loc': 'upper right',
57
+ 'legend_bbox_to_anchor': (1, 1.15)
36
58
  }
37
-
38
- fig, ax = plt.subplots(figsize=(20, 5))
59
+ if plot_params:
60
+ plot_params = dict(plot_params) # avoid mutating caller's dict
61
+ has_custom_legend_loc = 'legend_loc' in plot_params
62
+ has_custom_legend_anchor = 'legend_bbox_to_anchor' in plot_params
63
+ custom_colors = plot_params.pop('colors', None)
64
+ custom_grid = plot_params.pop('grid', None)
65
+ default_params.update(plot_params)
66
+ if custom_colors:
67
+ default_params['colors'].update(custom_colors)
68
+ if custom_grid:
69
+ default_params['grid'].update(custom_grid)
70
+ if has_custom_legend_loc and not has_custom_legend_anchor:
71
+ default_params['legend_bbox_to_anchor'] = None
72
+
73
+ # Define colors
74
+ color_map = default_params['colors']
75
+
76
+ fig, ax = plt.subplots(figsize=default_params['figsize'])
39
77
 
40
78
  # Plot the value line
41
79
  ax.plot(df.index, df[value_col], color='black', lw=1)
@@ -84,7 +122,7 @@ def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict
84
122
  prev_new_end = (prev_end + pd.Timedelta(days=1)).strftime('%Y-%m-%d')
85
123
  mask = (df.index >= prev_end) & (df.index <= prev_new_end)
86
124
  prev_color = color_map.get(segments_enhanced[i-1]['direction'], 'gray')
87
- ax.fill_between(df.index[mask], ymin, ymax, color=prev_color, alpha=0.4)
125
+ ax.fill_between(df.index[mask], ymin, ymax, color=prev_color, alpha=default_params['alpha'])
88
126
 
89
127
 
90
128
  # Adjust ends when appropriate
@@ -108,7 +146,7 @@ def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict
108
146
  end = end
109
147
 
110
148
  mask = (df.index >= start) & (df.index <= end)
111
- ax.fill_between(df.index[mask], ymin, ymax, color=color, alpha=0.4)
149
+ ax.fill_between(df.index[mask], ymin, ymax, color=color, alpha=default_params['alpha'])
112
150
 
113
151
  # Add ranking if up/down trend
114
152
  if 'change_rank' in seg and seg['direction'] in ['Up', 'Down']:
@@ -140,21 +178,25 @@ def plot_pytrendy(df: pd.DataFrame, value_col: str, segments_enhanced: list[dict
140
178
  plt.setp(ax.get_xticklabels(), rotation=90, ha='right')
141
179
 
142
180
  # Optional: show grid lines for both
143
- ax.grid(True, which='major', color='gray', alpha=0.3)
181
+ grid_params = default_params['grid']
182
+ if grid_params['visible']:
183
+ ax.grid(True, **{k: v for k, v in grid_params.items() if k != 'visible'})
184
+ else:
185
+ ax.grid(False, which='both')
144
186
 
145
- ax.set_title("PyTrendy Detection", fontsize=20)
146
- ax.set_xlabel("Date")
147
- ax.set_ylabel("Value")
187
+ ax.set_title(default_params['title'], fontsize=20)
188
+ ax.set_xlabel(default_params['xlabel'])
189
+ ax.set_ylabel(default_params['ylabel'])
148
190
 
149
191
  # Create custom legend handles (colored boxes)
150
192
  legend_handles = [
151
- mpatches.Patch(color='lightgreen', alpha=0.4, label='Up'),
152
- mpatches.Patch(color='lightcoral', alpha=0.4, label='Down'),
153
- mpatches.Patch(color='lightblue', alpha=0.4, label='Flat'),
154
- mpatches.Patch(color='lightgray', alpha=0.4, label='Noise'),
193
+ mpatches.Patch(color=default_params['colors']['Up'], alpha=default_params['alpha'], label='Up'),
194
+ mpatches.Patch(color=default_params['colors']['Down'], alpha=default_params['alpha'], label='Down'),
195
+ mpatches.Patch(color=default_params['colors']['Flat'], alpha=default_params['alpha'], label='Flat'),
196
+ mpatches.Patch(color=default_params['colors']['Noise'], alpha=default_params['alpha'], label='Noise'),
155
197
  ]
156
- ax.legend(handles=legend_handles, loc='upper right',
157
- bbox_to_anchor=(1, 1.15), ncol=4, frameon=True)
198
+ ax.legend(handles=legend_handles, loc=default_params['legend_loc'],
199
+ bbox_to_anchor=default_params['legend_bbox_to_anchor'], ncol=4, frameon=True)
158
200
 
159
201
  plt.tight_layout()
160
202
  if not suppress_show:
File without changes
File without changes