pynamicalsys 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,155 @@
1
+ # plot_styler.py
2
+
3
+ # Copyright (C) 2025 Matheus Rolim Sales
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import matplotlib as mpl
19
+ import matplotlib.pyplot as plt
20
+ from typing import Optional
21
+
22
+
23
+ class PlotStyler:
24
+ """
25
+ A utility class to globally configure and apply consistent styling for Matplotlib plots.
26
+
27
+ This class sets default plot aesthetics such as font sizes, line widths, marker styles,
28
+ tick behavior, and more, using Matplotlib's rcParams. It ensures consistent visual style
29
+ across multiple figures and subplots with minimal repetitive code.
30
+
31
+ Parameters
32
+ ----------
33
+ fontsize : int, default=20
34
+ Base font size for labels, titles, and text.
35
+ legend_fontsize : int, default=14
36
+ Font size for the legend.
37
+ axes_linewidth : float, default=1.3
38
+ Width of the axes borders.
39
+ font_family : str, default="STIXGeneral"
40
+ Font family used for text and math rendering.
41
+ line_width : float, default=2.0
42
+ Default line width for plot lines.
43
+ markersize : float, default=6.0
44
+ Default marker size for lines with markers.
45
+ markeredgewidth : float, default=1.0
46
+ Edge width for plot markers.
47
+ markeredgecolor : str, default="black"
48
+ Edge color for plot markers.
49
+ tick_direction_in : bool, default=True
50
+ Whether ticks point inward (`True`) or outward (`False`).
51
+ minor_ticks_visible : bool, default=True
52
+ Whether minor ticks are visible.
53
+
54
+ Methods
55
+ -------
56
+ apply_style()
57
+ Applies the defined styling parameters globally using Matplotlib's rcParams.
58
+ set_tick_padding(ax, pad_x=None, pad_y=None)
59
+ Sets custom padding between axis ticks and labels for a given Axes object.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ fontsize: int = 20,
65
+ legend_fontsize: int = 14,
66
+ axes_linewidth: float = 1.3,
67
+ font_family: str = "STIXGeneral",
68
+ tick_direction_in: bool = True,
69
+ minor_ticks_visible: bool = False,
70
+ linewidth: float = 1.0,
71
+ markersize: float = 5.0,
72
+ markeredgewidth: float = 1.0,
73
+ markeredgecolor: str = "black",
74
+ ticks_on_all_sides: bool = True,
75
+ ) -> None:
76
+ self.fontsize = fontsize
77
+ self.legend_fontsize = legend_fontsize
78
+ self.axes_linewidth = axes_linewidth
79
+ self.font_family = font_family
80
+ self.tick_labelsize = fontsize - 3
81
+ self.tick_direction_in = (tick_direction_in,)
82
+ self.minor_ticks_visible = minor_ticks_visible
83
+ self.linewidth = linewidth
84
+ self.markersize = markersize
85
+ self.markeredgewidth = markeredgewidth
86
+ self.markeredgecolor = markeredgecolor
87
+ self.ticks_on_all_sides = ticks_on_all_sides
88
+
89
+ def apply_style(self) -> None:
90
+ """
91
+ Apply global matplotlib styling.
92
+ """
93
+ self._reset_style() # Reset any previous styles
94
+ plt.clf()
95
+ plt.rc("font", size=self.fontsize)
96
+ plt.rc("xtick", labelsize=self.tick_labelsize)
97
+ plt.rc("ytick", labelsize=self.tick_labelsize)
98
+ plt.rc("legend", fontsize=self.legend_fontsize)
99
+
100
+ # Font and math rendering
101
+ plt.rc("font", family=self.font_family)
102
+ plt.rcParams["mathtext.fontset"] = "stix"
103
+
104
+ # Axes linewidth
105
+ mpl.rcParams["axes.linewidth"] = self.axes_linewidth
106
+
107
+ # Global tick settings
108
+ if self.tick_direction_in:
109
+ plt.rcParams["xtick.direction"] = "in"
110
+ plt.rcParams["ytick.direction"] = "in"
111
+
112
+ if self.minor_ticks_visible:
113
+ plt.rcParams["xtick.minor.visible"] = True
114
+ plt.rcParams["ytick.minor.visible"] = True
115
+
116
+ # Ticks on all sides (requires setting this per axis after creation)
117
+ if self.ticks_on_all_sides:
118
+ mpl.rcParams["xtick.top"] = True
119
+ mpl.rcParams["ytick.right"] = True
120
+
121
+ # Line and marker styling
122
+ plt.rcParams["lines.linewidth"] = self.linewidth
123
+ plt.rcParams["lines.markersize"] = self.markersize
124
+ plt.rcParams["lines.markeredgewidth"] = self.markeredgewidth
125
+ plt.rcParams["lines.markeredgecolor"] = self.markeredgecolor
126
+
127
+ def set_tick_padding(
128
+ self, ax: plt.Axes, pad_x: Optional[int] = None, pad_y: Optional[int] = None
129
+ ) -> None:
130
+ """
131
+ Set custom tick padding for a single Axes.
132
+
133
+ Parameters
134
+ ----------
135
+ ax : matplotlib Axes
136
+ The axes object to modify.
137
+ pad_x : int, optional
138
+ Padding for x-axis major ticks.
139
+ pad_y : int, optional
140
+ Padding for y-axis major ticks.
141
+ """
142
+ if pad_x is not None:
143
+ ax.tick_params(axis="x", which="major", pad=pad_x)
144
+ if pad_y is not None:
145
+ ax.tick_params(axis="y", which="major", pad=pad_y)
146
+
147
+ def _reset_style(self) -> None:
148
+ """
149
+ Reset all matplotlib settings to default values.
150
+
151
+ This method undoes any customizations made by `apply_style`
152
+ or other changes to `matplotlib.rcParams`.
153
+ """
154
+ mpl.rcdefaults()
155
+ plt.rcdefaults()
@@ -0,0 +1,139 @@
1
+ # time_series_metrics.py
2
+
3
+ # Copyright (C) 2025 Matheus Rolim Sales
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import numpy as np
19
+ from numpy.typing import NDArray
20
+
21
+ from pynamicalsys.common.recurrence_quantification_analysis import (
22
+ recurrence_matrix,
23
+ RTEConfig,
24
+ white_vertline_distr,
25
+ )
26
+
27
+
28
+ class TimeSeriesMetrics:
29
+ def __init__(self, time_series: NDArray[np.float64]) -> None:
30
+ """
31
+ Initialize the TimeSeriesMetrics class.
32
+
33
+ This class provides methods to compute metrics related to time series analysis,
34
+ such as survival probability and in a future release, the autocorrelation function.
35
+ """
36
+ self.time_series = time_series
37
+
38
+ # The time series can be either 1D (shape(N,)) or 2D (shape(N, dim))
39
+ if time_series.ndim not in {1, 2}:
40
+ raise ValueError("time_series must be 1D or 2D array")
41
+
42
+ def recurrence_matrix(
43
+ self, compute_white_vert_distr=False, **kwargs
44
+ ) -> NDArray[np.float64]:
45
+ """
46
+ Compute the recurrence matrix for the time series.
47
+
48
+ Parameters
49
+ ----------
50
+ metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
51
+ Distance metric used for phase space reconstruction.
52
+ std_metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
53
+ Distance metric used for standard deviation calculation.
54
+ threshold : float, default=0.1
55
+ Recurrence threshold (relative to data range).
56
+ threshold_std : bool, default=True
57
+ Whether to scale threshold by data standard deviation.
58
+
59
+ Returns
60
+ -------
61
+ NDArray[np.float64]
62
+ The recurrence matrix of the time series.
63
+ """
64
+ config = RTEConfig(**kwargs)
65
+ # Metric setup
66
+ metric_map = {"supremum": np.inf, "euclidean": 2, "manhattan": 1}
67
+ try:
68
+ ord = metric_map[config.std_metric.lower()]
69
+ except KeyError:
70
+ raise ValueError(
71
+ f"Invalid std_metric: {config.std_metric}. Must be {list(metric_map.keys())}"
72
+ )
73
+
74
+ # Threshold calculation
75
+ if config.threshold_std:
76
+ std = np.std(self.time_series, axis=0)
77
+ eps = config.threshold * np.linalg.norm(std, ord=ord)
78
+ if eps <= 0:
79
+ eps = 0.1
80
+ else:
81
+ eps = config.threshold
82
+
83
+ if compute_white_vert_distr:
84
+ recmat = recurrence_matrix(
85
+ self.time_series, float(eps), metric=config.metric
86
+ )
87
+ P = white_vertline_distr(recmat)[config.lmin :]
88
+
89
+ return recmat, P
90
+ else:
91
+ return recurrence_matrix(self.time_series, float(eps), metric=config.metric)
92
+
93
+ def recurrence_time_entropy(self, **kwargs):
94
+ """
95
+ Compute the recurrence time entropy of the time series.
96
+
97
+ Parameters
98
+ ----------
99
+ metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
100
+ Distance metric used for phase space reconstruction.
101
+ std_metric : {'supremum', 'euclidean', 'manhattan'}, default='supremum'
102
+ Distance metric used for standard deviation calculation.
103
+ lmin : int, default=1
104
+ Minimum line length to consider in recurrence quantification.
105
+ threshold : float, default=0.1
106
+ Recurrence threshold (relative to data range).
107
+ threshold_std : bool, default=True
108
+ Whether to scale threshold by data standard deviation.
109
+ return_recmat : bool, default=False
110
+ Whether to return the recurrence matrix.
111
+ return_p : bool, default=False
112
+ Whether to return white vertical line length distribution.
113
+
114
+ Returns
115
+ -------
116
+ Tuple[float, float]
117
+ The recurrence time entropy and its standard deviation.
118
+ """
119
+
120
+ config = RTEConfig(**kwargs)
121
+
122
+ # Compute the recurrence matrix
123
+ rec_matrix = self.recurrence_matrix(**kwargs)
124
+
125
+ # Calculate the white vertical line distribution
126
+ P = white_vertline_distr(rec_matrix)[config.lmin :]
127
+ P = P[P > 0] # Filter out zero probabilities
128
+ P /= P.sum() # Normalize the distribution
129
+
130
+ # Calculate the recurrence time entropy
131
+ rte = -np.sum(P * np.log(P))
132
+
133
+ result = [rte]
134
+ if config.return_recmat:
135
+ result.append(rec_matrix)
136
+ if config.return_p:
137
+ result.append(P)
138
+
139
+ return result[0] if len(result) == 1 else tuple(result)
@@ -0,0 +1,16 @@
1
+ # # __init__.py
2
+
3
+ # # Copyright (C) 2025 Matheus Rolim Sales
4
+ # #
5
+ # # This program is free software: you can redistribute it and/or modify
6
+ # # it under the terms of the GNU General Public License as published by
7
+ # # the Free Software Foundation, either version 3 of the License, or
8
+ # # (at your option) any later version.
9
+ # #
10
+ # # This program is distributed in the hope that it will be useful,
11
+ # # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # # GNU General Public License for more details.
14
+ # #
15
+ # # You should have received a copy of the GNU General Public License
16
+ # # along with this program. If not, see <https://www.gnu.org/licenses/>.