MatplotLibAPI 3.2.21__py3-none-any.whl → 4.0.0__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 +519 -196
- MatplotLibAPI/area.py +177 -0
- MatplotLibAPI/bar.py +185 -0
- MatplotLibAPI/base_plot.py +88 -0
- MatplotLibAPI/box_violin.py +180 -0
- MatplotLibAPI/bubble.py +568 -0
- MatplotLibAPI/{Composite.py → composite.py} +127 -106
- MatplotLibAPI/heatmap.py +223 -0
- MatplotLibAPI/histogram.py +170 -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/core.py +1360 -0
- MatplotLibAPI/network/plot.py +597 -0
- MatplotLibAPI/network/scaling.py +56 -0
- MatplotLibAPI/pie.py +154 -0
- MatplotLibAPI/pivot.py +274 -0
- MatplotLibAPI/sankey.py +99 -0
- MatplotLibAPI/{StyleTemplate.py → style_template.py} +27 -22
- MatplotLibAPI/sunburst.py +139 -0
- MatplotLibAPI/{Table.py → table.py} +112 -87
- MatplotLibAPI/{Timeserie.py → timeserie.py} +98 -42
- MatplotLibAPI/{Treemap.py → treemap.py} +43 -55
- MatplotLibAPI/typing.py +12 -0
- MatplotLibAPI/{_visualization_utils.py → utils.py} +7 -13
- MatplotLibAPI/waffle.py +173 -0
- MatplotLibAPI/word_cloud.py +489 -0
- {matplotlibapi-3.2.21.dist-info → matplotlibapi-4.0.0.dist-info}/METADATA +98 -9
- matplotlibapi-4.0.0.dist-info/RECORD +36 -0
- {matplotlibapi-3.2.21.dist-info → matplotlibapi-4.0.0.dist-info}/WHEEL +1 -1
- matplotlibapi-4.0.0.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 -458
- MatplotLibAPI/Heatmap.py +0 -121
- MatplotLibAPI/Histogram.py +0 -73
- MatplotLibAPI/Network.py +0 -989
- 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/Wordcloud.py +0 -373
- MatplotLibAPI/_typing.py +0 -17
- matplotlibapi-3.2.21.dist-info/RECORD +0 -26
- {matplotlibapi-3.2.21.dist-info → matplotlibapi-4.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"""Word cloud plotting utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Iterable, Optional, Sequence, Tuple, Union
|
|
6
|
+
|
|
7
|
+
from matplotlib.transforms import BboxBase
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib import colormaps
|
|
12
|
+
from matplotlib.axes import Axes
|
|
13
|
+
from matplotlib.backend_bases import FigureCanvasBase
|
|
14
|
+
from matplotlib.figure import Figure, SubFigure
|
|
15
|
+
from wordcloud import WordCloud
|
|
16
|
+
|
|
17
|
+
from .base_plot import BasePlot
|
|
18
|
+
|
|
19
|
+
from .utils import _get_axis
|
|
20
|
+
|
|
21
|
+
from .style_template import (
|
|
22
|
+
FIG_SIZE,
|
|
23
|
+
MAX_RESULTS,
|
|
24
|
+
TITLE_SCALE_FACTOR,
|
|
25
|
+
WORDCLOUD_STYLE_TEMPLATE,
|
|
26
|
+
StyleTemplate,
|
|
27
|
+
string_formatter,
|
|
28
|
+
validate_dataframe,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _filter_stopwords(
|
|
33
|
+
words: Iterable[str], stopwords: Optional[Iterable[str]]
|
|
34
|
+
) -> np.ndarray:
|
|
35
|
+
"""Remove stopwords from a sequence of words.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
words : Iterable[str]
|
|
40
|
+
Words to filter.
|
|
41
|
+
stopwords : Iterable[str], optional
|
|
42
|
+
Collection of stopwords to exclude. Defaults to ``None``.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
numpy.ndarray
|
|
47
|
+
Filtered words.
|
|
48
|
+
"""
|
|
49
|
+
if stopwords is None:
|
|
50
|
+
return np.array(list(words))
|
|
51
|
+
|
|
52
|
+
stop_set: set[str] = {word.lower() for word in stopwords}
|
|
53
|
+
return np.array([word for word in words if word.lower() not in stop_set])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def from_pandas_nodelist(
|
|
57
|
+
nodes_df: pd.DataFrame,
|
|
58
|
+
node_col: str = "node",
|
|
59
|
+
weight_col: str = "weight",
|
|
60
|
+
):
|
|
61
|
+
"""Create SankeyData from a DataFrame."""
|
|
62
|
+
validate_dataframe(nodes_df, cols=[node_col, weight_col])
|
|
63
|
+
return nodes_df[[node_col, weight_col]].set_index(node_col)[weight_col].to_dict()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _prepare_word_frequencies(
|
|
67
|
+
pd_df: pd.DataFrame,
|
|
68
|
+
text_column: str,
|
|
69
|
+
weight_column: str,
|
|
70
|
+
max_words: int,
|
|
71
|
+
stopwords: Optional[Iterable[str]],
|
|
72
|
+
) -> Tuple[list[str], list[float]]:
|
|
73
|
+
"""Aggregate and filter word frequencies.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
pd_df : pandas.DataFrame
|
|
78
|
+
Input DataFrame containing word data.
|
|
79
|
+
text_column : str
|
|
80
|
+
Column containing words or phrases.
|
|
81
|
+
weight_column : str, optional
|
|
82
|
+
Column containing numeric weights. Defaults to ``None``.
|
|
83
|
+
max_words : int
|
|
84
|
+
Maximum number of words to include.
|
|
85
|
+
stopwords : Iterable[str], optional
|
|
86
|
+
Words to exclude from the visualization. Defaults to ``None``.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
tuple of list
|
|
91
|
+
Lists of filtered words and their corresponding weights.
|
|
92
|
+
|
|
93
|
+
Raises
|
|
94
|
+
------
|
|
95
|
+
AttributeError
|
|
96
|
+
If required columns are missing from the DataFrame.
|
|
97
|
+
"""
|
|
98
|
+
validate_dataframe(pd_df, cols=[text_column], sort_by=weight_column)
|
|
99
|
+
word_frequencies = from_pandas_nodelist(
|
|
100
|
+
pd_df, node_col=text_column, weight_col=weight_column
|
|
101
|
+
)
|
|
102
|
+
words: np.ndarray = np.asarray(list(word_frequencies.keys()), dtype=np.str_)
|
|
103
|
+
weights: np.ndarray = np.asarray(list(word_frequencies.values()), dtype=np.float64)
|
|
104
|
+
|
|
105
|
+
filtered_words = _filter_stopwords(words, stopwords)
|
|
106
|
+
mask: np.ndarray = np.asarray(np.isin(words, filtered_words), dtype=bool)
|
|
107
|
+
filtered_weights: np.ndarray = np.asarray(weights[mask], dtype=np.float64)
|
|
108
|
+
|
|
109
|
+
sorted_indices = np.argsort(filtered_weights)[::-1]
|
|
110
|
+
sorted_words = filtered_words[sorted_indices][:max_words].tolist()
|
|
111
|
+
sorted_weights = filtered_weights[sorted_indices][:max_words].tolist()
|
|
112
|
+
|
|
113
|
+
return sorted_words, sorted_weights
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _create_circular_mask(size: int = 300, radius: Optional[int] = None) -> np.ndarray:
|
|
117
|
+
"""Construct a binary mask with a circular opening for a word cloud.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
size : int, optional
|
|
122
|
+
Width and height of the mask in pixels. Defaults to ``300``.
|
|
123
|
+
radius : int, optional
|
|
124
|
+
Radius of the circular opening in pixels. Defaults to ``size // 2``.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
numpy.ndarray
|
|
129
|
+
Two-dimensional array suitable for the ``mask`` argument of
|
|
130
|
+
``wordcloud.WordCloud`` where ``0`` values define the drawable region.
|
|
131
|
+
|
|
132
|
+
Raises
|
|
133
|
+
------
|
|
134
|
+
ValueError
|
|
135
|
+
If ``size`` or ``radius`` are non-positive.
|
|
136
|
+
"""
|
|
137
|
+
if size <= 0:
|
|
138
|
+
raise ValueError("size must be a positive integer.")
|
|
139
|
+
|
|
140
|
+
resolved_radius: int = radius if radius is not None else size // 2
|
|
141
|
+
if resolved_radius <= 0:
|
|
142
|
+
raise ValueError("radius must be a positive integer.")
|
|
143
|
+
|
|
144
|
+
center: float = (size - 1) / 2
|
|
145
|
+
x, y = np.ogrid[:size, :size]
|
|
146
|
+
mask_region = (x - center) ** 2 + (y - center) ** 2 > resolved_radius**2
|
|
147
|
+
return 255 * mask_region.astype(np.uint8)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _plot_words(
|
|
151
|
+
ax: Axes,
|
|
152
|
+
words: Sequence[str],
|
|
153
|
+
weights: Sequence[float],
|
|
154
|
+
style: StyleTemplate,
|
|
155
|
+
title: Optional[str],
|
|
156
|
+
random_state: Optional[int],
|
|
157
|
+
mask: Optional[np.ndarray],
|
|
158
|
+
) -> Axes:
|
|
159
|
+
"""Render words on the provided axes with sizes proportional to weights.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
ax : matplotlib.axes.Axes
|
|
164
|
+
Axes on which to draw.
|
|
165
|
+
words : Sequence[str]
|
|
166
|
+
Words to render.
|
|
167
|
+
weights : Sequence[float]
|
|
168
|
+
Corresponding weights for sizing.
|
|
169
|
+
style : StyleTemplate
|
|
170
|
+
Style configuration for the plot.
|
|
171
|
+
title : str, optional
|
|
172
|
+
Title of the plot. Defaults to ``None``.
|
|
173
|
+
random_state : int, optional
|
|
174
|
+
Seed for reproducible placement. Defaults to ``None``.
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
matplotlib.axes.Axes
|
|
179
|
+
Axes containing the rendered word cloud.
|
|
180
|
+
"""
|
|
181
|
+
ax.set_facecolor(style.background_color)
|
|
182
|
+
ax.axis("off")
|
|
183
|
+
|
|
184
|
+
if not words:
|
|
185
|
+
if title:
|
|
186
|
+
ax.set_title(
|
|
187
|
+
title,
|
|
188
|
+
color=style.font_color,
|
|
189
|
+
fontsize=style.font_size * TITLE_SCALE_FACTOR * 0.75,
|
|
190
|
+
)
|
|
191
|
+
return ax
|
|
192
|
+
|
|
193
|
+
fig_raw = ax.figure
|
|
194
|
+
if not isinstance(fig_raw, (Figure, SubFigure)):
|
|
195
|
+
raise RuntimeError("Axes is not associated with a Figure.")
|
|
196
|
+
|
|
197
|
+
fig_obj: Union[Figure, SubFigure] = fig_raw
|
|
198
|
+
canvas: FigureCanvasBase = fig_obj.canvas
|
|
199
|
+
if canvas is None:
|
|
200
|
+
raise RuntimeError("Figure does not have an attached canvas.")
|
|
201
|
+
|
|
202
|
+
canvas.draw()
|
|
203
|
+
ax_bbox: BboxBase = ax.get_window_extent()
|
|
204
|
+
|
|
205
|
+
if mask is None:
|
|
206
|
+
mask_dimension: int = max(int(ax_bbox.width), int(ax_bbox.height), 1)
|
|
207
|
+
resolved_mask: np.ndarray = _create_circular_mask(size=mask_dimension)
|
|
208
|
+
else:
|
|
209
|
+
resolved_mask: np.ndarray = np.asarray(mask, dtype=np.uint8)
|
|
210
|
+
|
|
211
|
+
if resolved_mask.ndim != 2:
|
|
212
|
+
raise ValueError("mask must be a 2D array.")
|
|
213
|
+
|
|
214
|
+
height, width = resolved_mask.shape
|
|
215
|
+
|
|
216
|
+
frequency_map: Dict[str, float] = {
|
|
217
|
+
string_formatter(word): float(weight) for word, weight in zip(words, weights)
|
|
218
|
+
}
|
|
219
|
+
if frequency_map and max(frequency_map.values()) <= 0:
|
|
220
|
+
frequency_map = {word: 1.0 for word in frequency_map}
|
|
221
|
+
|
|
222
|
+
max_font_size = int(style.font_mapping[max(style.font_mapping.keys())] * 20)
|
|
223
|
+
|
|
224
|
+
wc: WordCloud = WordCloud(
|
|
225
|
+
width=width,
|
|
226
|
+
height=height,
|
|
227
|
+
background_color=style.background_color,
|
|
228
|
+
colormap=colormaps.get_cmap(style.palette),
|
|
229
|
+
# min_font_size=min_font_size,
|
|
230
|
+
# max_font_size=max_font_size,
|
|
231
|
+
random_state=random_state,
|
|
232
|
+
mask=resolved_mask,
|
|
233
|
+
).generate_from_frequencies(frequency_map, max_font_size=max_font_size)
|
|
234
|
+
|
|
235
|
+
ax.imshow(wc.to_array(), interpolation="bilinear")
|
|
236
|
+
|
|
237
|
+
if title:
|
|
238
|
+
ax.set_title(
|
|
239
|
+
title,
|
|
240
|
+
color=style.font_color,
|
|
241
|
+
fontsize=style.font_size * TITLE_SCALE_FACTOR * 0.75,
|
|
242
|
+
)
|
|
243
|
+
return ax
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class WordCloudPlot(BasePlot):
|
|
247
|
+
"""Represent a word-cloud plot builder.
|
|
248
|
+
|
|
249
|
+
Methods
|
|
250
|
+
-------
|
|
251
|
+
aplot
|
|
252
|
+
Plot the word cloud on an existing Matplotlib axes.
|
|
253
|
+
fplot
|
|
254
|
+
Plot the word cloud on a new Matplotlib figure.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
def __init__(self, pd_df: pd.DataFrame, text_column: str, weight_column: str):
|
|
258
|
+
validate_dataframe(pd_df, cols=[text_column], sort_by=weight_column)
|
|
259
|
+
super().__init__(pd_df=pd_df)
|
|
260
|
+
self.text_column = text_column
|
|
261
|
+
self.weight_column = weight_column
|
|
262
|
+
|
|
263
|
+
def aplot(
|
|
264
|
+
self,
|
|
265
|
+
title: Optional[str] = None,
|
|
266
|
+
style: StyleTemplate = WORDCLOUD_STYLE_TEMPLATE,
|
|
267
|
+
max_words: int = MAX_RESULTS,
|
|
268
|
+
stopwords: Optional[Iterable[str]] = None,
|
|
269
|
+
random_state: Optional[int] = None,
|
|
270
|
+
ax: Optional[Axes] = None,
|
|
271
|
+
mask: Optional[np.ndarray] = None,
|
|
272
|
+
**kwargs: Any,
|
|
273
|
+
) -> Axes:
|
|
274
|
+
"""Plot the configured word cloud on existing axes.
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
title : str, optional
|
|
279
|
+
Plot title.
|
|
280
|
+
style : StyleTemplate, optional
|
|
281
|
+
Style configuration. The default is ``WORDCLOUD_STYLE_TEMPLATE``.
|
|
282
|
+
max_words : int, optional
|
|
283
|
+
Maximum number of words to include. The default is ``MAX_RESULTS``.
|
|
284
|
+
stopwords : Iterable[str], optional
|
|
285
|
+
Words to exclude from the cloud.
|
|
286
|
+
random_state : int, optional
|
|
287
|
+
Random seed used by word-cloud placement.
|
|
288
|
+
ax : Axes, optional
|
|
289
|
+
Matplotlib axes to draw on. If None, use the current axes.
|
|
290
|
+
mask : np.ndarray, optional
|
|
291
|
+
Binary mask controlling drawable pixels.
|
|
292
|
+
**kwargs : Any
|
|
293
|
+
Additional keyword arguments reserved for compatibility.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
Axes
|
|
298
|
+
Matplotlib axes containing the rendered word cloud.
|
|
299
|
+
"""
|
|
300
|
+
words, weights = _prepare_word_frequencies(
|
|
301
|
+
pd_df=self._obj,
|
|
302
|
+
text_column=self.text_column,
|
|
303
|
+
weight_column=self.weight_column,
|
|
304
|
+
max_words=max_words,
|
|
305
|
+
stopwords=stopwords,
|
|
306
|
+
)
|
|
307
|
+
plot_ax = _get_axis(ax)
|
|
308
|
+
return _plot_words(
|
|
309
|
+
plot_ax,
|
|
310
|
+
words,
|
|
311
|
+
weights,
|
|
312
|
+
style=style,
|
|
313
|
+
title=title,
|
|
314
|
+
random_state=random_state,
|
|
315
|
+
mask=mask,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def fplot(
|
|
319
|
+
self,
|
|
320
|
+
title: Optional[str] = None,
|
|
321
|
+
style: StyleTemplate = WORDCLOUD_STYLE_TEMPLATE,
|
|
322
|
+
max_words: int = MAX_RESULTS,
|
|
323
|
+
stopwords: Optional[Iterable[str]] = None,
|
|
324
|
+
random_state: Optional[int] = None,
|
|
325
|
+
figsize: Tuple[float, float] = FIG_SIZE,
|
|
326
|
+
mask: Optional[np.ndarray] = None,
|
|
327
|
+
) -> Figure:
|
|
328
|
+
"""Plot the configured word cloud on a new figure.
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
title : str, optional
|
|
333
|
+
Plot title.
|
|
334
|
+
style : StyleTemplate, optional
|
|
335
|
+
Style configuration. The default is ``WORDCLOUD_STYLE_TEMPLATE``.
|
|
336
|
+
max_words : int, optional
|
|
337
|
+
Maximum number of words to include. The default is ``MAX_RESULTS``.
|
|
338
|
+
stopwords : Iterable[str], optional
|
|
339
|
+
Words to exclude from the cloud.
|
|
340
|
+
random_state : int, optional
|
|
341
|
+
Random seed used by word-cloud placement.
|
|
342
|
+
figsize : tuple[float, float], optional
|
|
343
|
+
Figure size. The default is ``FIG_SIZE``.
|
|
344
|
+
mask : np.ndarray, optional
|
|
345
|
+
Binary mask controlling drawable pixels.
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
Figure
|
|
350
|
+
Matplotlib figure containing the rendered word cloud.
|
|
351
|
+
"""
|
|
352
|
+
fig = Figure(
|
|
353
|
+
figsize=figsize,
|
|
354
|
+
facecolor=style.background_color,
|
|
355
|
+
edgecolor=style.background_color,
|
|
356
|
+
)
|
|
357
|
+
ax = Axes(fig=fig, facecolor=style.background_color)
|
|
358
|
+
self.aplot(
|
|
359
|
+
title=title,
|
|
360
|
+
style=style,
|
|
361
|
+
max_words=max_words,
|
|
362
|
+
stopwords=stopwords,
|
|
363
|
+
random_state=random_state,
|
|
364
|
+
ax=ax,
|
|
365
|
+
mask=mask,
|
|
366
|
+
)
|
|
367
|
+
return fig
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def aplot_wordcloud(
|
|
371
|
+
pd_df: pd.DataFrame,
|
|
372
|
+
text_column: str,
|
|
373
|
+
weight_column: str,
|
|
374
|
+
title: Optional[str] = None,
|
|
375
|
+
style: StyleTemplate = WORDCLOUD_STYLE_TEMPLATE,
|
|
376
|
+
max_words: int = MAX_RESULTS,
|
|
377
|
+
stopwords: Optional[Iterable[str]] = None,
|
|
378
|
+
random_state: Optional[int] = None,
|
|
379
|
+
ax: Optional[Axes] = None,
|
|
380
|
+
mask: Optional[np.ndarray] = None,
|
|
381
|
+
**kwargs: Any,
|
|
382
|
+
) -> Axes:
|
|
383
|
+
"""Plot a word cloud on the provided axes.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
pd_df : pandas.DataFrame
|
|
388
|
+
DataFrame containing the words to visualize.
|
|
389
|
+
text_column : str
|
|
390
|
+
Column containing words or phrases.
|
|
391
|
+
weight_column : str, optional
|
|
392
|
+
Column containing numeric weights. Defaults to ``None`` for equal weights.
|
|
393
|
+
title : str, optional
|
|
394
|
+
Plot title. Defaults to ``None``.
|
|
395
|
+
style : StyleTemplate, optional
|
|
396
|
+
Styling options. Defaults to ``WORDCLOUD_STYLE_TEMPLATE``.
|
|
397
|
+
max_words : int, optional
|
|
398
|
+
Maximum number of words to display. Defaults to ``MAX_RESULTS``.
|
|
399
|
+
stopwords : Iterable[str], optional
|
|
400
|
+
Words to exclude from the visualization. Defaults to ``None``.
|
|
401
|
+
random_state : int, optional
|
|
402
|
+
Seed for word placement. Defaults to ``None``.
|
|
403
|
+
ax : matplotlib.axes.Axes or numpy.ndarray, optional
|
|
404
|
+
Axes to draw on. Defaults to ``None`` which uses the current axes.
|
|
405
|
+
mask : numpy.ndarray, optional
|
|
406
|
+
Two-dimensional mask array defining the drawable region of the word cloud.
|
|
407
|
+
Defaults to a circular mask generated by :func:`create_circular_mask`.
|
|
408
|
+
|
|
409
|
+
Returns
|
|
410
|
+
-------
|
|
411
|
+
matplotlib.axes.Axes
|
|
412
|
+
Axes containing the rendered word cloud.
|
|
413
|
+
|
|
414
|
+
Raises
|
|
415
|
+
------
|
|
416
|
+
AttributeError
|
|
417
|
+
If required columns are missing from the DataFrame.
|
|
418
|
+
TypeError
|
|
419
|
+
If ``ax`` is not a ``matplotlib.axes.Axes`` instance.
|
|
420
|
+
"""
|
|
421
|
+
return WordCloudPlot(
|
|
422
|
+
pd_df=pd_df,
|
|
423
|
+
text_column=text_column,
|
|
424
|
+
weight_column=weight_column,
|
|
425
|
+
).aplot(
|
|
426
|
+
title=title,
|
|
427
|
+
style=style,
|
|
428
|
+
max_words=max_words,
|
|
429
|
+
stopwords=stopwords,
|
|
430
|
+
random_state=random_state,
|
|
431
|
+
ax=ax,
|
|
432
|
+
mask=mask,
|
|
433
|
+
**kwargs,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def fplot_wordcloud(
|
|
438
|
+
pd_df: pd.DataFrame,
|
|
439
|
+
text_column: str,
|
|
440
|
+
weight_column: str,
|
|
441
|
+
title: Optional[str] = None,
|
|
442
|
+
style: StyleTemplate = WORDCLOUD_STYLE_TEMPLATE,
|
|
443
|
+
max_words: int = MAX_RESULTS,
|
|
444
|
+
stopwords: Optional[Iterable[str]] = None,
|
|
445
|
+
random_state: Optional[int] = None,
|
|
446
|
+
figsize: Tuple[float, float] = FIG_SIZE,
|
|
447
|
+
mask: Optional[np.ndarray] = None,
|
|
448
|
+
) -> Figure:
|
|
449
|
+
"""Return a new figure containing a word cloud.
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
pd_df : pd.DataFrame
|
|
454
|
+
DataFrame containing text and optional weight columns.
|
|
455
|
+
text_column : str
|
|
456
|
+
Column containing text terms.
|
|
457
|
+
weight_column : str, optional
|
|
458
|
+
Column containing numeric weights. The default is None.
|
|
459
|
+
title : str, optional
|
|
460
|
+
Plot title. The default is None.
|
|
461
|
+
style : StyleTemplate, optional
|
|
462
|
+
Style configuration. The default is ``WORDCLOUD_STYLE_TEMPLATE``.
|
|
463
|
+
max_words : int, optional
|
|
464
|
+
Maximum number of words to include. The default is ``MAX_RESULTS``.
|
|
465
|
+
stopwords : Iterable[str], optional
|
|
466
|
+
Words to exclude from the cloud. The default is None.
|
|
467
|
+
random_state : int, optional
|
|
468
|
+
Random seed used by word cloud layout. The default is None.
|
|
469
|
+
figsize : tuple[float, float], optional
|
|
470
|
+
Size of the created figure. The default is ``FIG_SIZE``.
|
|
471
|
+
mask : np.ndarray, optional
|
|
472
|
+
Optional 2D mask limiting word cloud shape. The default is None.
|
|
473
|
+
|
|
474
|
+
Returns
|
|
475
|
+
-------
|
|
476
|
+
Figure
|
|
477
|
+
Matplotlib figure containing the word cloud.
|
|
478
|
+
"""
|
|
479
|
+
return WordCloudPlot(
|
|
480
|
+
pd_df=pd_df, text_column=text_column, weight_column=weight_column
|
|
481
|
+
).fplot(
|
|
482
|
+
title=title,
|
|
483
|
+
style=style,
|
|
484
|
+
max_words=max_words,
|
|
485
|
+
stopwords=stopwords,
|
|
486
|
+
random_state=random_state,
|
|
487
|
+
figsize=figsize,
|
|
488
|
+
mask=mask,
|
|
489
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MatplotLibAPI
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
License-File: LICENSE
|
|
5
5
|
Requires-Python: >=3.8
|
|
6
6
|
Requires-Dist: kaleido
|
|
@@ -21,6 +21,8 @@ Requires-Dist: pytest; extra == 'dev'
|
|
|
21
21
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
22
22
|
Requires-Dist: pytest-pydocstyle; extra == 'dev'
|
|
23
23
|
Requires-Dist: tomli; extra == 'dev'
|
|
24
|
+
Provides-Extra: mcp
|
|
25
|
+
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
25
27
|
|
|
26
28
|
# MatplotLibAPI
|
|
@@ -47,23 +49,98 @@ This will create a `data` directory with CSV files for each plot type.
|
|
|
47
49
|
|
|
48
50
|
## Usage
|
|
49
51
|
|
|
50
|
-
Here's a simple example of how to create a bubble chart using
|
|
52
|
+
Here's a simple example of how to create a bubble chart using the object-based API with a sample CSV file:
|
|
51
53
|
|
|
52
54
|
```python
|
|
53
55
|
import pandas as pd
|
|
54
56
|
import matplotlib.pyplot as plt
|
|
55
|
-
from MatplotLibAPI import
|
|
57
|
+
from MatplotLibAPI.bubble import Bubble
|
|
56
58
|
|
|
57
59
|
# Load the sample data
|
|
58
60
|
df = pd.read_csv('data/bubble.csv')
|
|
59
61
|
|
|
60
|
-
#
|
|
61
|
-
fig =
|
|
62
|
+
# Build and render the bubble chart
|
|
63
|
+
fig = Bubble(
|
|
64
|
+
pd_df=df,
|
|
65
|
+
label='country',
|
|
66
|
+
x='gdp_per_capita',
|
|
67
|
+
y='population',
|
|
68
|
+
z='population',
|
|
69
|
+
).fplot(title='Country Statistics')
|
|
62
70
|
|
|
63
71
|
# Display the plot
|
|
64
72
|
plt.show()
|
|
65
73
|
```
|
|
66
74
|
|
|
75
|
+
|
|
76
|
+
## MCP Integration
|
|
77
|
+
|
|
78
|
+
You can run a dedicated MCP server that exposes MatplotLibAPI plotting tools for LLM agents.
|
|
79
|
+
|
|
80
|
+
1. Install MCP dependencies:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install -e .[mcp]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
2. Start the MCP server over stdio:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
matplotlibapi-mcp
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Tool surface
|
|
93
|
+
|
|
94
|
+
The MCP server provides these tools:
|
|
95
|
+
|
|
96
|
+
- `plot_bubble`: dedicated bubble-chart rendering.
|
|
97
|
+
- `plot_network`: dedicated network-chart rendering.
|
|
98
|
+
- `plot_module`: generic module renderer for `bar`, `histogram`, `box_violin`, `heatmap`, `correlation_matrix`, `area`, `pie`, `waffle`, `sankey`, `table`, `timeserie`, `wordcloud`, `treemap`, and `sunburst`.
|
|
99
|
+
- Explicit module endpoints such as `plot_bar`, `plot_heatmap`, `plot_sankey`, `plot_treemap`, and others for direct LLM tool selection with no module-dispatch step.
|
|
100
|
+
- `describe_plot_modules`: discoverability endpoint that returns supported modules, shared input contract, parameter hints, and dedicated-tool mapping.
|
|
101
|
+
|
|
102
|
+
All rendering tools accept either:
|
|
103
|
+
|
|
104
|
+
- `csv_path`: filesystem path to a CSV file, or
|
|
105
|
+
- `table`: in-memory records (`list[dict]`).
|
|
106
|
+
|
|
107
|
+
All rendering tools return PNG bytes (octet payload) for downstream transport.
|
|
108
|
+
|
|
109
|
+
For LLM orchestration, explicit endpoints are generally easier to select and ground (for example `plot_heatmap` rather than `plot_module` + `plot_module="heatmap"`). Keep `plot_module` for dynamic clients that need one generic surface.
|
|
110
|
+
|
|
111
|
+
### Discoverability-first workflow
|
|
112
|
+
|
|
113
|
+
For dynamic clients and agent exploration, use this sequence:
|
|
114
|
+
|
|
115
|
+
1. Call `describe_plot_modules`.
|
|
116
|
+
2. Select a module from `supported_plot_modules`.
|
|
117
|
+
3. Read recommended keys from `parameter_hints[module_name]`.
|
|
118
|
+
4. Call `plot_module` with `params` + `csv_path` or `table`.
|
|
119
|
+
|
|
120
|
+
If a module key is invalid, `plot_module` returns a clear error with supported values.
|
|
121
|
+
|
|
122
|
+
### Example generic call
|
|
123
|
+
|
|
124
|
+
Example payload for `plot_module` with in-memory table records:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"plot_module": "heatmap",
|
|
129
|
+
"table": [
|
|
130
|
+
{"month": "Jan", "channel": "Email", "engagements": 120},
|
|
131
|
+
{"month": "Jan", "channel": "Social", "engagements": 200}
|
|
132
|
+
],
|
|
133
|
+
"params": {
|
|
134
|
+
"x": "month",
|
|
135
|
+
"y": "channel",
|
|
136
|
+
"value": "engagements",
|
|
137
|
+
"title": "Engagement Heatmap"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The package exposes a single MCP entry point: `matplotlibapi-mcp`.
|
|
143
|
+
|
|
67
144
|
## Plot Types
|
|
68
145
|
|
|
69
146
|
The library supports the following plot types:
|
|
@@ -92,10 +169,16 @@ This repository includes a `data` directory with sample CSV files for each plot
|
|
|
92
169
|
|
|
93
170
|
```python
|
|
94
171
|
import pandas as pd
|
|
95
|
-
from MatplotLibAPI import
|
|
172
|
+
from MatplotLibAPI.bubble import Bubble
|
|
96
173
|
|
|
97
174
|
df = pd.read_csv('data/bubble.csv')
|
|
98
|
-
fig =
|
|
175
|
+
fig = Bubble(
|
|
176
|
+
pd_df=df,
|
|
177
|
+
label='country',
|
|
178
|
+
x='gdp_per_capita',
|
|
179
|
+
y='life_expectancy',
|
|
180
|
+
z='population',
|
|
181
|
+
).fplot()
|
|
99
182
|
fig.show()
|
|
100
183
|
```
|
|
101
184
|
|
|
@@ -103,10 +186,16 @@ fig.show()
|
|
|
103
186
|
|
|
104
187
|
```python
|
|
105
188
|
import pandas as pd
|
|
106
|
-
from MatplotLibAPI import
|
|
189
|
+
from MatplotLibAPI.network import NetworkGraph
|
|
107
190
|
|
|
108
191
|
df = pd.read_csv('data/network.csv')
|
|
109
|
-
|
|
192
|
+
graph = NetworkGraph.from_pandas_edgelist(
|
|
193
|
+
df,
|
|
194
|
+
source='city_a',
|
|
195
|
+
target='city_b',
|
|
196
|
+
edge_weight_col='distance_km',
|
|
197
|
+
)
|
|
198
|
+
fig = graph.fplot(title='City Network', edge_weight_col='distance_km')
|
|
110
199
|
fig.show()
|
|
111
200
|
```
|
|
112
201
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
MatplotLibAPI/__init__.py,sha256=RRT8uq3WQT16lUVs4DgwmL3pCeK6Jo6nOu8LIZFjMH4,200
|
|
2
|
+
MatplotLibAPI/accessor.py,sha256=XfPQrDqYBmNiGRHwD8kPZ0m-O8qU4FWZRT0OHsMFhzY,64092
|
|
3
|
+
MatplotLibAPI/area.py,sha256=DZXmn8vZIaRiPlZq0CIhwXtBQ8vgyNBq1bqEern4Wqo,4714
|
|
4
|
+
MatplotLibAPI/bar.py,sha256=zh5mcCP9FYjzi9WDlO9D6x9tuYpHN195j9Pf6_jSjrA,5107
|
|
5
|
+
MatplotLibAPI/base_plot.py,sha256=ubbjfk5cRr7YvlEzZ9wiZ_V_KsbUV_S5pmirHxD9fR8,2621
|
|
6
|
+
MatplotLibAPI/box_violin.py,sha256=xsa-hNV0hp2zjPg4_bLjBp0lMOezBxTTOtvmooJF65M,4849
|
|
7
|
+
MatplotLibAPI/bubble.py,sha256=-BLEclKxVpagW3dk8qwEruemehJmy-kALttatgcMILI,17035
|
|
8
|
+
MatplotLibAPI/composite.py,sha256=vhPSWpXDzVKcy5BZAgyvtKOKEukepgtZzDAU473Tv1E,9531
|
|
9
|
+
MatplotLibAPI/heatmap.py,sha256=pXmB5GzU8Gg8JRoIoMc3ZzSej80GgYO3m7j8PKZV7JQ,5855
|
|
10
|
+
MatplotLibAPI/histogram.py,sha256=k8MpTR76c8FpKMyNOsq3oVHIj2Wwj73hSpeXQmJonik,4743
|
|
11
|
+
MatplotLibAPI/mcp_server.py,sha256=hjyW7PipY78rLb5uBU5s9tf3bYRM4GBcR91MzMSX794,19192
|
|
12
|
+
MatplotLibAPI/pie.py,sha256=VGgTJoPu42JYqXlvI9WC4QB7IEXmHDj3BXDPjRzqyGc,4605
|
|
13
|
+
MatplotLibAPI/pivot.py,sha256=rk5wQYxPzSb6gkvtuTmDc9ZpaNThoeSl03BCQ4azOWw,7098
|
|
14
|
+
MatplotLibAPI/sankey.py,sha256=OtQWzzQP9iPJAWylFnuKnmNFEkjHnzw7zyMQBUl9RIM,3022
|
|
15
|
+
MatplotLibAPI/style_template.py,sha256=AFihe6twpMLYc_4-o0mhEvVMitTRtdAayZB-ECzUSsA,8498
|
|
16
|
+
MatplotLibAPI/sunburst.py,sha256=9UPzov1H9hUHQZ1DtH9pVMjpbeSnnIdRVPpr7majgt8,3767
|
|
17
|
+
MatplotLibAPI/table.py,sha256=LAzsVPZ4cpbqqaQ-mBAI-2X715hkFA9bhV6LbRaNBXw,7086
|
|
18
|
+
MatplotLibAPI/timeserie.py,sha256=iE_MIDwQ19dWgxoxa0kd27DdfBe5__XsxrDp5qk1W-k,11899
|
|
19
|
+
MatplotLibAPI/treemap.py,sha256=jmaBMlMbP9DY7ox49q1ispuK16Fz0lBNvuyZOrrwcFk,4903
|
|
20
|
+
MatplotLibAPI/typing.py,sha256=zZSz7cpqPSEIrryUEW5C1_QFjEjBeeAVjBu5jktskAk,441
|
|
21
|
+
MatplotLibAPI/utils.py,sha256=SZvDGsm1pt9rp1JxeXkQ4wUmOj6b3b04U2KiBmFwwJ4,1792
|
|
22
|
+
MatplotLibAPI/waffle.py,sha256=KXnJ5A_4_9bICt1MAj9ExwlmB9EV_W34HRuXFAV99W8,5247
|
|
23
|
+
MatplotLibAPI/word_cloud.py,sha256=qMBLFL1E5vGsclaz5ty-CxaD60aOQwmvaQiDkuroHwQ,15420
|
|
24
|
+
MatplotLibAPI/mcp/__init__.py,sha256=KzF8wOB2dQrAs7i1YMqQWyLnpu0vO8OS7HGyFIGkyoA,434
|
|
25
|
+
MatplotLibAPI/mcp/metadata.py,sha256=G0SJSG4XX5fZQy5F2Ii3FVOM1uprFcBvbGpK7BxMDrg,2880
|
|
26
|
+
MatplotLibAPI/mcp/renderers.py,sha256=mB7B9ytr1ahxkK8MSIFQKtQ3nz5jYmX2JgKhQUe0W4k,1293
|
|
27
|
+
MatplotLibAPI/network/__init__.py,sha256=jyef46y8-3u7hVhUHfFVEvYx65jSptyIbuJP6VE_f2Y,728
|
|
28
|
+
MatplotLibAPI/network/constants.py,sha256=ugU0w1del8XFa-fzHaiKIGXQVhVj4TUyeYEtyDc68JM,466
|
|
29
|
+
MatplotLibAPI/network/core.py,sha256=6h5eR6fwkIbO_GjDjO_Y4fBwbnvTR51jEn5d0-7qjxU,45759
|
|
30
|
+
MatplotLibAPI/network/plot.py,sha256=bktXpQuJRriqhCzELYS9HU1NlsM99nqpSOgww6WRVTk,20435
|
|
31
|
+
MatplotLibAPI/network/scaling.py,sha256=gdbusWHN-yKYWJjSZ0viqM5S31tZfLH8hojoc2fML2c,1632
|
|
32
|
+
matplotlibapi-4.0.0.dist-info/METADATA,sha256=EVMDbThu5oKgPI3iuPiqSx3H_wP_Yw9PGBgGoFbWG4E,8451
|
|
33
|
+
matplotlibapi-4.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
34
|
+
matplotlibapi-4.0.0.dist-info/entry_points.txt,sha256=x_X45-YKZXmwaEiyYqQiPVHFJ5Rj66qwNesDDq3-rWg,68
|
|
35
|
+
matplotlibapi-4.0.0.dist-info/licenses/LICENSE,sha256=hMErKLb6YZR3lRR5zr-vxeFkvY69QAaafgSpZ5-P1dQ,1067
|
|
36
|
+
matplotlibapi-4.0.0.dist-info/RECORD,,
|