dataplot 0.1.6__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.
dataplot/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """
2
+ # dataplot
3
+ Provides plotting tools useful in datascience.
4
+
5
+ ## See Also
6
+ ### Github repository
7
+ * https://github.com/Chitaoji/dataplot/
8
+
9
+ ### PyPI project
10
+ * https://pypi.org/project/dataplot/
11
+
12
+ ## License
13
+ This project falls under the BSD 3-Clause License.
14
+
15
+ """
16
+
17
+ import lazyr
18
+
19
+ with lazyr.setverbose(0):
20
+ lazyr.register("pandas")
21
+ lazyr.register("scipy.stats")
22
+ lazyr.register("seaborn")
23
+
24
+ from . import container, core, dataset, setting
25
+ from .container import *
26
+ from .core import *
27
+ from .dataset import *
28
+ from .setting import *
29
+
30
+ __all__: list[str] = []
31
+ __all__.extend(core.__all__)
32
+ __all__.extend(setting.__all__)
33
+ __all__.extend(container.__all__)
34
+ __all__.extend(dataset.__all__)
dataplot/_typing.py ADDED
@@ -0,0 +1,211 @@
1
+ """
2
+ Contains typing classes.
3
+
4
+ NOTE: this module is not intended to be imported at runtime.
5
+
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Literal, NotRequired, Optional, TypedDict, TypeVar
9
+
10
+ if TYPE_CHECKING:
11
+ from .setting import PlotSettable
12
+
13
+
14
+ PlotSettableVar = TypeVar("PlotSettableVar", bound="PlotSettable")
15
+ DefaultVar = TypeVar("DefaultVar")
16
+ StyleName = Literal[
17
+ "Solarize_Light2",
18
+ "_classic_test_patch",
19
+ "_mpl-gallery",
20
+ "_mpl-gallery-nogrid",
21
+ "bmh",
22
+ "classic",
23
+ "dark_background",
24
+ "fast",
25
+ "fivethirtyeight",
26
+ "ggplot",
27
+ "grayscale",
28
+ "seaborn-v0_8",
29
+ "seaborn-v0_8-bright",
30
+ "seaborn-v0_8-colorblind",
31
+ "seaborn-v0_8-dark",
32
+ "seaborn-v0_8-dark-palette",
33
+ "seaborn-v0_8-darkgrid",
34
+ "seaborn-v0_8-deep",
35
+ "seaborn-v0_8-muted",
36
+ "seaborn-v0_8-notebook",
37
+ "seaborn-v0_8-paper",
38
+ "seaborn-v0_8-pastel",
39
+ "seaborn-v0_8-poster",
40
+ "seaborn-v0_8-talk",
41
+ "seaborn-v0_8-ticks",
42
+ "seaborn-v0_8-white",
43
+ "seaborn-v0_8-whitegrid",
44
+ "tableau-colorblind10",
45
+ ]
46
+ LegendLoc = Literal[
47
+ "best",
48
+ "upper right",
49
+ "upper left",
50
+ "lower left",
51
+ "lower right",
52
+ "right",
53
+ "center left",
54
+ "center right",
55
+ "lower center",
56
+ "upper center",
57
+ "center",
58
+ ]
59
+ FontSize = Literal[
60
+ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
61
+ ]
62
+ FontWeight = Literal[
63
+ "ultralight",
64
+ "light",
65
+ "normal",
66
+ "regular",
67
+ "book",
68
+ "medium",
69
+ "roman",
70
+ "semibold",
71
+ "demibold",
72
+ "demi",
73
+ "bold",
74
+ "heavy",
75
+ "extra bold",
76
+ "black",
77
+ ]
78
+ FontStyleName = Literal["normal", "italic", "oblique"]
79
+ ColorId = (
80
+ Literal["b", "g", "r", "c", "m", "y", "k", "w"]
81
+ | Literal["C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9"]
82
+ )
83
+ VerticalAlignment = Literal["baseline", "bottom", "center", "center_baseline", "top"]
84
+ HorizontalAlignment = Literal["left", "center", "right"]
85
+ DistName = Literal["normal", "expon"]
86
+ SettingKey = Literal[
87
+ "title",
88
+ "xlabel",
89
+ "ylabel",
90
+ "alpha",
91
+ "dpi",
92
+ "grid",
93
+ "grid_alpha",
94
+ "style",
95
+ "figsize",
96
+ "fontdict",
97
+ "legend_loc",
98
+ "subplots_adjust",
99
+ ]
100
+ ResampleRule = Literal["head", "tail", "random"]
101
+
102
+
103
+ class SettingDict(TypedDict):
104
+ """
105
+ Dict of keyword-arguments for `._set()`.
106
+
107
+ """
108
+
109
+ title: NotRequired[Optional[str]]
110
+ xlabel: NotRequired[Optional[str]]
111
+ ylabel: NotRequired[Optional[str]]
112
+ alpha: NotRequired[Optional[float]]
113
+ dpi: NotRequired[Optional[float]]
114
+ grid: NotRequired[Optional[bool]]
115
+ grid_alpha: NotRequired[Optional[float]]
116
+ style: NotRequired[Optional[StyleName]]
117
+ figsize: NotRequired[Optional[tuple[int, int]]]
118
+ fontdict: NotRequired[Optional["FontDict"]]
119
+ legend_loc: NotRequired[Optional[str]]
120
+ format_label: NotRequired[Optional[bool]]
121
+ subplots_adjust: NotRequired[Optional["SubplotDict"]]
122
+
123
+
124
+ class FigureSettingDict(TypedDict):
125
+ """
126
+ Dict of keyword-arguments for `.set_figure()`.
127
+
128
+ """
129
+
130
+ title: NotRequired[Optional[str]]
131
+ dpi: NotRequired[Optional[float]]
132
+ style: NotRequired[Optional[StyleName]]
133
+ figsize: NotRequired[Optional[tuple[int, int]]]
134
+ fontdict: NotRequired[Optional["FontDict"]]
135
+ subplots_adjust: NotRequired[Optional["SubplotDict"]]
136
+
137
+
138
+ class AxesSettingDict(TypedDict):
139
+ """
140
+ Dict of keyword-arguments for `.set_axes()`.
141
+
142
+ """
143
+
144
+ title: NotRequired[Optional[str]]
145
+ xlabel: NotRequired[Optional[str]]
146
+ ylabel: NotRequired[Optional[str]]
147
+ alpha: NotRequired[Optional[float]]
148
+ grid: NotRequired[Optional[bool]]
149
+ grid_alpha: NotRequired[Optional[float]]
150
+ fontdict: NotRequired[Optional["FontDict"]]
151
+ legend_loc: NotRequired[Optional[LegendLoc]]
152
+
153
+
154
+ class FontDict(TypedDict):
155
+ """
156
+ A dictionary controlling the appearance of the title text.
157
+
158
+ Unset parameters are left unmodified; initial values are given by
159
+ `matplotlib.rcParams`.
160
+
161
+ Parameters
162
+ ----------
163
+ fontsize : float | FontSizeStr, optional
164
+ The font size.
165
+ fontweight : float | FontWeightStr, optional
166
+ The font weight. If a float, should be in range 0-1000.
167
+ fontstyle : FontStyleStr, optional
168
+ The font style.
169
+ color : ColorStr, optional
170
+ The font color.
171
+ verticalalignment : VerticalAlignmentStr, optional
172
+ The vertical alignment relative to the anchor point.
173
+ horizontalalignment : HorizontalAlignmentStr, optional
174
+ The horizontal alignment relative to the anchor point.
175
+
176
+ """
177
+
178
+ fontsize: NotRequired[float | FontSize]
179
+ fontweight: NotRequired[float | FontWeight]
180
+ fontstyle: NotRequired[FontStyleName]
181
+ color: NotRequired[ColorId]
182
+ verticalalignment: NotRequired[VerticalAlignment]
183
+ horizontalalignment: NotRequired[HorizontalAlignment]
184
+
185
+
186
+ class SubplotDict(TypedDict):
187
+ """
188
+ A dictionary controlling the subplot layout parameters.
189
+
190
+ Unset parameters are left unmodified; initial values are given by
191
+ `matplotlib.rcParams`, whose recommended values are {"left": 0.125,
192
+ "bottom": 0.11, "right": 0.9, "top": 0.88, "wspace": 0.2, "hspace":
193
+ 0.2}.
194
+
195
+ Parameters
196
+ ----------
197
+ left / right / bottom / top : float, optional
198
+ The position of the left / right / bottom / top edge of the
199
+ subplots, as a fraction of the figure width.
200
+ wspace / hspace : float, optional
201
+ The width / height of the padding between subplots, as a fraction
202
+ of the average Axes width / height.
203
+
204
+ """
205
+
206
+ left: NotRequired[float]
207
+ bottom: NotRequired[float]
208
+ right: NotRequired[float]
209
+ top: NotRequired[float]
210
+ wspace: NotRequired[float]
211
+ hspace: NotRequired[float]
@@ -0,0 +1,24 @@
1
+ """
2
+ Contains artists.
3
+
4
+ """
5
+
6
+ from . import base, corrmap, histogram, ksplot, linechart, ppplot, qqplot, scatterchart
7
+ from .base import *
8
+ from .corrmap import *
9
+ from .histogram import *
10
+ from .ksplot import *
11
+ from .linechart import *
12
+ from .ppplot import *
13
+ from .qqplot import *
14
+ from .scatterchart import *
15
+
16
+ __all__: list[str] = []
17
+ __all__.extend(base.__all__)
18
+ __all__.extend(corrmap.__all__)
19
+ __all__.extend(histogram.__all__)
20
+ __all__.extend(ksplot.__all__)
21
+ __all__.extend(linechart.__all__)
22
+ __all__.extend(ppplot.__all__)
23
+ __all__.extend(qqplot.__all__)
24
+ __all__.extend(scatterchart.__all__)
@@ -0,0 +1,78 @@
1
+ """
2
+ Contains the core of artist: Artist, Plotter.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from collections import Counter
10
+ from typing import TYPE_CHECKING, Any, Optional
11
+
12
+ import numpy as np
13
+ from validating import attr, dataclass
14
+
15
+ from ..container import FigWrapper
16
+ from ..setting import PlotSettable
17
+ from ..utils.multi import MultiObject, multiple
18
+
19
+ if TYPE_CHECKING:
20
+ from ..container import AxesWrapper
21
+
22
+ __all__ = ["Artist", "Plotter"]
23
+
24
+
25
+ @dataclass(validate_methods=True)
26
+ class Artist(PlotSettable):
27
+ """
28
+ Paint the desired images on an axes.
29
+
30
+ Parameters
31
+ ----------
32
+ plotter : Plotter
33
+ Painting tool.
34
+
35
+ """
36
+
37
+ plotter: "Plotter | MultiObject"
38
+ ax: Optional["AxesWrapper"] = attr(default=None, repr=False)
39
+
40
+ def __repr__(self) -> str:
41
+ self.paint(self.ax)
42
+ names = (x.__class__.__name__ for x in multiple(self.plotter))
43
+ s = ", ".join(f"{i} {x}s" for x, i in Counter(names).items())
44
+ return f"<{self.__class__.__name__} with {s}>"
45
+
46
+ def paint(self, ax: Optional["AxesWrapper"] = None) -> None:
47
+ """
48
+ Paint on the axes.
49
+
50
+ Parameters
51
+ ----------
52
+ ax : AxesWrapper, optional
53
+ Specifies the axes-wrapper on which the plot should be painted,
54
+ by default None.
55
+
56
+ """
57
+ with self.customize(FigWrapper, 1, 1, active := ax is None) as fig:
58
+ self.plotter.paint(fig.axes[0] if active else ax)
59
+
60
+
61
+ @dataclass(init=False, validate_methods=True)
62
+ class Plotter(PlotSettable):
63
+ """
64
+ Painting tool used by Artist - user can ignore this.
65
+
66
+ """
67
+
68
+ data: Optional[np.ndarray] = attr(repr=False, default=None, init=False)
69
+ label: Optional[str] = attr(default=None, init=False)
70
+
71
+ def paint(
72
+ self,
73
+ ax: "AxesWrapper",
74
+ *,
75
+ __multi_prev_returned__: Any = None,
76
+ __multi_is_final__: bool = True,
77
+ ) -> Any:
78
+ """Paint."""
@@ -0,0 +1,57 @@
1
+ """
2
+ Contains a plotter class: CorrMap.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from typing import TYPE_CHECKING, Optional
10
+
11
+ import numpy as np
12
+ import pandas as pd
13
+ import seaborn as sns
14
+ from validating import dataclass
15
+
16
+ from .base import Plotter
17
+
18
+ if TYPE_CHECKING:
19
+ from ..container import AxesWrapper
20
+
21
+
22
+ __all__ = ["CorrMap"]
23
+
24
+
25
+ @dataclass(validate_methods=True)
26
+ class CorrMap(Plotter):
27
+ """
28
+ A plotter class that creates a correlation heatmap.
29
+
30
+ """
31
+
32
+ annot: bool
33
+
34
+ def paint(
35
+ self,
36
+ ax: "AxesWrapper",
37
+ *,
38
+ __multi_prev_returned__: Optional[tuple[list[np.ndarray], list[str]]] = None,
39
+ __multi_is_final__: bool = True,
40
+ ) -> tuple[list[np.ndarray], list[str]]:
41
+ if __multi_prev_returned__ is None:
42
+ arrays, labels = [], []
43
+ else:
44
+ arrays, labels = __multi_prev_returned__
45
+ arrays.append(self.data)
46
+ labels.append(self.label)
47
+ if __multi_is_final__:
48
+ ax.set_default(title="Correlation Heatmap")
49
+ ax.load(self.settings)
50
+ self.__plot(ax, arrays, labels)
51
+ return arrays, labels
52
+
53
+ def __plot(
54
+ self, ax: "AxesWrapper", arrays: list[np.ndarray], labels: list[str]
55
+ ) -> None:
56
+ corr = pd.DataFrame(arrays, index=labels).T.corr()
57
+ sns.heatmap(corr, ax=ax.ax, annot=self.annot)
@@ -0,0 +1,89 @@
1
+ """
2
+ Contains a plotter class: Histogram.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from typing import TYPE_CHECKING, Optional
10
+
11
+ import numpy as np
12
+ from scipy import stats
13
+ from validating import dataclass
14
+
15
+ from .base import Plotter
16
+
17
+ if TYPE_CHECKING:
18
+ from ..container import AxesWrapper
19
+
20
+ __all__ = ["Histogram"]
21
+
22
+
23
+ @dataclass(validate_methods=True)
24
+ class Histogram(Plotter):
25
+ """
26
+ A plotter class that creates a histogram.
27
+
28
+ """
29
+
30
+ bins: int | list[float]
31
+ fit: bool
32
+ density: bool
33
+ log: bool
34
+ same_bin: bool
35
+ stats: bool
36
+
37
+ def paint(
38
+ self,
39
+ ax: "AxesWrapper",
40
+ *,
41
+ __multi_prev_returned__: Optional[tuple[str, np.ndarray]] = None,
42
+ __multi_is_final__: bool = True,
43
+ ) -> list[float]:
44
+ ax.set_default(
45
+ title="Histogram",
46
+ alpha=0.8,
47
+ xlabel="value",
48
+ ylabel="density" if self.density else "count",
49
+ )
50
+ ax.load(self.settings)
51
+ if __multi_prev_returned__ is None:
52
+ xlabel, bins = ax.settings.xlabel, None
53
+ else:
54
+ xlabel, bins = __multi_prev_returned__
55
+ ds, new_bins = self.__hist(
56
+ ax, bins=self.bins if (bins is None or not self.same_bin) else bins
57
+ )
58
+ if self.stats:
59
+ xlabel += "\n" + ds
60
+ if __multi_is_final__:
61
+ ax.set_axes(xlabel=xlabel)
62
+ return (xlabel, new_bins)
63
+
64
+ def __hist(
65
+ self, ax: "AxesWrapper", bins: int | list[float] = 100
66
+ ) -> tuple[str, np.ndarray]:
67
+ _, bin_list, _ = ax.ax.hist(
68
+ self.data,
69
+ bins=bins,
70
+ density=self.density,
71
+ log=self.log,
72
+ alpha=ax.settings.alpha,
73
+ label=self.label,
74
+ )
75
+ mean, std = np.nanmean(self.data), np.nanstd(self.data)
76
+ skew: float = stats.skew(self.data, bias=False, nan_policy="omit")
77
+ kurt: float = stats.kurtosis(self.data, bias=False, nan_policy="omit")
78
+ if self.fit and self.density:
79
+ ax.ax.plot(
80
+ bin_list,
81
+ stats.norm.pdf(bin_list, mean, std),
82
+ alpha=ax.settings.alpha,
83
+ label=f"{self.label} · fit",
84
+ )
85
+ return (
86
+ f"{self.label}: mean={mean:.3f}, std={std:.3f}, skew={skew:.3f}, "
87
+ f"kurt={kurt:.3f}",
88
+ bin_list,
89
+ )
@@ -0,0 +1,42 @@
1
+ """
2
+ Contains a plotter class: KSPlot.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from validating import dataclass
10
+ from typing import TYPE_CHECKING
11
+
12
+ from ..utils.math import get_quantile
13
+ from .qqplot import QQPlot
14
+
15
+ if TYPE_CHECKING:
16
+ from ..container import AxesWrapper
17
+
18
+
19
+ __all__ = ["KSPlot"]
20
+
21
+
22
+ @dataclass(validate_methods=True)
23
+ class KSPlot(QQPlot):
24
+ """
25
+ A plotter class that creates a K-S plot.
26
+
27
+ """
28
+
29
+ def paint(self, ax: "AxesWrapper", **_) -> None:
30
+ ax.set_default(
31
+ title="Kolmogorov-Smirnov Plot",
32
+ xlabel="value",
33
+ ylabel="cummulative probability",
34
+ )
35
+ ax.load(self.settings)
36
+ self.__plot(ax)
37
+
38
+ def __plot(self, ax: "AxesWrapper") -> None:
39
+ xlabel, p, q1 = self._generate_dist()
40
+ q2 = get_quantile(self.data, p)
41
+ ax.ax.plot(q1, p, self.fmt, label=xlabel)
42
+ ax.ax.plot(q2, p, self.fmt, label=self.label)
@@ -0,0 +1,62 @@
1
+ """
2
+ Contains a plotter class: LineChart.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from validating import dataclass
10
+ from typing import TYPE_CHECKING, Optional
11
+
12
+ import numpy as np
13
+
14
+ from ..setting import PlotSettable
15
+ from .base import Plotter
16
+
17
+ if TYPE_CHECKING:
18
+ from ..container import AxesWrapper
19
+ from ..dataset import PlotDataSet
20
+
21
+ __all__ = ["LineChart"]
22
+
23
+
24
+ @dataclass(validate_methods=True)
25
+ class LineChart(Plotter):
26
+ """
27
+ A plotter class that creates a line chart.
28
+
29
+ """
30
+
31
+ xticks: Optional["np.ndarray | PlotDataSet"]
32
+ fmt: str
33
+ scatter: bool
34
+ sorted: bool
35
+
36
+ def paint(self, ax: "AxesWrapper", **_) -> None:
37
+ ax.set_default(title="Line Chart")
38
+ ax.load(self.settings)
39
+ self.__plot(ax)
40
+
41
+ def __plot(self, ax: "AxesWrapper") -> None:
42
+ if isinstance(self.xticks, PlotSettable):
43
+ xticks = self.xticks.data
44
+ else:
45
+ xticks = self.xticks
46
+ if xticks is None:
47
+ xticks = range(len(self.data))
48
+ elif (len_t := len(xticks)) != (len_d := len(self.data)):
49
+ raise ValueError(
50
+ "x-ticks and data must have the same length, but have "
51
+ f"lengths {len_t} and {len_d}"
52
+ )
53
+
54
+ if self.sorted:
55
+ paired = sorted(zip(xticks, self.data, strict=True), key=lambda pair: pair[0])
56
+ xticks, data = zip(*paired, strict=True)
57
+ else:
58
+ data = self.data
59
+
60
+ ax.ax.plot(xticks, data, self.fmt, label=self.label)
61
+ if self.scatter:
62
+ ax.ax.scatter(xticks, data, zorder=2.0)
@@ -0,0 +1,41 @@
1
+ """
2
+ Contains a plotter class: PPPlot.
3
+
4
+ NOTE: this module is private. All functions and objects are available in the main
5
+ `dataplot` namespace - use that instead.
6
+
7
+ """
8
+
9
+ from validating import dataclass
10
+ from typing import TYPE_CHECKING
11
+
12
+ from ..utils.math import get_prob
13
+ from .qqplot import QQPlot
14
+
15
+ if TYPE_CHECKING:
16
+ from ..container import AxesWrapper
17
+
18
+ __all__ = ["PPPlot"]
19
+
20
+
21
+ @dataclass(validate_methods=True)
22
+ class PPPlot(QQPlot):
23
+ """
24
+ A plotter class that creates a P-P plot.
25
+
26
+ """
27
+
28
+ def paint(self, ax: "AxesWrapper", **_) -> None:
29
+ ax.set_default(
30
+ title="Probability-Probability Plot",
31
+ xlabel="cumulative probility",
32
+ ylabel="cumulative probility",
33
+ )
34
+ ax.load(self.settings)
35
+ self.__plot(ax)
36
+
37
+ def __plot(self, ax: "AxesWrapper") -> None:
38
+ xlabel, p1, q = self._generate_dist()
39
+ p2 = get_prob(self.data, q)
40
+ ax.ax.plot(p1, p2, self.fmt, zorder=2.1, label=f"{self.label} & {xlabel}")
41
+ self._plot_fitted_line(ax, p1, p2)