tikzplot42 0.5.8__tar.gz → 0.5.10__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 (58) hide show
  1. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/PKG-INFO +7 -1
  2. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/README.md +6 -0
  3. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/pyproject.toml +1 -1
  4. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/axes.py +178 -0
  5. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/axes.pyi +296 -1
  6. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/elements.py +28 -0
  7. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/figure.py +2 -2
  8. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/figure.pyi +2 -2
  9. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/plots.py +42 -0
  10. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/plots.pyi +294 -3
  11. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot42.egg-info/PKG-INFO +7 -1
  12. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot42.egg-info/SOURCES.txt +2 -0
  13. tikzplot42-0.5.10/tests/test22.py +54 -0
  14. tikzplot42-0.5.10/tests/test23.py +12 -0
  15. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/LICENSE +0 -0
  16. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/setup.cfg +0 -0
  17. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/__init__.py +0 -0
  18. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/__init__.pyi +0 -0
  19. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/axes3d.py +0 -0
  20. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/axes3d.pyi +0 -0
  21. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/border_finder.py +0 -0
  22. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/colorbar.py +0 -0
  23. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/colorbar.pyi +0 -0
  24. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/colors.py +0 -0
  25. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/config.py +0 -0
  26. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/config.pyi +0 -0
  27. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/elements.pyi +0 -0
  28. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/latex_special.py +0 -0
  29. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/pie.py +0 -0
  30. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/py.typed +0 -0
  31. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/state.py +0 -0
  32. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/styles.py +0 -0
  33. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/styles.pyi +0 -0
  34. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot/texts.py +0 -0
  35. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot42.egg-info/dependency_links.txt +0 -0
  36. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot42.egg-info/requires.txt +0 -0
  37. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/src/tikzplot42.egg-info/top_level.txt +0 -0
  38. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test1.py +0 -0
  39. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test10.py +0 -0
  40. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test11.py +0 -0
  41. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test12.py +0 -0
  42. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test13.py +0 -0
  43. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test14.py +0 -0
  44. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test15.py +0 -0
  45. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test16.py +0 -0
  46. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test17.py +0 -0
  47. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test18.py +0 -0
  48. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test19.py +0 -0
  49. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test2.py +0 -0
  50. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test20.py +0 -0
  51. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test21.py +0 -0
  52. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test3.py +0 -0
  53. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test4.py +0 -0
  54. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test5.py +0 -0
  55. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test6.py +0 -0
  56. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test7.py +0 -0
  57. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test8.py +0 -0
  58. {tikzplot42-0.5.8 → tikzplot42-0.5.10}/tests/test9.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tikzplot42
3
- Version: 0.5.8
3
+ Version: 0.5.10
4
4
  Summary: TikzPlot to make TikZ/PGFPlots plots with matplotlib.pyplot-like syntax.
5
5
  Author-email: Zan Ambrozic <zan.ambrozic7@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -745,6 +745,7 @@ Some basic plot commands are already implemented with commonly used arguments:
745
745
  #### Plotting
746
746
  - `plot()`,
747
747
  - `scatter()`,
748
+ - `quiver()`,
748
749
  - `loglog()`,
749
750
  - `semilogx()/semilogy()`,
750
751
  - `errorbar()`,
@@ -765,6 +766,11 @@ Some basic plot commands are already implemented with commonly used arguments:
765
766
  - `pie()`,
766
767
  - `pie_label()` (basic only for now),
767
768
  - `ecdf()` (empirical cumulative distribution function),
769
+ - `magnitue_spectrum()`,
770
+ - `phase_spectrum()`,
771
+ - `angle_spectrum()`,
772
+ - `specgram()`,
773
+ - `psd()` (power spectral density),
768
774
  - `magnify()` (used to magnify part of a plot, but Tikz cannot handle `fill_between` if this one is used, which is a long known issue),
769
775
  - `axvline()/axhline()`,
770
776
  - `axvspan()/axhspan()` (background span).
@@ -45,6 +45,7 @@ Some basic plot commands are already implemented with commonly used arguments:
45
45
  #### Plotting
46
46
  - `plot()`,
47
47
  - `scatter()`,
48
+ - `quiver()`,
48
49
  - `loglog()`,
49
50
  - `semilogx()/semilogy()`,
50
51
  - `errorbar()`,
@@ -65,6 +66,11 @@ Some basic plot commands are already implemented with commonly used arguments:
65
66
  - `pie()`,
66
67
  - `pie_label()` (basic only for now),
67
68
  - `ecdf()` (empirical cumulative distribution function),
69
+ - `magnitue_spectrum()`,
70
+ - `phase_spectrum()`,
71
+ - `angle_spectrum()`,
72
+ - `specgram()`,
73
+ - `psd()` (power spectral density),
68
74
  - `magnify()` (used to magnify part of a plot, but Tikz cannot handle `fill_between` if this one is used, which is a long known issue),
69
75
  - `axvline()/axhline()`,
70
76
  - `axvspan()/axhspan()` (background span).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tikzplot42"
7
- version = "0.5.8"
7
+ version = "0.5.10"
8
8
  description = "TikzPlot to make TikZ/PGFPlots plots with matplotlib.pyplot-like syntax."
9
9
  authors = [
10
10
  { name = "Zan Ambrozic", email = "zan.ambrozic7@gmail.com" }
@@ -225,6 +225,67 @@ class BaseAxes:
225
225
  except: pass
226
226
 
227
227
  return self._plot(x, y, **kwargs, ls="", settings={"scatter": None})
228
+
229
+ def quiver(self, *args, **kwargs):
230
+ kws = {"pivot", "scale", "color", "c", "alpha", "cmap", "width", "linewidth", "lw"}
231
+ kwargs = self._check_kwargs("quiver", kws, **kwargs)
232
+ X = Y = C = None
233
+ if len(args) == 2:
234
+ U, V = _np.atleast_1d(*args)
235
+ elif len(args) == 3:
236
+ U, V, C = _np.atleast_1d(*args)
237
+ elif len(args) == 4:
238
+ X, Y, U, V = _np.atleast_1d(*args)
239
+ elif len(args) == 5:
240
+ X, Y, U, V, C = _np.atleast_1d(*args)
241
+ else:
242
+ raise Warning("Invalid number of arguments for quiver. Expected 2 to 5 arguments.")
243
+ nr, nc = (1, U.shape[0]) if U.ndim == 1 else U.shape
244
+ if X is not None and Y is not None:
245
+ X, Y = X.ravel(), Y.ravel()
246
+ if len(X) == nc and len(Y) == nr:
247
+ X, Y = (a.ravel() for a in _np.meshgrid(X, Y))
248
+ elif len(X) != len(Y):
249
+ raise Warning("X and Y must have the same length.")
250
+ else:
251
+ ind_grid = _np.meshgrid(_np.arange(nc), _np.arange(nr))
252
+ X, Y = (a.ravel() for a in ind_grid)
253
+ X, Y, U, V = (a.flatten() for a in (X, Y, U, V))
254
+ sc = kwargs.pop("scale", None)
255
+ if sc is not None:
256
+ U *= sc
257
+ V *= sc
258
+ pivot = kwargs.get("pivot", None)
259
+ if pivot is not None:
260
+ if pivot == "tail":
261
+ pass
262
+ elif pivot == "middle":
263
+ X -= U / 2
264
+ Y -= V / 2
265
+ elif pivot == "tip":
266
+ X -= U
267
+ Y -= V
268
+ else:
269
+ raise Warning(f"Invalid pivot: {pivot}. Must be one of 'tail', 'middle', or 'tip'.")
270
+ if C is not None:
271
+ cmap = kwargs.get("cmap", "viridis")
272
+ kwargs["cmap"] = Colorbar(cmap=cmap, lower=_np.min(C), upper=_np.max(C))
273
+ if self._cmap_bar and self._cmap_bar != kwargs["cmap"]:
274
+ raise Warning("Multiple colormaps on same axis! Only one per axis is allowed.")
275
+ self._cmap_bar = kwargs["cmap"]
276
+ kwargs["C"] = C.flatten()
277
+ else:
278
+ kwargs["color"] = kwargs.get("color", kwargs.get("c", "k"))
279
+ kwargs["u"] = U
280
+ kwargs["v"] = V
281
+ if "width" in kwargs:
282
+ kwargs["line width"] = kwargs.pop("width")
283
+ settings = {}
284
+ settings["quiver"] = dict(u=r"\thisrow{u}", v=r"\thisrow{v}")
285
+ if "cmap" in kwargs:
286
+ settings["quiver"].update({"every arrow/.append style": "mapped color"})
287
+ settings["point meta"] = r"\thisrow{c}"
288
+ return self._plot(X, Y, **kwargs, settings=settings)
228
289
 
229
290
  def semilogy(self, x, y, *args, **kwargs):
230
291
  kws = {"fmt", "base", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
@@ -934,6 +995,123 @@ class BaseAxes:
934
995
  cum_weights = 1 - cum_weights
935
996
  return self.plot(x, cum_weights, **kwargs)
936
997
 
998
+ def _fft_core(self, x_win, n, sides, Fs) -> tuple[_np.ndarray, _np.ndarray]:
999
+ if sides == "onesided":
1000
+ return _np.fft.rfft(x_win, n=n), _np.fft.rfftfreq(n, d=1.0 / Fs)
1001
+ X, freqs = _np.fft.fft(x_win, n=n), _np.fft.fftfreq(n, d=1.0 / Fs)
1002
+ return (_np.fft.fftshift(X), _np.fft.fftshift(freqs)) if sides == "twosided" else (X, freqs)
1003
+
1004
+ def _get_window(self, window, NFFT) -> _np.ndarray:
1005
+ if window is None:
1006
+ return _np.hanning(NFFT)
1007
+ win = _np.asarray(window(_np.ones(NFFT)) if callable(window) else window)
1008
+ if len(win) != NFFT:
1009
+ raise ValueError(f"Window length ({len(win)}) must equal NFFT ({NFFT})")
1010
+ return win
1011
+
1012
+ def _apply_detrend(self, s, detrend) -> _np.ndarray:
1013
+ if detrend in (None, "none"):
1014
+ return s
1015
+ if detrend == "mean":
1016
+ return s - _np.mean(s)
1017
+ if detrend == "linear":
1018
+ t = _np.arange(len(s), dtype=_np.float64)
1019
+ return s - _np.polyval(_np.polyfit(t, s, 1), t)
1020
+ if callable(detrend):
1021
+ d = detrend(s)
1022
+ assert isinstance(d, _np.ndarray) and d.shape == s.shape, "Detrending function must return an array of the same shape as input."
1023
+ return d
1024
+ raise ValueError(f"Unknown detrend option: {detrend}")
1025
+
1026
+ def _spect_helper(self, x, Fs=2.0, Fc=0.0, window=None, pad_to=None, sides="default"):
1027
+ x = _np.asarray(x)
1028
+ Fs, Fc, n_fft = float(Fs or 2.0), float(Fc or 0.0), pad_to or len(x)
1029
+ sides = ("onesided" if _np.isrealobj(x) else "twosided") if sides == "default" else sides
1030
+ win = self._get_window(window, len(x))
1031
+ X, freqs = self._fft_core(x * win, n_fft, sides, Fs)
1032
+ return X, freqs + Fc, win, sides, n_fft
1033
+
1034
+ def _plot_spectrum(self, name, x, mode, ylabel, Fs=2.0, Fc=0.0, window=None, pad_to=None, sides="default", scale=None, **kwargs):
1035
+ kws = {"alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
1036
+ kwargs = self._check_kwargs(name, kws, **kwargs)
1037
+ X, freqs, win, sides, n_fft = self._spect_helper(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides)
1038
+ if mode == "mag":
1039
+ y = _np.abs(X) / _np.abs(win).sum()
1040
+ if sides == "onesided":
1041
+ y[1 : None if n_fft % 2 else -1] *= 2.0
1042
+ if scale == "dB":
1043
+ y, ylabel = 20 * _np.log10(y), "Magnitude (dB)"
1044
+ elif mode == "phase":
1045
+ y = _np.unwrap(_np.angle(X))
1046
+ else: # angle
1047
+ y = _np.angle(X)
1048
+ self.set_ylabel(ylabel)
1049
+ if isinstance(self, Axes):
1050
+ self.set_xlabel("Frequency")
1051
+ return y, freqs, self.plot(freqs, y, **kwargs)
1052
+
1053
+ def magnitude_spectrum(self, x, *, Fs=2.0, Fc=0.0, window=None, pad_to=None, sides="default", scale=None, **kwargs):
1054
+ return self._plot_spectrum("magnitude_spectrum", x, "mag", "Magnitude (energy)", Fs, Fc, window, pad_to, sides, scale, **kwargs)
1055
+
1056
+ def phase_spectrum(self, x, *, Fs=2.0, Fc=0.0, window=None, pad_to=None, sides="default", **kwargs):
1057
+ return self._plot_spectrum("phase_spectrum", x, "phase", "Phase (radians)", Fs, Fc, window, pad_to, sides, **kwargs)
1058
+
1059
+ def angle_spectrum(self, x, *, Fs=2.0, Fc=0.0, window=None, pad_to=None, sides="default", **kwargs):
1060
+ return self._plot_spectrum("angle_spectrum", x, "angle", "Angle (radians)", Fs, Fc, window, pad_to, sides, **kwargs)
1061
+
1062
+ def _ft_helper(self, x, NFFT=256, Fs=2.0, Fc=0.0, detrend=None, window=None, noverlap=0, pad_to=None, sides="default", scale_by_freq=True, mode="psd", scale="default") -> tuple[_np.ndarray, _np.ndarray, float]:
1063
+ x = _np.asarray(x)
1064
+ sides = ("onesided" if _np.isrealobj(x) else "twosided") if sides == "default" else sides
1065
+ step, n_fft = NFFT - noverlap, pad_to or NFFT
1066
+ if step <= 0 or n_fft < NFFT:
1067
+ raise ValueError("Invalid noverlap or pad_to value")
1068
+ win = self._get_window(window, NFFT)
1069
+ num_segments = (len(x) - noverlap) // step
1070
+ if num_segments == 0:
1071
+ raise ValueError("Not enough data to compute spectrum")
1072
+ mode = "psd" if mode in (None, "default") else mode.lower()
1073
+ cols = []
1074
+ for i in range(num_segments):
1075
+ seg = self._apply_detrend(x[i * step : i * step + NFFT], detrend)
1076
+ X, _ = self._fft_core(seg * win, n_fft, sides, Fs)
1077
+ if mode in ("psd", "spectrum", "magnitude"):
1078
+ is_p = mode in ("psd", "spectrum")
1079
+ sf = ((Fs if mode == "psd" and scale_by_freq else 1.0) * (_np.abs(win) ** 2).sum()) if is_p else _np.abs(win).sum()
1080
+ val = ((_np.abs(X) ** 2) if is_p else _np.abs(X)) / sf
1081
+ if sides == "onesided":
1082
+ val[1 : None if n_fft % 2 else -1] *= 2.0
1083
+ elif mode in ("angle", "phase"):
1084
+ val = _np.angle(X)
1085
+ else:
1086
+ raise ValueError(f"Unknown mode: {mode}")
1087
+ cols.append(val)
1088
+ Pxx_matrix = _np.column_stack(cols)
1089
+ if mode == "phase":
1090
+ Pxx_matrix = _np.unwrap(Pxx_matrix, axis=0)
1091
+ scale = ("dB" if mode in ("psd", "spectrum") else "linear") if scale in (None, "default") else scale.lower()
1092
+ if scale == "dB":
1093
+ mult = 20.0 if mode == "magnitude" else 10.0
1094
+ Pxx_matrix = mult * _np.log10(_np.maximum(Pxx_matrix, 1e-20))
1095
+ _, freqs = self._fft_core(win, n_fft, sides, Fs)
1096
+ return Pxx_matrix, freqs + Fc, step
1097
+
1098
+ def specgram(self, x, *, NFFT=256, Fs=2.0, Fc=0.0, detrend=None, window=None, noverlap=0, pad_to=None, sides="default", scale_by_freq=True, mode="psd", scale="default", cmap="viridis", **kwargs):
1099
+ Pxx, freqs, step = self._ft_helper(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, noverlap=noverlap, pad_to=pad_to, sides=sides, scale_by_freq=scale_by_freq, mode=mode, scale=scale)
1100
+ t = (_np.arange(Pxx.shape[1]) * step + (NFFT / 2.0)) / Fs
1101
+ if not isinstance(self, Axes):
1102
+ raise Warning("specgram is only supported on primary axes.")
1103
+ extent = [t[0], t[-1], freqs[0], freqs[-1]] if len(t) > 1 else None
1104
+ return Pxx, freqs, t, self.imshow(Pxx, cmap=cmap, origin="lower", aspect="auto", extent=extent, **kwargs)
1105
+
1106
+ def psd(self, x, *, NFFT=256, Fs=2.0, Fc=0.0, detrend=None, window=None, noverlap=0, pad_to=None, sides="default", scale_by_freq=True, **kwargs):
1107
+ kws = {"alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
1108
+ kwargs = self._check_kwargs("psd", kws, **kwargs)
1109
+ Pxx, freqs, _ = self._ft_helper(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, noverlap=noverlap, pad_to=pad_to, sides=sides, scale_by_freq=scale_by_freq, mode="psd", scale="linear")
1110
+ self.set_ylabel("Power Spectral Density (dB/Hz)")
1111
+ if isinstance(self, Axes):
1112
+ self.set_xlabel("Frequency (Hz)")
1113
+ return Pxx, freqs, self.plot(freqs, 10 * _np.log10(_np.mean(Pxx, axis=1)), **kwargs)
1114
+
937
1115
  def pie(self, x, *, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, labeldistance=1.1, radius=1, startangle=0, counterclock=True, wedgeprops=None, rotate_labels=False, normalize=True):
938
1116
  kws = {"width"}
939
1117
  kwargs = self._check_kwargs("pie: wedgeprops", kws, **wedgeprops) if wedgeprops else {}
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional, Sequence, Tuple, Union, Literal
1
+ from typing import Any, Optional, Sequence, Tuple, Union, Literal, Callable
2
2
  import numpy as np
3
3
  from .colorbar import Colorbar
4
4
  from .elements import Graph
@@ -85,6 +85,37 @@ class BaseAxes:
85
85
  Colorbar limits for scatter points, if color is given sequence of floats and cmap is given as string, otherwise ignored. If cmap is given as str and no vmin or vmax is provided, they will be set to the min and max of color sequence.
86
86
  """
87
87
  ...
88
+
89
+ def quiver(self, *args, pivot: Literal["tail", "middle", "tip"]="tail", scale:float=1.0, alpha: float = 1.0, color: Optional[Union[Sequence[ColorLike], ColorLike]] = ..., c: Optional[ColorLike] = ...,
90
+ linewidth: Optional[float]= ..., lw: Optional[float] = ..., cmap: Optional[Union[str, Colorbar]] = ..., width: Optional[float] = ...) -> Graph:
91
+ """
92
+ Draw a quiver plot to the selected axis.
93
+
94
+ Parameters
95
+ ----------
96
+ args : ArrayLike
97
+ Datapoints for quiver plot, (X, Y, U, V, C) or (U, V) for grid (thus 2, 4 or 5 args).
98
+
99
+ pivot: {"tail", "middle", "tip"}, optional
100
+ The part of the arrow that is at the grid point. Default is "tail".
101
+
102
+ scale: float, optional
103
+ Scale factor for arrows. Default is 1.0.
104
+
105
+ alpha: float, optional
106
+ Opacity
107
+
108
+ color or c: ColorLike, optional:
109
+ all matplotlib color formats (without X11/xkcd)
110
+
111
+ width or linewidth or lw: float, optional
112
+ Line width in pt
113
+
114
+ cmap: str or list (R, G, B), optional
115
+ Colormap for quiver arrows, if color is given as float or sequence of floats. Can be a colormap name or a Colorbar object.
116
+ """
117
+ ...
118
+
88
119
  def semilogy(self, x: ArrayLike = ..., y: ArrayLike = ..., base: Optional[float] = 10, fmt: Optional[str] = ...,*, alpha: float = 1.0, color: Optional[ColorLike] = ..., c: Optional[ColorLike] = ...,
89
120
  linestyle: Optional[LineStyle] = ..., ls: Optional[LineStyle] = ..., linewidth: Optional[float]= ..., lw: Optional[float] = ...,
90
121
  marker: Optional[MarkerStyle] = ..., markersize: Optional[float] = ..., ms: Optional[float] = ..., label:Optional[str]=...) -> Graph:
@@ -734,6 +765,270 @@ class BaseAxes:
734
765
  """
735
766
  ...
736
767
 
768
+ def magnitude_spectrum(self, x: ArrayLike, *, Fs: float = 2.0, Fc: float = 0.0, window: Optional[Union[str, np.ndarray]] = None, pad_to: Optional[int] = None, sides: Literal["default", "onesided", "twosided"] = "default", scale: Literal["default", "linear", "dB"] = "default", fmt: Optional[str] = ..., alpha: float = 1.0, color: Optional[ColorLike] = ..., c: Optional[ColorLike] = ..., linestyle: Optional[LineStyle] = ..., ls: Optional[LineStyle] = ..., linewidth: Optional[float]= ..., lw: Optional[float] = ..., marker: Optional[MarkerStyle] = ..., markersize: Optional[float] = ..., ms: Optional[float] = ..., label:Optional[str]=...) -> tuple[Sequence[float], Sequence[float], Graph]:
769
+ """
770
+ Draw the magnitude spectrum of a signal to the selected axis.
771
+
772
+ Parameters
773
+ ----------
774
+ x: ArrayLike
775
+ Input signal for which the magnitude spectrum is computed.
776
+
777
+ Fs: float, optional
778
+ Sampling frequency of the input signal. Default is 2.0.
779
+
780
+ Fc: float, optional
781
+ Center frequency for the spectrum. Default is 0.0.
782
+
783
+ window: str or ndarray, optional
784
+ Window function to apply to the signal before computing the spectrum. If None, no windowing is applied.
785
+
786
+ pad_to: int, optional
787
+ Length to which the signal is zero-padded before computing the FFT. If None, no padding is applied.
788
+
789
+ sides: {"default", "onesided", "twosided"}, optional
790
+ Specifies whether to return a one-sided or two-sided spectrum. Default is "default".
791
+
792
+ scale: {"default", "linear", "dB"}, optional
793
+ Scale of the magnitude spectrum. Default is "default".
794
+
795
+ fmt: str, optional
796
+ Style format string for the plot.
797
+
798
+ alpha: float, optional
799
+ Opacity of the plot.
800
+
801
+ color or c: all matplotlib color formats (without X11/xkcd), optional
802
+ Color of the line and markers.
803
+
804
+ linestyle or ls: str, optional
805
+ Line style for the magnitude spectrum plot.
806
+
807
+ linewidth or lw: float, optional
808
+ Line width in points for the magnitude spectrum plot.
809
+
810
+ marker: str, optional
811
+ Marker style for the data points in the magnitude spectrum plot.
812
+
813
+ markersize or ms: float, optional
814
+ Size of the markers in points.
815
+
816
+ label: str, optional
817
+ Legend entry for the magnitude spectrum plot.
818
+ """
819
+ ...
820
+
821
+ def phase_spectrum(self, x: ArrayLike, *, Fs: float = 2.0, Fc: float = 0.0, window: Optional[Union[str, np.ndarray]] = None, pad_to: Optional[int] = None, sides: Literal["default", "onesided", "twosided"] = "default", fmt: Optional[str] = ..., alpha: float = 1.0, color: Optional[ColorLike] = ..., c: Optional[ColorLike] = ..., linestyle: Optional[LineStyle] = ..., ls: Optional[LineStyle] = ..., linewidth: Optional[float]= ..., lw: Optional[float] = ..., marker: Optional[MarkerStyle] = ..., markersize: Optional[float] = ..., ms: Optional[float] = ..., label:Optional[str]=...) -> tuple[Sequence[float], Sequence[float], Graph]:
822
+ """
823
+ Draw the phase spectrum of a signal to the selected axis.
824
+
825
+ Parameters
826
+ ----------
827
+ x: ArrayLike
828
+ Input signal for which the magnitude spectrum is computed.
829
+
830
+ Fs: float, optional
831
+ Sampling frequency of the input signal. Default is 2.0.
832
+
833
+ Fc: float, optional
834
+ Center frequency for the spectrum. Default is 0.0.
835
+
836
+ window: str or ndarray, optional
837
+ Window function to apply to the signal before computing the spectrum. If None, no windowing is applied.
838
+
839
+ pad_to: int, optional
840
+ Length to which the signal is zero-padded before computing the FFT. If None, no padding is applied.
841
+
842
+ sides: {"default", "onesided", "twosided"}, optional
843
+ Specifies whether to return a one-sided or two-sided spectrum. Default is "default".
844
+
845
+ fmt: str, optional
846
+ Style format string for the plot.
847
+
848
+ alpha: float, optional
849
+ Opacity of the plot.
850
+
851
+ color or c: all matplotlib color formats (without X11/xkcd), optional
852
+ Color of the line and markers.
853
+
854
+ linestyle or ls: str, optional
855
+ Line style for the magnitude spectrum plot.
856
+
857
+ linewidth or lw: float, optional
858
+ Line width in points for the magnitude spectrum plot.
859
+
860
+ marker: str, optional
861
+ Marker style for the data points in the magnitude spectrum plot.
862
+
863
+ markersize or ms: float, optional
864
+ Size of the markers in points.
865
+
866
+ label: str, optional
867
+ Legend entry for the magnitude spectrum plot.
868
+ """
869
+ ...
870
+
871
+ def angle_spectrum(self, x: ArrayLike, *, Fs: float = 2.0, Fc: float = 0.0, window: Optional[Union[str, np.ndarray]] = None, pad_to: Optional[int] = None, sides: Literal["default", "onesided", "twosided"] = "default", fmt: Optional[str] = ..., alpha: float = 1.0, color: Optional[ColorLike] = ..., c: Optional[ColorLike] = ..., linestyle: Optional[LineStyle] = ..., ls: Optional[LineStyle] = ..., linewidth: Optional[float]= ..., lw: Optional[float] = ..., marker: Optional[MarkerStyle] = ..., markersize: Optional[float] = ..., ms: Optional[float] = ..., label:Optional[str]=...) -> tuple[Sequence[float], Sequence[float], Graph]:
872
+ """
873
+ Draw the angle spectrum of a signal to the selected axis.
874
+
875
+ Parameters
876
+ ----------
877
+ x: ArrayLike
878
+ Input signal for which the magnitude spectrum is computed.
879
+
880
+ Fs: float, optional
881
+ Sampling frequency of the input signal. Default is 2.0.
882
+
883
+ Fc: float, optional
884
+ Center frequency for the spectrum. Default is 0.0.
885
+
886
+ window: str or ndarray, optional
887
+ Window function to apply to the signal before computing the spectrum. If None, no windowing is applied.
888
+
889
+ pad_to: int, optional
890
+ Length to which the signal is zero-padded before computing the FFT. If None, no padding is applied.
891
+
892
+ sides: {"default", "onesided", "twosided"}, optional
893
+ Specifies whether to return a one-sided or two-sided spectrum. Default is "default".
894
+
895
+ fmt: str, optional
896
+ Style format string for the plot.
897
+
898
+ alpha: float, optional
899
+ Opacity of the plot.
900
+
901
+ color or c: all matplotlib color formats (without X11/xkcd), optional
902
+ Color of the line and markers.
903
+
904
+ linestyle or ls: str, optional
905
+ Line style for the magnitude spectrum plot.
906
+
907
+ linewidth or lw: float, optional
908
+ Line width in points for the magnitude spectrum plot.
909
+
910
+ marker: str, optional
911
+ Marker style for the data points in the magnitude spectrum plot.
912
+
913
+ markersize or ms: float, optional
914
+ Size of the markers in points.
915
+
916
+ label: str, optional
917
+ Legend entry for the magnitude spectrum plot.
918
+ """
919
+ ...
920
+
921
+ def specgram(self, x: ArrayLike, *, NFFT: int = 256, Fs: float = 2.0, Fc: float = 0.0, detrend: Optional[Union[str, Callable]] = None, window: Optional[Union[str, np.ndarray]] = None, noverlap: int = 0, pad_to: Optional[int] = None, sides: Literal["default", "onesided", "twosided"] = "default", scale_by_freq: bool = True, mode: Literal["psd", "spectrum", "magnitude", "phase", "angle"] = "psd", scale: Literal["default", "linear", "dB"] = "default", cmap: str = "viridis", **kwargs) -> tuple[ArrayLike, ArrayLike, ArrayLike, Graph]:
922
+ """
923
+ Draw a spectrogram to the selected axis.
924
+
925
+ Parameters
926
+ ----------
927
+ x: ArrayLike
928
+ Input signal for which the spectrogram is computed.
929
+
930
+ NFFT: int, optional
931
+ The number of data points used in each block for the FFT. Default is 256.
932
+
933
+ Fs: float, optional
934
+ Sampling frequency of the input signal. Default is 2.0.
935
+
936
+ Fc: float, optional
937
+ Center frequency for the spectrum. Default is 0.0.
938
+
939
+ detrend: str or callable, optional
940
+ Specifies how to detrend each segment. If None, no detrending is applied.
941
+
942
+ window: str or ndarray, optional
943
+ Window function to apply to each segment before computing the FFT. If None, no windowing is applied.
944
+
945
+ noverlap: int, optional
946
+ The number of points of overlap between segments. Default is 0.
947
+
948
+ pad_to: int, optional
949
+ Length to which each segment is zero-padded before computing the FFT. If None, no padding is applied.
950
+
951
+ sides: {"default", "onesided", "twosided"}, optional
952
+ Specifies whether to return a one-sided or two-sided spectrum. Default is "default".
953
+
954
+ scale_by_freq: bool, optional
955
+ If True (default), scale the power spectral density by the frequency bin width.
956
+
957
+ mode: {"psd", "spectrum", "magnitude", "phase", "angle"}, optional
958
+ Specifies what type of spectrum to compute. Default is "psd".
959
+
960
+ scale: {"default", "linear", "dB"}, optional
961
+ Scale of the spectrogram. Default is "default".
962
+
963
+ cmap: str, optional
964
+ Colormap for the spectrogram. Default is "viridis".
965
+
966
+ kwargs: additional keyword arguments for imshow.
967
+ """
968
+ ...
969
+
970
+ def psd(self, x: ArrayLike, *, NFFT: int = 256, Fs: float = 2.0, Fc: float = 0.0, detrend: Optional[Union[str, Callable]] = None, window: Optional[Union[str, np.ndarray]] = None, noverlap: int = 0, pad_to: Optional[int] = None, sides: Literal["default", "onesided", "twosided"] = "default", scale_by_freq: bool = True, fmt: Optional[str] = ..., alpha: float = 1.0, color: Optional[ColorLike] = ..., c: Optional[ColorLike] = ..., linestyle: Optional[LineStyle] = ..., ls: Optional[LineStyle] = ..., linewidth: Optional[float]= ..., lw: Optional[float] = ..., marker: Optional[MarkerStyle] = ..., markersize: Optional[float] = ..., ms: Optional[float] = ..., label:Optional[str]=...) -> tuple[np.ndarray, np.ndarray, Graph]:
971
+ """
972
+ Draw a power spectral density (PSD) plot to the selected axis.
973
+
974
+ Parameters
975
+ ----------
976
+ x: ArrayLike
977
+ Input signal for which the PSD is computed.
978
+
979
+ NFFT: int, optional
980
+ The number of data points used in each block for the FFT. Default is 256.
981
+
982
+ Fs: float, optional
983
+ Sampling frequency of the input signal. Default is 2.0.
984
+
985
+ Fc: float, optional
986
+ Center frequency for the spectrum. Default is 0.0.
987
+
988
+ detrend: str or callable, optional
989
+ Specifies how to detrend each segment. If None, no detrending is applied.
990
+
991
+ window: str or ndarray, optional
992
+ Window function to apply to each segment before computing the FFT. If None, no windowing is applied.
993
+
994
+ noverlap: int, optional
995
+ The number of points of overlap between segments. Default is 0.
996
+
997
+ pad_to: int, optional
998
+ Length to which each segment is zero-padded before computing the FFT. If None, no padding is applied.
999
+
1000
+ sides: {"default", "onesided", "twosided"}, optional
1001
+ Specifies whether to return a one-sided or two-sided spectrum. Default is "default".
1002
+
1003
+ scale_by_freq: bool, optional
1004
+ If True (default), scale the power spectral density by the frequency bin width.
1005
+
1006
+ fmt: str, optional
1007
+ Style format string for the plot.
1008
+
1009
+ alpha: float, optional
1010
+ Opacity of the plot.
1011
+
1012
+ color or c: all matplotlib color formats (without X11/xkcd), optional
1013
+ Color of the line and markers.
1014
+
1015
+ linestyle or ls: str, optional
1016
+ Line style for the PSD plot.
1017
+
1018
+ linewidth or lw: float, optional
1019
+ Line width in points for the PSD plot.
1020
+
1021
+ marker: str, optional
1022
+ Marker style for the data points in the PSD plot.
1023
+
1024
+ markersize or ms: float, optional
1025
+ Size of the markers in points.
1026
+
1027
+ label: str, optional
1028
+ Legend entry for the PSD plot.
1029
+ """
1030
+ ...
1031
+
737
1032
  def pie(self, x: ArrayLike, *, explode: Optional[ArrayLike] = None, labels: Optional[Sequence[str]] = None, colors: Optional[Sequence[ColorLike]] = None, autopct: Optional[str] = None, pctdistance: float = 0.6, labeldistance: float = 1.1, radius: float = 1.0, startangle: float = 0.0, counterclock: bool = True, wedgeprops: Optional[dict] = None, rotate_labels: bool = False, normalize: bool = True) -> Pie:
738
1033
  """
739
1034
  Draw a pie chart to the selected axis.
@@ -302,6 +302,8 @@ class BaseGraph:
302
302
  self._p_dict[i] = self._st_dict[st]
303
303
  opts["point meta"] = "explicit symbolic"
304
304
  opts["scatter/classes"] = f"{{\n" + ',\n'.join(f"{v}={{{k}}}" for k,v in self._st_dict.items()) + "\n}"
305
+ if "quiver" in self._settings:
306
+ opts["-stealth"] = None
305
307
  self._style_str = ""
306
308
  for o in opts:
307
309
  if opts[o] is None:
@@ -475,6 +477,13 @@ class Graph(BaseGraph):
475
477
  self._st_dict = {}
476
478
  self._p_dict = {}
477
479
  self._colors = None
480
+ if "quiver" in self._settings:
481
+ self._u = self._style.pop("u", None)
482
+ self._v = self._style.pop("v", None)
483
+ self._c = None
484
+ if "cmap" in self._style and "C" in self._style:
485
+ self._has_color = True
486
+ self._c = self._style.pop("C", None)
478
487
  if "axvline" in settings or "axhline" in settings or "axvspan" in settings or "axhspan" in settings:
479
488
  self._x, self._y=coordinates
480
489
  elif isinstance(coordinates, tuple):
@@ -544,6 +553,10 @@ class Graph(BaseGraph):
544
553
  cols.append("label")
545
554
  if self._sizes is not None:
546
555
  cols.append("size")
556
+ elif "quiver" in self._settings:
557
+ cols += ["u", "v"]
558
+ if self._c is not None:
559
+ cols.append("c")
547
560
  elif self._meta is not None:
548
561
  cols.append("meta")
549
562
  return " ".join(cols)
@@ -570,6 +583,11 @@ class Graph(BaseGraph):
570
583
  line.append(self._p_dict[i])
571
584
  if self._sizes is not None:
572
585
  line.append(f"{self._sizes[i]:.9f} pt")
586
+ elif "quiver" in self._settings:
587
+ line.append(self._u[i])
588
+ line.append(self._v[i])
589
+ if self._c is not None:
590
+ line.append(self._c[i])
573
591
  elif self._meta is not None:
574
592
  line.append(str(self._meta[i]))
575
593
  rows.append(" ".join(str(v) for v in line))
@@ -840,6 +858,11 @@ class Graph(BaseGraph):
840
858
  self._colors = self._colors[mask]
841
859
  if self._sizes is not None:
842
860
  self._sizes = self._sizes[mask]
861
+ elif "quiver" in self._settings:
862
+ self._u = self._u[mask]
863
+ self._v = self._v[mask]
864
+ if self._c is not None:
865
+ self._c = self._c[mask]
843
866
 
844
867
  def _check_equal(self, x,y):
845
868
  if self._classic:
@@ -918,6 +941,11 @@ class Graph(BaseGraph):
918
941
  self._colors = self._colors[mask]
919
942
  if self._sizes is not None:
920
943
  self._sizes = self._sizes[mask]
944
+ if "quiver" in self._settings:
945
+ self._u = self._u[mask]
946
+ self._v = self._v[mask]
947
+ if self._c is not None:
948
+ self._c = self._c[mask]
921
949
 
922
950
  class Graph3(BaseGraph):
923
951
  def __init__(self, axes, coordinates, settings={}, xerr=None, yerr=None, zerr=None, path_name=None, **style):
@@ -268,7 +268,7 @@ class Figure:
268
268
  del self._axes[key]
269
269
  del ax
270
270
 
271
- def colorbar(self, mappable, cax=None, location=None, orientation=None, shrink=1, pad=None, ticks=None, label=None):
271
+ def colorbar(self, mappable, ax=None, location=None, orientation=None, shrink=1, pad=None, ticks=None, label=None):
272
272
  horizontal = False
273
273
  if orientation is not None:
274
274
  if orientation == "horizontal":
@@ -282,7 +282,7 @@ class Figure:
282
282
  location = "bottom"
283
283
  else:
284
284
  location = "right"
285
- cbar = Colorbar(mappable, axis=cax, location=location, horizontal=horizontal, rel_len=shrink, pad=pad, ticks=ticks, label=label)
285
+ cbar = Colorbar(mappable, axis=ax, location=location, horizontal=horizontal, rel_len=shrink, pad=pad, ticks=ticks, label=label)
286
286
  return cbar
287
287
 
288
288
  def _compute_group_spacing(self, cpy=None):
@@ -118,7 +118,7 @@ class Figure:
118
118
  """
119
119
  ...
120
120
 
121
- def colorbar(self, mappable: Any, *, cax: Optional[Axes | Axes3] = None, location: Literal["right", "left", "top", "bottom"] | None = None, orientation: Literal["horizontal", "vertical"] | None = None, shrink: float = 1.0, pad: float |None= None, ticks: Sequence[float] | None = None, label: str | None = None) -> Colorbar:
121
+ def colorbar(self, mappable: Any, *, ax: Optional[Axes | Axes3] = None, location: Literal["right", "left", "top", "bottom"] | None = None, orientation: Literal["horizontal", "vertical"] | None = None, shrink: float = 1.0, pad: float |None= None, ticks: Sequence[float] | None = None, label: str | None = None) -> Colorbar:
122
122
  """
123
123
  Add a colorbar to the figure.
124
124
 
@@ -127,7 +127,7 @@ class Figure:
127
127
  mappable : Any
128
128
  The object to which the colorbar applies (e.g. imshow).
129
129
 
130
- cax : Axes or Axes3, optional
130
+ ax : Axes or Axes3, optional
131
131
  The axes in which to draw the colorbar. If None, colorbar will not be shown.
132
132
 
133
133
  location : str, optional