MatplotLibAPI 3.2.13__py3-none-any.whl → 3.2.14__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/Bubble.py CHANGED
@@ -1,176 +1,453 @@
1
- # Hint for Visual Code Python Interactive window
2
- # %%
3
- from typing import Optional, Tuple
4
- import pandas as pd
1
+ """Bubble chart plotting helpers.
2
+
3
+ Provides functions to create and render bubble charts using seaborn and matplotlib,
4
+ with customizable styling via `StyleTemplate`.
5
+ """
6
+
7
+ from typing import Dict, Optional, Tuple, cast
8
+
5
9
  import matplotlib.pyplot as plt
6
- from matplotlib.ticker import NullLocator
10
+ import pandas as pd
11
+ import seaborn as sns
7
12
  from matplotlib.axes import Axes
8
13
  from matplotlib.figure import Figure
9
- import seaborn as sns
10
-
14
+ from matplotlib.ticker import NullLocator
11
15
 
12
- from MatplotLibAPI.StyleTemplate import DynamicFuncFormatter, StyleTemplate, generate_ticks, string_formatter, bmk_formatter, percent_formatter, format_func, validate_dataframe
16
+ from .StyleTemplate import (
17
+ BUBBLE_STYLE_TEMPLATE,
18
+ MAX_RESULTS,
19
+ StyleTemplate,
20
+ bmk_formatter,
21
+ format_func,
22
+ generate_ticks,
23
+ percent_formatter,
24
+ string_formatter,
25
+ validate_dataframe,
26
+ DynamicFuncFormatter,
27
+ FormatterFunc,
28
+ )
13
29
 
14
- MAX_RESULTS = 50
15
30
 
16
- BUBBLE_STYLE_TEMPLATE = StyleTemplate(
17
- format_funcs={"label": string_formatter,
18
- "x": bmk_formatter,
19
- "y": percent_formatter,
20
- "label": string_formatter,
21
- "z": bmk_formatter},
22
- yscale="log"
23
- )
31
+ def _prepare_bubble_data(
32
+ pd_df: pd.DataFrame,
33
+ label: str,
34
+ x: str,
35
+ y: str,
36
+ z: str,
37
+ sort_by: Optional[str],
38
+ ascending: bool,
39
+ max_values: int,
40
+ center_to_mean: bool,
41
+ style: StyleTemplate,
42
+ ) -> pd.DataFrame:
43
+ """Prepare data for bubble chart.
24
44
 
45
+ Parameters
46
+ ----------
47
+ pd_df : pd.DataFrame
48
+ Input DataFrame.
49
+ label : str
50
+ Column name for bubble labels.
51
+ x : str
52
+ Column name for x-axis values.
53
+ y : str
54
+ Column name for y-axis values.
55
+ z : str
56
+ Column name for bubble sizes.
57
+ sort_by : Optional[str]
58
+ Column to sort by.
59
+ ascending : bool
60
+ Sort order.
61
+ max_values : int
62
+ Maximum number of bubbles to display.
63
+ center_to_mean : bool
64
+ Whether to center x-axis values around the mean.
65
+ style : StyleTemplate
66
+ Styling for the plot.
25
67
 
26
- def aplot_bubble(
27
- pd_df: pd.DataFrame,
28
- label: str,
29
- x: str,
30
- y: str,
31
- z: str,
32
- title: Optional[str] = None,
33
- style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
34
- max_values: int = MAX_RESULTS,
35
- center_to_mean: bool = False,
36
- sort_by: Optional[str] = None,
37
- ascending: bool = False,
38
- hline=False,
39
- vline=False,
40
- ax: Optional[Axes] = None):
68
+ Returns
69
+ -------
70
+ pd.DataFrame
71
+ Prepared DataFrame for plotting.
41
72
 
73
+ Raises
74
+ ------
75
+ AttributeError
76
+ If required columns are missing from the DataFrame.
77
+ """
42
78
  validate_dataframe(pd_df, cols=[label, x, y, z], sort_by=sort_by)
43
- style.format_funcs = format_func(
44
- style.format_funcs, label=label, x=x, y=y, z=z)
45
- if not sort_by:
46
- sort_by = z
79
+ sort_col = sort_by or z
80
+
81
+ plot_df = (
82
+ pd_df[[label, x, y, z]]
83
+ .sort_values(by=[sort_col], ascending=ascending) # type: ignore
84
+ .head(max_values)
85
+ .copy()
86
+ )
47
87
 
48
- plot_df = pd_df[[label, x, y, z]].sort_values(
49
- by=sort_by, ascending=ascending).head(max_values)
50
88
  if center_to_mean:
51
- x_col_mean = plot_df[x].mean()
52
- plot_df[x] = plot_df[x] - x_col_mean
53
- plot_df['quintile'] = pd.qcut(
54
- plot_df[z], 5, labels=False)
89
+ plot_df[x] -= plot_df[x].mean()
90
+
91
+ plot_df["quintile"] = pd.qcut(plot_df[z], 5, labels=False, duplicates="drop")
92
+ plot_df["fontsize"] = plot_df["quintile"].map(style.font_mapping) # type: ignore
93
+ return plot_df
55
94
 
56
- # styling
57
95
 
58
- plot_df["fontsize"] = plot_df['quintile'].map(style.font_mapping)
96
+ def _setup_bubble_axes(
97
+ ax: Axes,
98
+ style: StyleTemplate,
99
+ pd_df: pd.DataFrame,
100
+ x: str,
101
+ y: str,
102
+ format_funcs: Optional[Dict[str, Optional[FormatterFunc]]],
103
+ ) -> None:
104
+ """Configure axes for the bubble chart.
59
105
 
60
- if not ax:
61
- ax = plt.gca()
106
+ Parameters
107
+ ----------
108
+ ax : Axes
109
+ Matplotlib axes object.
110
+ style : StyleTemplate
111
+ Styling for the plot.
112
+ pd_df : pd.DataFrame
113
+ DataFrame used for plotting.
114
+ x : str
115
+ Column name for x-axis values.
116
+ y : str
117
+ Column name for y-axis values.
118
+ format_funcs : Optional[Dict[str, Optional[FormatterFunc]]]
119
+ Functions to format axis tick labels.
120
+ """
121
+ ax.set_facecolor(style.background_color)
122
+
123
+ if style.xscale:
124
+ ax.set(xscale=style.xscale)
125
+ if style.yscale:
126
+ ax.set(yscale=style.yscale)
127
+
128
+ # X-axis ticks and formatting
129
+ x_min, x_max = cast(float, pd_df[x].min()), cast(float, pd_df[x].max())
130
+ ax.xaxis.set_ticks(generate_ticks(x_min, x_max, num_ticks=style.x_ticks))
131
+ ax.xaxis.grid(True, "major", linewidth=0.5, color=style.font_color)
132
+ if format_funcs and (fmt_x := format_funcs.get(x)):
133
+ ax.xaxis.set_major_formatter(DynamicFuncFormatter(fmt_x))
62
134
 
63
- ax = sns.scatterplot(
135
+ # Y-axis ticks and formatting
136
+ y_min, y_max = cast(float, pd_df[y].min()), cast(float, pd_df[y].max())
137
+ ax.yaxis.set_ticks(generate_ticks(y_min, y_max, num_ticks=style.y_ticks))
138
+ if style.yscale == "log":
139
+ ax.yaxis.set_minor_locator(NullLocator())
140
+ else:
141
+ ax.minorticks_off()
142
+ ax.yaxis.grid(True, "major", linewidth=0.5, color=style.font_color)
143
+ if format_funcs and (fmt_y := format_funcs.get(y)):
144
+ ax.yaxis.set_major_formatter(DynamicFuncFormatter(fmt_y))
145
+
146
+ ax.tick_params(
147
+ axis="both", which="major", colors=style.font_color, labelsize=style.font_size
148
+ )
149
+
150
+
151
+ def _draw_bubbles(
152
+ ax: Axes, plot_df: pd.DataFrame, x: str, y: str, z: str, style: StyleTemplate
153
+ ) -> None:
154
+ """Draw bubbles on the axes.
155
+
156
+ Parameters
157
+ ----------
158
+ ax : Axes
159
+ Matplotlib axes object.
160
+ plot_df : pd.DataFrame
161
+ DataFrame with data for plotting.
162
+ x : str
163
+ Column name for x-axis values.
164
+ y : str
165
+ Column name for y-axis values.
166
+ z : str
167
+ Column name for bubble sizes.
168
+ style : StyleTemplate
169
+ Styling for the plot.
170
+ """
171
+ sns.scatterplot(
64
172
  data=plot_df,
65
173
  x=x,
66
174
  y=y,
67
175
  size=z,
68
- hue='quintile',
176
+ hue="quintile",
69
177
  sizes=(100, 2000),
70
178
  legend=False,
71
179
  palette=sns.color_palette(style.palette, as_cmap=True),
72
180
  edgecolor=style.background_color,
73
- ax=ax)
74
- ax.set_facecolor(style.background_color)
75
- if style.xscale:
76
- ax.set(xscale=style.xscale)
77
- if style.yscale:
78
- ax.set(yscale=style.yscale)
181
+ ax=ax,
182
+ )
79
183
 
80
- x_min = pd_df[x].min()
81
- x_max = pd_df[x].max()
82
- x_mean = pd_df[x].mean()
83
- ax.set_xticks(generate_ticks(x_min, x_max, num_ticks=style.x_ticks))
84
- ax.xaxis.grid(True, "major", linewidth=.5, color=style.font_color)
85
- if style.format_funcs.get("x"):
86
- ax.xaxis.set_major_formatter(
87
- DynamicFuncFormatter(style.format_funcs.get("x")))
88
184
 
89
- y_min = pd_df[y].min()
90
- y_max = pd_df[y].max()
91
- y_mean = pd_df[y].mean()
92
- ax.set_yticks(generate_ticks(y_min, y_max, num_ticks=style.y_ticks))
185
+ def _draw_bubble_labels(
186
+ ax: Axes,
187
+ plot_df: pd.DataFrame,
188
+ label: str,
189
+ x: str,
190
+ y: str,
191
+ style: StyleTemplate,
192
+ format_funcs: Optional[Dict[str, Optional[FormatterFunc]]],
193
+ ) -> None:
194
+ """Draw labels for each bubble.
93
195
 
196
+ Parameters
197
+ ----------
198
+ ax : Axes
199
+ Matplotlib axes object.
200
+ plot_df : pd.DataFrame
201
+ DataFrame with data for plotting.
202
+ label : str
203
+ Column name for bubble labels.
204
+ x : str
205
+ Column name for x-axis values.
206
+ y : str
207
+ Column name for y-axis values.
208
+ style : StyleTemplate
209
+ Styling for the plot.
210
+ format_funcs : Optional[Dict[str, Optional[FormatterFunc]]]
211
+ Functions to format bubble labels.
212
+ """
213
+ for _, row in plot_df.iterrows():
214
+ x_val, y_val, label_val = row[x], row[y], str(row[label])
215
+ if format_funcs and (fmt_label := format_funcs.get(label)):
216
+ label_val = fmt_label(label_val, None)
217
+ ax.text(
218
+ cast(float, x_val),
219
+ cast(float, y_val),
220
+ label_val,
221
+ ha="center",
222
+ fontsize=row["fontsize"],
223
+ color=style.font_color,
224
+ )
94
225
 
95
- if style.yscale == 'log':
96
- ax.yaxis.set_minor_locator(NullLocator()) # Disable minor ticks for log scale
97
- else:
98
- ax.minorticks_off() # Disable minor ticks for linear scale
99
226
 
100
- ax.yaxis.grid(True, "major", linewidth=.5, color=style.font_color)
101
- if style.format_funcs.get("y"):
102
- ax.yaxis.set_major_formatter(
103
- DynamicFuncFormatter(style.format_funcs.get("y")))
227
+ def _draw_mean_lines(
228
+ ax: Axes,
229
+ plot_df: pd.DataFrame,
230
+ x: str,
231
+ y: str,
232
+ hline: bool,
233
+ vline: bool,
234
+ style: StyleTemplate,
235
+ ) -> None:
236
+ """Draw horizontal and vertical mean lines.
104
237
 
105
- ax.tick_params(axis='both',
106
- which='major',
107
- colors=style.font_color,
108
- labelsize=style.font_size)
238
+ Parameters
239
+ ----------
240
+ ax : Axes
241
+ Matplotlib axes object.
242
+ plot_df : pd.DataFrame
243
+ DataFrame with data for plotting.
244
+ x : str
245
+ Column name for x-axis values.
246
+ y : str
247
+ Column name for y-axis values.
248
+ hline : bool
249
+ Whether to draw a horizontal line at the mean of y.
250
+ vline : bool
251
+ Whether to draw a vertical line at the mean of x.
252
+ style : StyleTemplate
253
+ Styling for the plot.
254
+ """
109
255
  if vline:
110
- ax.vlines(x=x_mean,
111
- ymin=y_min,
112
- ymax=y_max,
113
- linestyle='--',
114
- colors=style.font_color)
256
+ ax.axvline(
257
+ int(cast(float, plot_df[x].mean())), linestyle="--", color=style.font_color
258
+ )
115
259
  if hline:
116
- ax.hlines(y=y_mean,
117
- xmin=x_min,
118
- xmax=x_max,
119
- linestyle='--',
120
- colors=style.font_color)
121
-
122
- for index, row in plot_df.iterrows():
123
- x_value = row[x]
124
- y_value = row[y]
125
- s_value = str(row[label])
126
- if style.format_funcs.get("label"):
127
- s_value = style.format_funcs.get("label")(s_value)
128
- fs = row["fontsize"]
129
- ax.text(x_value,
130
- y_value,
131
- s_value,
132
- horizontalalignment='center',
133
- fontdict={'color': style.font_color, 'fontsize': fs})
260
+ ax.axhline(
261
+ int(cast(float, plot_df[y].mean())), linestyle="--", color=style.font_color
262
+ )
263
+
264
+
265
+ def aplot_bubble(
266
+ pd_df: pd.DataFrame,
267
+ label: str,
268
+ x: str,
269
+ y: str,
270
+ z: str,
271
+ title: Optional[str] = None,
272
+ style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
273
+ max_values: int = MAX_RESULTS,
274
+ center_to_mean: bool = False,
275
+ sort_by: Optional[str] = None,
276
+ ascending: bool = False,
277
+ hline: bool = False,
278
+ vline: bool = False,
279
+ ax: Optional[Axes] = None,
280
+ ) -> Axes:
281
+ """Plot a bubble chart onto the given axes.
282
+
283
+ Parameters
284
+ ----------
285
+ pd_df : pd.DataFrame
286
+ DataFrame containing the data to plot.
287
+ label : str
288
+ Column name used for labeling bubbles.
289
+ x : str
290
+ Column name for x-axis values.
291
+ y : str
292
+ Column name for y-axis values.
293
+ z : str
294
+ Column name for bubble sizes.
295
+ title : str, optional
296
+ Plot title.
297
+ style : StyleTemplate, optional
298
+ Plot styling options. The default is `BUBBLE_STYLE_TEMPLATE`.
299
+ max_values : int, optional
300
+ Max number of rows to display. The default is `MAX_RESULTS`.
301
+ center_to_mean : bool, optional
302
+ Whether to center x values around their mean. The default is `False`.
303
+ sort_by : str, optional
304
+ Column to sort by before slicing.
305
+ ascending : bool, optional
306
+ Sort order. The default is `False`.
307
+ hline : bool, optional
308
+ Whether to draw a horizontal line at the mean of y. The default is `False`.
309
+ vline : bool, optional
310
+ Whether to draw a vertical line at the mean of x. The default is `False`.
311
+ ax : Axes, optional
312
+ Existing matplotlib axes to use. If None, uses current axes.
313
+
314
+ Returns
315
+ -------
316
+ Axes
317
+ The matplotlib Axes object containing the bubble chart.
318
+
319
+ Raises
320
+ ------
321
+ AttributeError
322
+ If required columns are not in the DataFrame.
323
+
324
+ Examples
325
+ --------
326
+ >>> import pandas as pd
327
+ >>> import matplotlib.pyplot as plt
328
+ >>> from MatplotLibAPI.Bubble import aplot_bubble
329
+ >>> data = {
330
+ ... 'country': ['A', 'B', 'C', 'D'],
331
+ ... 'gdp_per_capita': [45000, 42000, 52000, 48000],
332
+ ... 'life_expectancy': [81, 78, 83, 82],
333
+ ... 'population': [10, 20, 5, 30]
334
+ ... }
335
+ >>> df = pd.DataFrame(data)
336
+ >>> fig, ax = plt.subplots()
337
+ >>> aplot_bubble(df, label='country', x='gdp_per_capita', y='life_expectancy', z='population', ax=ax)
338
+ """
339
+ if ax is None:
340
+ ax = cast(Axes, plt.gca())
341
+
342
+ plot_df = _prepare_bubble_data(
343
+ pd_df, label, x, y, z, sort_by, ascending, max_values, center_to_mean, style
344
+ )
345
+
346
+ format_funcs = format_func(style.format_funcs, label=label, x=x, y=y, z=z)
347
+
348
+ _setup_bubble_axes(ax, style, plot_df, x, y, format_funcs)
349
+
350
+ _draw_bubbles(ax, plot_df, x, y, z, style)
351
+
352
+ _draw_mean_lines(ax, plot_df, x, y, hline, vline, style)
353
+
354
+ _draw_bubble_labels(ax, plot_df, label, x, y, style, format_funcs)
355
+
134
356
  if title:
135
- ax.set_title(title, color=style.font_color, fontsize=style.font_size*2)
357
+ ax.set_title(title, color=style.font_color, fontsize=style.font_size * 2)
358
+
136
359
  return ax
137
360
 
138
361
 
139
362
  def fplot_bubble(
140
- pd_df: pd.DataFrame,
141
- label: str,
142
- x: str,
143
- y: str,
144
- z: str,
145
- title: Optional[str] = "Test",
146
- style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
147
- max_values: int = BUBBLE_STYLE_TEMPLATE,
148
- center_to_mean: bool = False,
149
- sort_by: Optional[str] = None,
150
- ascending: bool = False,
151
- hline=False,
152
- vline=False,
153
- figsize: Tuple[float, float] = (19.2, 10.8)) -> Figure:
154
-
155
- fig = plt.figure(figsize=figsize)
156
- fig.patch.set_facecolor(style.background_color)
157
- ax = fig.add_subplot()
158
- ax = aplot_bubble(pd_df=pd_df,
159
- label=label,
160
- x=x,
161
- y=y,
162
- z=z,
163
- title=title,
164
- style=style,
165
- max_values=max_values,
166
- center_to_mean=center_to_mean,
167
- sort_by=sort_by,
168
- ascending=ascending,
169
- hline=hline,
170
- vline=vline,
171
- ax=ax)
172
- return fig
363
+ pd_df: pd.DataFrame,
364
+ label: str,
365
+ x: str,
366
+ y: str,
367
+ z: str,
368
+ title: Optional[str] = None,
369
+ style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
370
+ max_values: int = MAX_RESULTS,
371
+ center_to_mean: bool = False,
372
+ sort_by: Optional[str] = None,
373
+ ascending: bool = False,
374
+ hline: bool = False,
375
+ vline: bool = False,
376
+ figsize: Tuple[float, float] = (19.2, 10.8),
377
+ ) -> Figure:
378
+ """Create a new matplotlib Figure with a bubble chart.
379
+
380
+ Parameters
381
+ ----------
382
+ pd_df : pd.DataFrame
383
+ DataFrame containing the data to plot.
384
+ label : str
385
+ Column name for bubble labels.
386
+ x : str
387
+ Column name for x-axis values.
388
+ y : str
389
+ Column name for y-axis values.
390
+ z : str
391
+ Column name for bubble sizes.
392
+ title : str, optional
393
+ Title for the chart. The default is ``None``.
394
+ style : StyleTemplate, optional
395
+ Plot styling. The default is `BUBBLE_STYLE_TEMPLATE`.
396
+ max_values : int, optional
397
+ Max number of rows to display. The default is `MAX_RESULTS`.
398
+ center_to_mean : bool, optional
399
+ Whether to center x around its mean. The default is `False`.
400
+ sort_by : str, optional
401
+ Column to sort by.
402
+ ascending : bool, optional
403
+ Sort order. The default is `False`.
404
+ hline : bool, optional
405
+ Draw horizontal line at mean y. The default is `False`.
406
+ vline : bool, optional
407
+ Draw vertical line at mean x. The default is `False`.
408
+ figsize : tuple[float, float], optional
409
+ Size of the figure. The default is (19.2, 10.8).
173
410
 
411
+ Returns
412
+ -------
413
+ Figure
414
+ A matplotlib Figure object containing the bubble chart.
174
415
 
416
+ Raises
417
+ ------
418
+ AttributeError
419
+ If required columns are not in the DataFrame.
175
420
 
176
- # endregion
421
+ Examples
422
+ --------
423
+ >>> import pandas as pd
424
+ >>> from MatplotLibAPI.Bubble import fplot_bubble
425
+ >>> data = {
426
+ ... 'country': ['A', 'B', 'C', 'D'],
427
+ ... 'gdp_per_capita': [45000, 42000, 52000, 48000],
428
+ ... 'life_expectancy': [81, 78, 83, 82],
429
+ ... 'population': [10, 20, 5, 30]
430
+ ... }
431
+ >>> df = pd.DataFrame(data)
432
+ >>> fig = fplot_bubble(df, label='country', x='gdp_per_capita', y='life_expectancy', z='population')
433
+ """
434
+ fig = cast(Figure, plt.figure(figsize=figsize))
435
+ fig.patch.set_facecolor(style.background_color)
436
+ ax = fig.add_subplot()
437
+ aplot_bubble(
438
+ pd_df=pd_df,
439
+ label=label,
440
+ x=x,
441
+ y=y,
442
+ z=z,
443
+ title=title,
444
+ style=style,
445
+ max_values=max_values,
446
+ center_to_mean=center_to_mean,
447
+ sort_by=sort_by,
448
+ ascending=ascending,
449
+ hline=hline,
450
+ vline=vline,
451
+ ax=ax,
452
+ )
453
+ return fig