MatplotLibAPI 3.3.0__py3-none-any.whl → 4.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.
- MatplotLibAPI/__init__.py +4 -86
- MatplotLibAPI/accessor.py +288 -191
- MatplotLibAPI/area.py +235 -0
- MatplotLibAPI/bar.py +193 -0
- MatplotLibAPI/base_plot.py +88 -0
- MatplotLibAPI/box_violin.py +186 -0
- MatplotLibAPI/bubble.py +569 -0
- MatplotLibAPI/{Composite.py → composite.py} +70 -83
- MatplotLibAPI/heatmap.py +246 -0
- MatplotLibAPI/histogram.py +172 -0
- MatplotLibAPI/mcp/__init__.py +17 -0
- MatplotLibAPI/mcp/metadata.py +90 -0
- MatplotLibAPI/mcp/renderers.py +45 -0
- MatplotLibAPI/mcp_server.py +626 -0
- MatplotLibAPI/network/__init__.py +28 -0
- MatplotLibAPI/network/constants.py +22 -0
- MatplotLibAPI/{Network.py → network/core.py} +347 -809
- MatplotLibAPI/network/plot.py +597 -0
- MatplotLibAPI/network/scaling.py +56 -0
- MatplotLibAPI/pie.py +155 -0
- MatplotLibAPI/pivot.py +282 -0
- MatplotLibAPI/sankey.py +99 -0
- MatplotLibAPI/{StyleTemplate.py → style_template.py} +8 -4
- MatplotLibAPI/sunburst.py +139 -0
- MatplotLibAPI/{Table.py → table.py} +109 -93
- MatplotLibAPI/{Timeserie.py → timeserie.py} +99 -42
- MatplotLibAPI/{Treemap.py → treemap.py} +43 -55
- MatplotLibAPI/typing.py +12 -0
- MatplotLibAPI/{_visualization_utils.py → utils.py} +30 -13
- MatplotLibAPI/waffle.py +174 -0
- MatplotLibAPI/{Wordcloud.py → word_cloud.py} +188 -88
- {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/METADATA +98 -9
- matplotlibapi-4.0.1.dist-info/RECORD +36 -0
- {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/WHEEL +1 -1
- matplotlibapi-4.0.1.dist-info/entry_points.txt +2 -0
- MatplotLibAPI/Area.py +0 -80
- MatplotLibAPI/Bar.py +0 -83
- MatplotLibAPI/BoxViolin.py +0 -75
- MatplotLibAPI/Bubble.py +0 -460
- MatplotLibAPI/Heatmap.py +0 -121
- MatplotLibAPI/Histogram.py +0 -73
- MatplotLibAPI/Pie.py +0 -70
- MatplotLibAPI/Pivot.py +0 -134
- MatplotLibAPI/Sankey.py +0 -46
- MatplotLibAPI/Sunburst.py +0 -89
- MatplotLibAPI/Waffle.py +0 -86
- MatplotLibAPI/_typing.py +0 -17
- matplotlibapi-3.3.0.dist-info/RECORD +0 -26
- {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/licenses/LICENSE +0 -0
MatplotLibAPI/area.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Area chart helpers for Matplotlib-based area visualizations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from matplotlib.axes import Axes
|
|
7
|
+
from matplotlib.figure import Figure
|
|
8
|
+
|
|
9
|
+
from .base_plot import BasePlot
|
|
10
|
+
|
|
11
|
+
from .style_template import (
|
|
12
|
+
AREA_STYLE_TEMPLATE,
|
|
13
|
+
StyleTemplate,
|
|
14
|
+
string_formatter,
|
|
15
|
+
validate_dataframe,
|
|
16
|
+
)
|
|
17
|
+
from .utils import _get_axis, _merge_kwargs
|
|
18
|
+
|
|
19
|
+
__all__ = ["AREA_STYLE_TEMPLATE", "aplot_area", "fplot_area"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AreaChart(BasePlot):
|
|
23
|
+
"""Plot area charts from tabular data.
|
|
24
|
+
|
|
25
|
+
Methods
|
|
26
|
+
-------
|
|
27
|
+
aplot
|
|
28
|
+
Plot an area chart on an existing Matplotlib axes.
|
|
29
|
+
fplot
|
|
30
|
+
Plot an area chart on a new Matplotlib figure.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
pd_df: pd.DataFrame,
|
|
36
|
+
x: str,
|
|
37
|
+
y: str,
|
|
38
|
+
label: Optional[str] = None,
|
|
39
|
+
stacked: bool = True,
|
|
40
|
+
):
|
|
41
|
+
"""Initialize an area chart plotter.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
pd_df : pd.DataFrame
|
|
46
|
+
DataFrame containing the data to visualize.
|
|
47
|
+
x : str
|
|
48
|
+
Column name used for the x-axis.
|
|
49
|
+
y : str
|
|
50
|
+
Column name used for the y-axis values.
|
|
51
|
+
label : str, optional
|
|
52
|
+
Column used to split the area into groups. The default is None.
|
|
53
|
+
stacked : bool, optional
|
|
54
|
+
Whether grouped areas are stacked. The default is True.
|
|
55
|
+
"""
|
|
56
|
+
super().__init__(pd_df=pd_df)
|
|
57
|
+
self.x = x
|
|
58
|
+
self.y = y
|
|
59
|
+
self.label = label
|
|
60
|
+
self.stacked = stacked
|
|
61
|
+
|
|
62
|
+
cols = [self.x, self.y]
|
|
63
|
+
if self.label:
|
|
64
|
+
cols.append(self.label)
|
|
65
|
+
validate_dataframe(self._obj, cols=cols)
|
|
66
|
+
|
|
67
|
+
def _plot_grouped_area(
|
|
68
|
+
self,
|
|
69
|
+
plot_ax: Axes,
|
|
70
|
+
**kwargs: Any,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Plot grouped area data using a pivoted dataframe."""
|
|
73
|
+
pivot_df = self._obj.pivot_table(
|
|
74
|
+
index=self.x,
|
|
75
|
+
columns=self.label,
|
|
76
|
+
values=self.y,
|
|
77
|
+
aggfunc="sum",
|
|
78
|
+
).sort_index()
|
|
79
|
+
|
|
80
|
+
plot_kwargs: dict[str, Any] = {
|
|
81
|
+
"kind": "area",
|
|
82
|
+
"stacked": self.stacked,
|
|
83
|
+
"alpha": 0.7,
|
|
84
|
+
"ax": plot_ax,
|
|
85
|
+
}
|
|
86
|
+
pivot_df.plot(**_merge_kwargs(plot_kwargs, kwargs))
|
|
87
|
+
|
|
88
|
+
legend = plot_ax.get_legend()
|
|
89
|
+
if legend is not None:
|
|
90
|
+
legend.set_title(string_formatter(self.label or ""))
|
|
91
|
+
|
|
92
|
+
def _plot_single_area(
|
|
93
|
+
self,
|
|
94
|
+
plot_ax: Axes,
|
|
95
|
+
style: StyleTemplate,
|
|
96
|
+
**kwargs: Any,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Plot a single-series area chart."""
|
|
99
|
+
sorted_df = self._obj.sort_values(by=self.x)
|
|
100
|
+
fill_between_kwargs: dict[str, Any] = {
|
|
101
|
+
"color": style.font_color,
|
|
102
|
+
"alpha": 0.4,
|
|
103
|
+
}
|
|
104
|
+
merged_fill_between_kwargs = _merge_kwargs(fill_between_kwargs, kwargs)
|
|
105
|
+
|
|
106
|
+
plot_ax.fill_between(
|
|
107
|
+
sorted_df[self.x],
|
|
108
|
+
sorted_df[self.y],
|
|
109
|
+
**merged_fill_between_kwargs,
|
|
110
|
+
)
|
|
111
|
+
plot_ax.plot(sorted_df[self.x], sorted_df[self.y], color=style.font_color)
|
|
112
|
+
|
|
113
|
+
def aplot(
|
|
114
|
+
self,
|
|
115
|
+
title: Optional[str] = None,
|
|
116
|
+
style: StyleTemplate = AREA_STYLE_TEMPLATE,
|
|
117
|
+
ax: Optional[Axes] = None,
|
|
118
|
+
**kwargs: Any,
|
|
119
|
+
) -> Axes:
|
|
120
|
+
"""Plot an area chart on the provided axis.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
title : str, optional
|
|
125
|
+
Title for the plot. The default is None.
|
|
126
|
+
style : StyleTemplate, optional
|
|
127
|
+
Style template for the plot. The default is AREA_STYLE_TEMPLATE.
|
|
128
|
+
ax : Axes, optional
|
|
129
|
+
Matplotlib axes to plot on. If None, use the current axes.
|
|
130
|
+
**kwargs : Any
|
|
131
|
+
Additional keyword arguments forwarded to the area plotting call.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
Axes
|
|
136
|
+
The Matplotlib axes containing the area chart.
|
|
137
|
+
"""
|
|
138
|
+
plot_ax = _get_axis(ax)
|
|
139
|
+
plot_ax.set_facecolor(style.background_color)
|
|
140
|
+
|
|
141
|
+
if self.label:
|
|
142
|
+
self._plot_grouped_area(plot_ax=plot_ax, **kwargs)
|
|
143
|
+
else:
|
|
144
|
+
self._plot_single_area(plot_ax=plot_ax, style=style, **kwargs)
|
|
145
|
+
|
|
146
|
+
plot_ax.set_xlabel(string_formatter(self.x))
|
|
147
|
+
plot_ax.set_ylabel(string_formatter(self.y))
|
|
148
|
+
plot_ax.tick_params(axis="x", labelrotation=45)
|
|
149
|
+
if title:
|
|
150
|
+
plot_ax.set_title(title)
|
|
151
|
+
return plot_ax
|
|
152
|
+
|
|
153
|
+
def fplot(
|
|
154
|
+
self,
|
|
155
|
+
title: Optional[str] = None,
|
|
156
|
+
style: StyleTemplate = AREA_STYLE_TEMPLATE,
|
|
157
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
158
|
+
**kwargs: Any,
|
|
159
|
+
) -> Figure:
|
|
160
|
+
"""Plot an area chart on a new figure.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
title : str, optional
|
|
165
|
+
Title for the plot. The default is None.
|
|
166
|
+
style : StyleTemplate, optional
|
|
167
|
+
Style template for the plot. The default is AREA_STYLE_TEMPLATE.
|
|
168
|
+
figsize : tuple[float, float], optional
|
|
169
|
+
Figure size. The default is (10, 6).
|
|
170
|
+
|
|
171
|
+
**kwargs : Any
|
|
172
|
+
Additional keyword arguments forwarded to ``aplot``.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
Figure
|
|
177
|
+
The Matplotlib figure containing the area chart.
|
|
178
|
+
"""
|
|
179
|
+
fig = Figure(figsize=figsize)
|
|
180
|
+
fig.set_facecolor(style.background_color)
|
|
181
|
+
ax = fig.add_subplot(111)
|
|
182
|
+
self.aplot(title=title, style=style, ax=ax, **kwargs)
|
|
183
|
+
return fig
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def aplot_area(
|
|
187
|
+
pd_df: pd.DataFrame,
|
|
188
|
+
x: str,
|
|
189
|
+
y: str,
|
|
190
|
+
label: Optional[str] = None,
|
|
191
|
+
stacked: bool = True,
|
|
192
|
+
title: Optional[str] = None,
|
|
193
|
+
style: StyleTemplate = AREA_STYLE_TEMPLATE,
|
|
194
|
+
ax: Optional[Axes] = None,
|
|
195
|
+
**kwargs: Any,
|
|
196
|
+
) -> Axes:
|
|
197
|
+
"""Plot area charts, optionally stacked for part-to-whole trends."""
|
|
198
|
+
return AreaChart(
|
|
199
|
+
pd_df=pd_df,
|
|
200
|
+
x=x,
|
|
201
|
+
y=y,
|
|
202
|
+
label=label,
|
|
203
|
+
stacked=stacked,
|
|
204
|
+
).aplot(
|
|
205
|
+
title=title,
|
|
206
|
+
style=style,
|
|
207
|
+
ax=ax,
|
|
208
|
+
**kwargs,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def fplot_area(
|
|
213
|
+
pd_df: pd.DataFrame,
|
|
214
|
+
x: str,
|
|
215
|
+
y: str,
|
|
216
|
+
label: Optional[str] = None,
|
|
217
|
+
stacked: bool = True,
|
|
218
|
+
title: Optional[str] = None,
|
|
219
|
+
style: StyleTemplate = AREA_STYLE_TEMPLATE,
|
|
220
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
221
|
+
**kwargs: Any,
|
|
222
|
+
) -> Figure:
|
|
223
|
+
"""Plot area charts on a new figure."""
|
|
224
|
+
return AreaChart(
|
|
225
|
+
pd_df=pd_df,
|
|
226
|
+
x=x,
|
|
227
|
+
y=y,
|
|
228
|
+
label=label,
|
|
229
|
+
stacked=stacked,
|
|
230
|
+
).fplot(
|
|
231
|
+
title=title,
|
|
232
|
+
style=style,
|
|
233
|
+
figsize=figsize,
|
|
234
|
+
**kwargs,
|
|
235
|
+
)
|
MatplotLibAPI/bar.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Bar and stacked bar chart helpers."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import seaborn as sns
|
|
8
|
+
from matplotlib.axes import Axes
|
|
9
|
+
from matplotlib.figure import Figure
|
|
10
|
+
|
|
11
|
+
from .base_plot import BasePlot
|
|
12
|
+
|
|
13
|
+
from .style_template import (
|
|
14
|
+
DISTRIBUTION_STYLE_TEMPLATE,
|
|
15
|
+
StyleTemplate,
|
|
16
|
+
string_formatter,
|
|
17
|
+
validate_dataframe,
|
|
18
|
+
)
|
|
19
|
+
from .utils import _get_axis, _merge_kwargs
|
|
20
|
+
|
|
21
|
+
__all__ = ["DISTRIBUTION_STYLE_TEMPLATE", "aplot_bar", "fplot_bar"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BarChart(BasePlot):
|
|
25
|
+
"""Plot grouped and stacked bar charts from tabular data.
|
|
26
|
+
|
|
27
|
+
Methods
|
|
28
|
+
-------
|
|
29
|
+
aplot
|
|
30
|
+
Plot a bar chart on an existing Matplotlib axes.
|
|
31
|
+
fplot
|
|
32
|
+
Plot a bar chart on a new Matplotlib figure.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
pd_df: pd.DataFrame,
|
|
38
|
+
category: str,
|
|
39
|
+
value: str,
|
|
40
|
+
group: Optional[str] = None,
|
|
41
|
+
stacked: bool = False,
|
|
42
|
+
):
|
|
43
|
+
cols = [category, value]
|
|
44
|
+
if group:
|
|
45
|
+
cols.append(group)
|
|
46
|
+
validate_dataframe(pd_df, cols=cols)
|
|
47
|
+
super().__init__(pd_df=pd_df)
|
|
48
|
+
self.category = category
|
|
49
|
+
self.value = value
|
|
50
|
+
self.group = group
|
|
51
|
+
self.stacked = stacked
|
|
52
|
+
|
|
53
|
+
def aplot(
|
|
54
|
+
self,
|
|
55
|
+
title: Optional[str] = None,
|
|
56
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
57
|
+
ax: Optional[Axes] = None,
|
|
58
|
+
**kwargs: Any,
|
|
59
|
+
) -> Axes:
|
|
60
|
+
"""Plot bar or stacked bar charts for categorical comparisons.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
title : str, optional
|
|
65
|
+
Title for the plot, by default None.
|
|
66
|
+
style : StyleTemplate, optional
|
|
67
|
+
Style template for the plot, by default DISTRIBUTION_STYLE_TEMPLATE.
|
|
68
|
+
ax : Axes, optional
|
|
69
|
+
Matplotlib Axes to plot on, by default None which uses the current Axes.
|
|
70
|
+
**kwargs : Any
|
|
71
|
+
Additional keyword arguments forwarded to the plotting function.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Axes
|
|
76
|
+
The Matplotlib Axes object containing the plot.
|
|
77
|
+
"""
|
|
78
|
+
plot_ax = _get_axis(ax)
|
|
79
|
+
|
|
80
|
+
if self.group:
|
|
81
|
+
pivot_df = self._obj.pivot_table(
|
|
82
|
+
index=self.category,
|
|
83
|
+
columns=self.group,
|
|
84
|
+
values=self.value,
|
|
85
|
+
aggfunc="sum",
|
|
86
|
+
)
|
|
87
|
+
plot_kwargs: dict[str, Any] = {
|
|
88
|
+
"kind": "bar",
|
|
89
|
+
"stacked": self.stacked,
|
|
90
|
+
"ax": plot_ax,
|
|
91
|
+
"alpha": 0.85,
|
|
92
|
+
}
|
|
93
|
+
pivot_df.plot(**_merge_kwargs(plot_kwargs, kwargs))
|
|
94
|
+
else:
|
|
95
|
+
barplot_kwargs: dict[str, Any] = {
|
|
96
|
+
"data": self._obj,
|
|
97
|
+
"x": self.category,
|
|
98
|
+
"y": self.value,
|
|
99
|
+
"palette": style.palette,
|
|
100
|
+
"ax": plot_ax,
|
|
101
|
+
}
|
|
102
|
+
sns.barplot(**_merge_kwargs(barplot_kwargs, kwargs))
|
|
103
|
+
|
|
104
|
+
plot_ax.set_facecolor(style.background_color)
|
|
105
|
+
plot_ax.set_xlabel(string_formatter(self.category))
|
|
106
|
+
plot_ax.set_ylabel(string_formatter(self.value))
|
|
107
|
+
if title:
|
|
108
|
+
plot_ax.set_title(title)
|
|
109
|
+
plot_ax.tick_params(axis="x", labelrotation=45)
|
|
110
|
+
return plot_ax
|
|
111
|
+
|
|
112
|
+
def fplot(
|
|
113
|
+
self,
|
|
114
|
+
title: Optional[str] = None,
|
|
115
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
116
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
117
|
+
) -> Figure:
|
|
118
|
+
"""Plot bar or stacked bar charts on a new figure.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
title : str, optional
|
|
123
|
+
Title for the plot, by default None.
|
|
124
|
+
style : StyleTemplate, optional
|
|
125
|
+
Style template for the plot, by default DISTRIBUTION_STYLE_TEMPLATE.
|
|
126
|
+
figsize : tuple[float, float], optional
|
|
127
|
+
The size of the figure, by default (10, 6).
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
Figure
|
|
132
|
+
The Matplotlib Figure object containing the plot.
|
|
133
|
+
"""
|
|
134
|
+
fig = Figure(
|
|
135
|
+
figsize=figsize,
|
|
136
|
+
facecolor=style.background_color,
|
|
137
|
+
edgecolor=style.background_color,
|
|
138
|
+
)
|
|
139
|
+
ax = fig.add_subplot(111)
|
|
140
|
+
ax.set_facecolor(style.background_color)
|
|
141
|
+
fig.set_facecolor(style.background_color)
|
|
142
|
+
self.aplot(title=title, style=style, ax=ax)
|
|
143
|
+
return fig
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def aplot_bar(
|
|
147
|
+
pd_df: pd.DataFrame,
|
|
148
|
+
category: str,
|
|
149
|
+
value: str,
|
|
150
|
+
group: Optional[str] = None,
|
|
151
|
+
stacked: bool = False,
|
|
152
|
+
title: Optional[str] = None,
|
|
153
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
154
|
+
ax: Optional[Axes] = None,
|
|
155
|
+
**kwargs: Any,
|
|
156
|
+
) -> Axes:
|
|
157
|
+
"""Plot bar or stacked bar charts for categorical comparisons."""
|
|
158
|
+
return BarChart(
|
|
159
|
+
pd_df=pd_df,
|
|
160
|
+
category=category,
|
|
161
|
+
value=value,
|
|
162
|
+
group=group,
|
|
163
|
+
stacked=stacked,
|
|
164
|
+
).aplot(
|
|
165
|
+
title=title,
|
|
166
|
+
style=style,
|
|
167
|
+
ax=ax,
|
|
168
|
+
**kwargs,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def fplot_bar(
|
|
173
|
+
pd_df: pd.DataFrame,
|
|
174
|
+
category: str,
|
|
175
|
+
value: str,
|
|
176
|
+
group: Optional[str] = None,
|
|
177
|
+
stacked: bool = False,
|
|
178
|
+
title: Optional[str] = None,
|
|
179
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
180
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
181
|
+
) -> Figure:
|
|
182
|
+
"""Plot bar or stacked bar charts on a new figure."""
|
|
183
|
+
return BarChart(
|
|
184
|
+
pd_df=pd_df,
|
|
185
|
+
category=category,
|
|
186
|
+
value=value,
|
|
187
|
+
group=group,
|
|
188
|
+
stacked=stacked,
|
|
189
|
+
).fplot(
|
|
190
|
+
title=title,
|
|
191
|
+
style=style,
|
|
192
|
+
figsize=figsize,
|
|
193
|
+
)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Abstract base class for all plot types."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from matplotlib.axes import Axes
|
|
8
|
+
from matplotlib.figure import Figure
|
|
9
|
+
|
|
10
|
+
from .style_template import StyleTemplate
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BasePlot(ABC):
|
|
14
|
+
"""Base class defining the interface for all plot types.
|
|
15
|
+
|
|
16
|
+
This abstract base class ensures consistency across all plot implementations
|
|
17
|
+
by requiring both accessor-based (aplot_*) and figure-based (fplot_*) methods.
|
|
18
|
+
|
|
19
|
+
Methods
|
|
20
|
+
-------
|
|
21
|
+
aplot
|
|
22
|
+
Plot on an existing Matplotlib axes.
|
|
23
|
+
fplot
|
|
24
|
+
Plot on a new Matplotlib figure.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, pd_df: pd.DataFrame):
|
|
28
|
+
"""Initialize the BasePlot."""
|
|
29
|
+
super().__init__()
|
|
30
|
+
self._obj = pd_df
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def aplot(self, *args: Any, **kwargs: Any) -> Axes:
|
|
34
|
+
"""Plot on an existing Matplotlib axes.
|
|
35
|
+
|
|
36
|
+
Subclasses should implement plot-specific parameters as needed.
|
|
37
|
+
Common parameters include title, style, and ax.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
pd_df : pd.DataFrame
|
|
42
|
+
The input DataFrame to plot.
|
|
43
|
+
*args : Any
|
|
44
|
+
Plot-specific positional arguments.
|
|
45
|
+
**kwargs : Any
|
|
46
|
+
Plot-specific keyword arguments. May include:
|
|
47
|
+
- title : str, optional
|
|
48
|
+
Chart title.
|
|
49
|
+
- style : StyleTemplate, optional
|
|
50
|
+
Styling template.
|
|
51
|
+
- ax : Axes, optional
|
|
52
|
+
Matplotlib axes to plot on. If None, uses the current axes.
|
|
53
|
+
- Additional plot-specific parameters.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
Axes
|
|
58
|
+
The Matplotlib axes object with the plot.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def fplot(self, *args: Any, **kwargs: Any) -> Figure:
|
|
63
|
+
"""Plot on a new Matplotlib figure.
|
|
64
|
+
|
|
65
|
+
Subclasses should implement plot-specific parameters as needed.
|
|
66
|
+
Common parameters include title, style, and figsize.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
pd_df : pd.DataFrame
|
|
71
|
+
The input DataFrame to plot.
|
|
72
|
+
*args : Any
|
|
73
|
+
Plot-specific positional arguments.
|
|
74
|
+
**kwargs : Any
|
|
75
|
+
Plot-specific keyword arguments. May include:
|
|
76
|
+
- title : str, optional
|
|
77
|
+
Chart title.
|
|
78
|
+
- style : StyleTemplate, optional
|
|
79
|
+
Styling template.
|
|
80
|
+
- figsize : tuple, optional
|
|
81
|
+
Figure size as (width, height).
|
|
82
|
+
- Additional plot-specific parameters.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Figure
|
|
87
|
+
The Matplotlib figure object with the plot.
|
|
88
|
+
"""
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Box and violin plot helpers."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import seaborn as sns
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
from matplotlib.axes import Axes
|
|
9
|
+
from matplotlib.figure import Figure
|
|
10
|
+
|
|
11
|
+
from .base_plot import BasePlot
|
|
12
|
+
|
|
13
|
+
from .style_template import (
|
|
14
|
+
DISTRIBUTION_STYLE_TEMPLATE,
|
|
15
|
+
StyleTemplate,
|
|
16
|
+
string_formatter,
|
|
17
|
+
validate_dataframe,
|
|
18
|
+
)
|
|
19
|
+
from .utils import _get_axis, _merge_kwargs
|
|
20
|
+
|
|
21
|
+
__all__ = ["DISTRIBUTION_STYLE_TEMPLATE", "aplot_box_violin", "fplot_box_violin"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BoxViolinPlot(BasePlot):
|
|
25
|
+
"""Plot box and violin distribution charts from tabular data.
|
|
26
|
+
|
|
27
|
+
Methods
|
|
28
|
+
-------
|
|
29
|
+
aplot
|
|
30
|
+
Plot a box or violin chart on an existing Matplotlib axes.
|
|
31
|
+
fplot
|
|
32
|
+
Plot a box or violin chart on a new Matplotlib figure.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
pd_df: pd.DataFrame,
|
|
38
|
+
column: str,
|
|
39
|
+
by: Optional[str] = None,
|
|
40
|
+
violin: bool = False,
|
|
41
|
+
):
|
|
42
|
+
cols = [column]
|
|
43
|
+
if by:
|
|
44
|
+
cols.append(by)
|
|
45
|
+
validate_dataframe(pd_df=pd_df, cols=cols)
|
|
46
|
+
super().__init__(pd_df=pd_df)
|
|
47
|
+
self.column = column
|
|
48
|
+
self.by = by
|
|
49
|
+
self.violin = violin
|
|
50
|
+
|
|
51
|
+
def aplot(
|
|
52
|
+
self,
|
|
53
|
+
title: Optional[str] = None,
|
|
54
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
55
|
+
ax: Optional[Axes] = None,
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> Axes:
|
|
58
|
+
"""Plot a box or violin chart on the provided axis.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
title : str, optional
|
|
63
|
+
Title for the plot. The default is None.
|
|
64
|
+
style : StyleTemplate, optional
|
|
65
|
+
Style template for the plot. The default is DISTRIBUTION_STYLE_TEMPLATE.
|
|
66
|
+
ax : Axes, optional
|
|
67
|
+
Matplotlib axes to plot on. If None, use the current axes.
|
|
68
|
+
**kwargs : Any
|
|
69
|
+
Additional keyword arguments reserved for compatibility.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
Axes
|
|
74
|
+
The Matplotlib axes containing the distribution chart.
|
|
75
|
+
"""
|
|
76
|
+
plot_ax = _get_axis(ax)
|
|
77
|
+
|
|
78
|
+
common_kwargs = {
|
|
79
|
+
"data": self._obj,
|
|
80
|
+
"x": self.by,
|
|
81
|
+
"y": self.column,
|
|
82
|
+
"palette": style.palette,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
plot_kwargs: dict[str, Any] = {
|
|
86
|
+
**common_kwargs,
|
|
87
|
+
"hue": self.by,
|
|
88
|
+
"legend": False,
|
|
89
|
+
"ax": plot_ax,
|
|
90
|
+
}
|
|
91
|
+
merged_plot_kwargs = _merge_kwargs(plot_kwargs, kwargs)
|
|
92
|
+
|
|
93
|
+
if self.violin:
|
|
94
|
+
sns.violinplot(**merged_plot_kwargs)
|
|
95
|
+
else:
|
|
96
|
+
sns.boxplot(**merged_plot_kwargs)
|
|
97
|
+
|
|
98
|
+
plot_ax.set_facecolor(style.background_color)
|
|
99
|
+
plot_ax.set_ylabel(string_formatter(self.column))
|
|
100
|
+
if self.by:
|
|
101
|
+
plot_ax.set_xlabel(string_formatter(self.by))
|
|
102
|
+
if title:
|
|
103
|
+
plot_ax.set_title(title)
|
|
104
|
+
return plot_ax
|
|
105
|
+
|
|
106
|
+
def fplot(
|
|
107
|
+
self,
|
|
108
|
+
title: Optional[str] = None,
|
|
109
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
110
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
111
|
+
) -> Figure:
|
|
112
|
+
"""Plot a box or violin chart on a new figure.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
title : str, optional
|
|
117
|
+
Title for the plot. The default is None.
|
|
118
|
+
style : StyleTemplate, optional
|
|
119
|
+
Style template for the plot. The default is DISTRIBUTION_STYLE_TEMPLATE.
|
|
120
|
+
figsize : tuple[float, float], optional
|
|
121
|
+
Figure size. The default is (10, 6).
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
Figure
|
|
126
|
+
The Matplotlib figure containing the distribution chart.
|
|
127
|
+
"""
|
|
128
|
+
fig = Figure(
|
|
129
|
+
figsize=figsize,
|
|
130
|
+
facecolor=style.background_color,
|
|
131
|
+
edgecolor=style.background_color,
|
|
132
|
+
)
|
|
133
|
+
ax = fig.add_subplot(111)
|
|
134
|
+
ax.set_facecolor(style.background_color)
|
|
135
|
+
self.aplot(
|
|
136
|
+
title=title,
|
|
137
|
+
style=style,
|
|
138
|
+
ax=ax,
|
|
139
|
+
)
|
|
140
|
+
return fig
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def aplot_box_violin(
|
|
144
|
+
pd_df: pd.DataFrame,
|
|
145
|
+
column: str,
|
|
146
|
+
by: Optional[str] = None,
|
|
147
|
+
violin: bool = False,
|
|
148
|
+
title: Optional[str] = None,
|
|
149
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
150
|
+
ax: Optional[Axes] = None,
|
|
151
|
+
**kwargs: Any,
|
|
152
|
+
) -> Axes:
|
|
153
|
+
"""Plot box or violin charts to summarize distributions."""
|
|
154
|
+
return BoxViolinPlot(
|
|
155
|
+
pd_df=pd_df,
|
|
156
|
+
column=column,
|
|
157
|
+
by=by,
|
|
158
|
+
violin=violin,
|
|
159
|
+
).aplot(
|
|
160
|
+
title=title,
|
|
161
|
+
style=style,
|
|
162
|
+
ax=ax,
|
|
163
|
+
**kwargs,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def fplot_box_violin(
|
|
168
|
+
pd_df: pd.DataFrame,
|
|
169
|
+
column: str,
|
|
170
|
+
by: Optional[str] = None,
|
|
171
|
+
violin: bool = False,
|
|
172
|
+
title: Optional[str] = None,
|
|
173
|
+
style: StyleTemplate = DISTRIBUTION_STYLE_TEMPLATE,
|
|
174
|
+
figsize: Tuple[float, float] = (10, 6),
|
|
175
|
+
) -> Figure:
|
|
176
|
+
"""Plot box or violin charts on a new figure."""
|
|
177
|
+
return BoxViolinPlot(
|
|
178
|
+
pd_df=pd_df,
|
|
179
|
+
column=column,
|
|
180
|
+
by=by,
|
|
181
|
+
violin=violin,
|
|
182
|
+
).fplot(
|
|
183
|
+
title=title,
|
|
184
|
+
style=style,
|
|
185
|
+
figsize=figsize,
|
|
186
|
+
)
|