gsplot 0.0.1__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.
- gsplot/__init__.py +98 -0
- gsplot/base/base.py +518 -0
- gsplot/base/base_alias_validator.py +155 -0
- gsplot/color/colormap.py +218 -0
- gsplot/config/config.py +422 -0
- gsplot/data/load_file.py +188 -0
- gsplot/figure/axes.py +361 -0
- gsplot/figure/axes_base.py +952 -0
- gsplot/figure/figure_tools.py +66 -0
- gsplot/figure/show.py +205 -0
- gsplot/figure/store.py +119 -0
- gsplot/hello_world/hello_world.py +23 -0
- gsplot/logger.py +155 -0
- gsplot/path/path.py +227 -0
- gsplot/plot/line.py +328 -0
- gsplot/plot/line_base.py +272 -0
- gsplot/plot/line_colormap_base.py +120 -0
- gsplot/plot/line_colormap_dashed.py +540 -0
- gsplot/plot/line_colormap_solid.py +289 -0
- gsplot/plot/scatter.py +228 -0
- gsplot/plot/scatter_colormap.py +296 -0
- gsplot/style/graph.py +466 -0
- gsplot/style/label.py +866 -0
- gsplot/style/legend.py +469 -0
- gsplot/style/legend_colormap.py +381 -0
- gsplot/style/ticks.py +167 -0
- gsplot/version.py +2 -0
- gsplot-0.0.1.dist-info/LICENSE +21 -0
- gsplot-0.0.1.dist-info/METADATA +82 -0
- gsplot-0.0.1.dist-info/RECORD +31 -0
- gsplot-0.0.1.dist-info/WHEEL +4 -0
gsplot/plot/line_base.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Any, Callable, TypeVar, cast
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from numpy.typing import NDArray
|
|
8
|
+
|
|
9
|
+
from ..color.colormap import Colormap
|
|
10
|
+
|
|
11
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
12
|
+
|
|
13
|
+
__all__: list[str] = []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NumLines:
|
|
17
|
+
"""
|
|
18
|
+
A thread-safe singleton class to track the number of lines plotted on each axis.
|
|
19
|
+
|
|
20
|
+
This class maintains a count of the number of lines plotted on specific axes in a
|
|
21
|
+
Matplotlib figure. It uses a thread-safe singleton pattern to ensure a single instance
|
|
22
|
+
across the application. It also provides a decorator to automatically increment the
|
|
23
|
+
line count when a plotting function is called.
|
|
24
|
+
|
|
25
|
+
Attributes
|
|
26
|
+
--------------------
|
|
27
|
+
num_lines : list[int]
|
|
28
|
+
A list where each index represents an axis, and the value is the number of lines
|
|
29
|
+
plotted on that axis.
|
|
30
|
+
|
|
31
|
+
Methods
|
|
32
|
+
--------------------
|
|
33
|
+
num_lines_axis(axis_index)
|
|
34
|
+
Retrieves the number of lines plotted on a specific axis.
|
|
35
|
+
increment(axis_index)
|
|
36
|
+
Increments the line count for a specific axis.
|
|
37
|
+
count(func)
|
|
38
|
+
A decorator to increment the line count whenever a plotting function is called.
|
|
39
|
+
reset()
|
|
40
|
+
Resets the singleton instance, clearing all line counts.
|
|
41
|
+
|
|
42
|
+
Examples
|
|
43
|
+
--------------------
|
|
44
|
+
>>> num_lines = NumLines()
|
|
45
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
46
|
+
0 # No lines plotted yet
|
|
47
|
+
|
|
48
|
+
>>> num_lines.increment(0) # Increment the count for axis 0
|
|
49
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
50
|
+
1
|
|
51
|
+
|
|
52
|
+
>>> @NumLines.count
|
|
53
|
+
... def plot_line(axis_index):
|
|
54
|
+
... print(f"Plotting on axis {axis_index}")
|
|
55
|
+
>>> plot_line(0)
|
|
56
|
+
Plotting on axis 0
|
|
57
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
58
|
+
2
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
_instance: NumLines | None = None
|
|
62
|
+
_lock: threading.Lock = threading.Lock() # Lock to ensure thread safety
|
|
63
|
+
|
|
64
|
+
def __new__(cls) -> "NumLines":
|
|
65
|
+
with cls._lock: # Ensure thread safety
|
|
66
|
+
if cls._instance is None:
|
|
67
|
+
cls._instance = super(NumLines, cls).__new__(cls)
|
|
68
|
+
cls._instance._initialize_num_lines()
|
|
69
|
+
return cls._instance
|
|
70
|
+
|
|
71
|
+
def _initialize_num_lines(self) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Initializes the line count to its default value ([0]).
|
|
74
|
+
"""
|
|
75
|
+
# Explicitly initialize the instance variable with a type hint
|
|
76
|
+
self._num_lines: list[int] = [0]
|
|
77
|
+
|
|
78
|
+
def update_num_lines(self, axis_index: int) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Ensures the line count list is large enough to include the given axis index.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
--------------------
|
|
84
|
+
axis_index : int
|
|
85
|
+
The index of the axis to update.
|
|
86
|
+
"""
|
|
87
|
+
length = len(self._num_lines)
|
|
88
|
+
if axis_index + 1 > length:
|
|
89
|
+
self._num_lines.extend([0] * (axis_index - length + 1))
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def num_lines(self) -> list[int]:
|
|
93
|
+
"""
|
|
94
|
+
Retrieves the list of line counts for all axes.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
--------------------
|
|
98
|
+
list[int]
|
|
99
|
+
The list of line counts, where each index corresponds to an axis.
|
|
100
|
+
|
|
101
|
+
Examples
|
|
102
|
+
--------------------
|
|
103
|
+
>>> num_lines = NumLines()
|
|
104
|
+
>>> print(num_lines.num_lines)
|
|
105
|
+
[0]
|
|
106
|
+
"""
|
|
107
|
+
return self._num_lines
|
|
108
|
+
|
|
109
|
+
def num_lines_axis(self, axis_index: int) -> int:
|
|
110
|
+
"""
|
|
111
|
+
Retrieves the number of lines plotted on a specific axis.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
--------------------
|
|
115
|
+
axis_index : int
|
|
116
|
+
The index of the axis.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
--------------------
|
|
120
|
+
int
|
|
121
|
+
The number of lines plotted on the specified axis.
|
|
122
|
+
|
|
123
|
+
Examples
|
|
124
|
+
--------------------
|
|
125
|
+
>>> num_lines = NumLines()
|
|
126
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
127
|
+
0
|
|
128
|
+
"""
|
|
129
|
+
self.update_num_lines(axis_index)
|
|
130
|
+
return self._num_lines[axis_index]
|
|
131
|
+
|
|
132
|
+
def increment(self, axis_index: int) -> None:
|
|
133
|
+
"""
|
|
134
|
+
Increments the line count for a specific axis.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
--------------------
|
|
138
|
+
axis_index : int
|
|
139
|
+
The index of the axis.
|
|
140
|
+
|
|
141
|
+
Examples
|
|
142
|
+
--------------------
|
|
143
|
+
>>> num_lines = NumLines()
|
|
144
|
+
>>> num_lines.increment(0)
|
|
145
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
146
|
+
1
|
|
147
|
+
"""
|
|
148
|
+
self.update_num_lines(axis_index)
|
|
149
|
+
self._num_lines[axis_index] += 1
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def count(cls, func: F) -> F:
|
|
153
|
+
"""
|
|
154
|
+
A decorator to increment the line count whenever a plotting function is called.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
--------------------
|
|
158
|
+
func : Callable
|
|
159
|
+
The function to decorate.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
--------------------
|
|
163
|
+
Callable
|
|
164
|
+
The decorated function.
|
|
165
|
+
|
|
166
|
+
Examples
|
|
167
|
+
--------------------
|
|
168
|
+
>>> @NumLines.count
|
|
169
|
+
... def plot_line(axis_index):
|
|
170
|
+
... print(f"Plotting on axis {axis_index}")
|
|
171
|
+
>>> plot_line(0)
|
|
172
|
+
Plotting on axis 0
|
|
173
|
+
>>> num_lines = NumLines()
|
|
174
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
175
|
+
1
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
def wrapper(self, *args: Any, **kwargs: Any) -> Any:
|
|
179
|
+
cls().increment(self.axis_index)
|
|
180
|
+
result = func(self, *args, **kwargs)
|
|
181
|
+
return result
|
|
182
|
+
|
|
183
|
+
return cast(F, wrapper)
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def reset(cls) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Resets the singleton instance, clearing all line counts.
|
|
189
|
+
|
|
190
|
+
Examples
|
|
191
|
+
--------------------
|
|
192
|
+
>>> num_lines = NumLines()
|
|
193
|
+
>>> num_lines.increment(0)
|
|
194
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
195
|
+
1
|
|
196
|
+
>>> NumLines.reset()
|
|
197
|
+
>>> num_lines = NumLines()
|
|
198
|
+
>>> print(num_lines.num_lines_axis(0))
|
|
199
|
+
0
|
|
200
|
+
"""
|
|
201
|
+
cls._instance = None
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class AutoColor:
|
|
205
|
+
"""
|
|
206
|
+
A utility class for generating colors automatically based on the axis index and line count.
|
|
207
|
+
|
|
208
|
+
This class uses a predefined colormap and cycles through colors based on the number of lines
|
|
209
|
+
already plotted on the target axis. The default colormap is "viridis", and it is divided
|
|
210
|
+
into a specified number of discrete colors.
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
--------------------
|
|
214
|
+
axis_index : int
|
|
215
|
+
The index of the target axis in the current figure.
|
|
216
|
+
|
|
217
|
+
Attributes
|
|
218
|
+
--------------------
|
|
219
|
+
COLORMAP_LENGTH : int
|
|
220
|
+
The number of discrete colors in the colormap (default is 5).
|
|
221
|
+
CMAP : str
|
|
222
|
+
The name of the Matplotlib colormap to use (default is "viridis").
|
|
223
|
+
colormap : numpy.ndarray
|
|
224
|
+
An array of RGB colors derived from the specified colormap.
|
|
225
|
+
num_lines_axis : int
|
|
226
|
+
The number of lines already plotted on the target axis.
|
|
227
|
+
cycle_color_index : int
|
|
228
|
+
The index of the color to use for the next line, calculated using modulo arithmetic.
|
|
229
|
+
|
|
230
|
+
Methods
|
|
231
|
+
--------------------
|
|
232
|
+
get_color()
|
|
233
|
+
Retrieves the next color from the colormap based on the current line count.
|
|
234
|
+
|
|
235
|
+
Examples
|
|
236
|
+
--------------------
|
|
237
|
+
>>> auto_color = AutoColor(axis_index=0)
|
|
238
|
+
>>> color = auto_color.get_color()
|
|
239
|
+
>>> print(color)
|
|
240
|
+
array([0.267004, 0.004874, 0.329415, 1.0]) # Example RGBA color from the colormap
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def __init__(self, axis_index: int) -> None:
|
|
244
|
+
self.COLORMAP_LENGTH: int = 5
|
|
245
|
+
self.CMAP = "viridis"
|
|
246
|
+
self.colormap: NDArray[Any] = Colormap(
|
|
247
|
+
cmap=self.CMAP, N=self.COLORMAP_LENGTH
|
|
248
|
+
).get_split_cmap()
|
|
249
|
+
|
|
250
|
+
self.num_lines_axis: int = NumLines().num_lines_axis(axis_index)
|
|
251
|
+
self.cycle_color_index: int = self.num_lines_axis % self.COLORMAP_LENGTH
|
|
252
|
+
|
|
253
|
+
def get_color(self) -> NDArray[Any]:
|
|
254
|
+
"""
|
|
255
|
+
Retrieves the next color from the colormap based on the current line count.
|
|
256
|
+
|
|
257
|
+
This method determines the appropriate color for the next line to be plotted
|
|
258
|
+
on the target axis by cycling through the discrete colormap.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
--------------------
|
|
262
|
+
numpy.ndarray
|
|
263
|
+
An array representing the RGBA color for the next line.
|
|
264
|
+
|
|
265
|
+
Examples
|
|
266
|
+
--------------------
|
|
267
|
+
>>> auto_color = AutoColor(axis_index=0)
|
|
268
|
+
>>> color = auto_color.get_color()
|
|
269
|
+
>>> print(color)
|
|
270
|
+
array([0.267004, 0.004874, 0.329415, 1.0]) # Example RGBA color
|
|
271
|
+
"""
|
|
272
|
+
return np.array(self.colormap[self.cycle_color_index])
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from matplotlib.colors import Normalize
|
|
5
|
+
from numpy.typing import ArrayLike, NDArray
|
|
6
|
+
|
|
7
|
+
__all__: list[str] = []
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LineColormapBase:
|
|
11
|
+
"""
|
|
12
|
+
A base class for creating colormaps and segments for line collections.
|
|
13
|
+
|
|
14
|
+
This class provides utility methods to create line segments for plotting with
|
|
15
|
+
individual colors and to normalize data for applying colormaps.
|
|
16
|
+
|
|
17
|
+
Methods
|
|
18
|
+
--------------------
|
|
19
|
+
_create_segment(x, y)
|
|
20
|
+
Creates a set of line segments for line collections, enabling individual
|
|
21
|
+
segment coloring.
|
|
22
|
+
_create_cmap(cmapdata)
|
|
23
|
+
Creates a normalization object for mapping data points to colors.
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------------------
|
|
27
|
+
>>> line_base = LineColormapBase()
|
|
28
|
+
>>> x = np.array([0, 1, 2, 3])
|
|
29
|
+
>>> y = np.array([1, 2, 3, 4])
|
|
30
|
+
>>> segments = line_base._create_segment(x, y)
|
|
31
|
+
>>> print(segments.shape)
|
|
32
|
+
(3, 2, 2) # Shape: (numlines, points per line, x and y)
|
|
33
|
+
|
|
34
|
+
>>> cmapdata = np.array([0.1, 0.4, 0.6, 0.9])
|
|
35
|
+
>>> norm = line_base._create_cmap(cmapdata)
|
|
36
|
+
>>> print(norm(cmapdata))
|
|
37
|
+
[0. 0.5 0.83333333 1. ] # Normalized data
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def _create_segment(self, x: ArrayLike, y: ArrayLike) -> NDArray[np.float64]:
|
|
41
|
+
"""
|
|
42
|
+
Creates a set of line segments for line collections, enabling individual segment coloring.
|
|
43
|
+
|
|
44
|
+
The method converts the input x and y arrays into a collection of segments
|
|
45
|
+
suitable for use with Matplotlib's `LineCollection`. Each segment connects two
|
|
46
|
+
adjacent points from the input arrays.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
--------------------
|
|
50
|
+
x : ArrayLike
|
|
51
|
+
The x-coordinates of the points.
|
|
52
|
+
y : ArrayLike
|
|
53
|
+
The y-coordinates of the points.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
--------------------
|
|
57
|
+
numpy.ndarray
|
|
58
|
+
An array of shape `(numlines, 2, 2)` representing the line segments,
|
|
59
|
+
where each segment is defined by two points `(x, y)`.
|
|
60
|
+
|
|
61
|
+
Examples
|
|
62
|
+
--------------------
|
|
63
|
+
>>> x = np.array([0, 1, 2, 3])
|
|
64
|
+
>>> y = np.array([1, 2, 3, 4])
|
|
65
|
+
>>> line_base = LineColormapBase()
|
|
66
|
+
>>> segments = line_base._create_segment(x, y)
|
|
67
|
+
>>> print(segments.shape)
|
|
68
|
+
(3, 2, 2) # Shape: (numlines, points per line, x and y)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# ╭──────────────────────────────────────────────────────────╮
|
|
72
|
+
# │ Create a set of line segments so that we can color them │
|
|
73
|
+
# │ individually │
|
|
74
|
+
# │ This creates the points as an N x 1 x 2 array so that │
|
|
75
|
+
# │ we can stack points │
|
|
76
|
+
# │ together easily to get the segments. The segments array │
|
|
77
|
+
# │ for line collection │
|
|
78
|
+
# │ needs to be (numlines) x (points per line) x 2 (for x │
|
|
79
|
+
# │ and y) │
|
|
80
|
+
# ╰──────────────────────────────────────────────────────────╯
|
|
81
|
+
points = np.array([x, y], dtype=np.float64).T.reshape(-1, 1, 2)
|
|
82
|
+
segments: NDArray[np.float64] = np.concatenate(
|
|
83
|
+
[points[:-1], points[1:]], axis=1
|
|
84
|
+
)
|
|
85
|
+
return segments
|
|
86
|
+
|
|
87
|
+
def _create_cmap(self, cmapdata: NDArray[Any]) -> Normalize:
|
|
88
|
+
"""
|
|
89
|
+
Creates a normalization object for mapping data points to colors.
|
|
90
|
+
|
|
91
|
+
This method generates a `Normalize` object from Matplotlib, which scales
|
|
92
|
+
input data to the range `[0, 1]` for use in colormaps. If the input data has
|
|
93
|
+
at least two elements, the maximum value is removed to prevent color saturation.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
--------------------
|
|
97
|
+
cmapdata : numpy.ndarray
|
|
98
|
+
The input data for normalization.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
--------------------
|
|
102
|
+
matplotlib.colors.Normalize
|
|
103
|
+
A normalization object mapping `cmapdata.min()` to 0 and `cmapdata.max()` to 1.
|
|
104
|
+
|
|
105
|
+
Examples
|
|
106
|
+
--------------------
|
|
107
|
+
>>> cmapdata = np.array([0.1, 0.4, 0.6, 0.9])
|
|
108
|
+
>>> line_base = LineColormapBase()
|
|
109
|
+
>>> norm = line_base._create_cmap(cmapdata)
|
|
110
|
+
>>> print(norm(cmapdata))
|
|
111
|
+
[0. 0.5 0.83333333 1. ] # Normalized data
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
# Create a continuous norm to map from data points to colors
|
|
115
|
+
if len(cmapdata) >= 2:
|
|
116
|
+
# delete maximun data
|
|
117
|
+
cmapdata = np.delete(cmapdata, np.where(cmapdata == np.max(cmapdata)))
|
|
118
|
+
norm = Normalize(cmapdata.min(), cmapdata.max())
|
|
119
|
+
|
|
120
|
+
return norm
|