MatplotLibAPI 4.0.3__tar.gz → 4.0.4__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 (71) hide show
  1. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/PKG-INFO +1 -1
  2. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/pyproject.toml +1 -1
  3. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/accessor.py +51 -0
  4. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/network/core.py +27 -0
  5. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/network/plot.py +2 -0
  6. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_network.py +60 -0
  7. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/.github/dependabot.yml +0 -0
  8. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/.github/workflows/ci.yml +0 -0
  9. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/.github/workflows/python-publish.yml +0 -0
  10. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/.gitignore +0 -0
  11. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/.pre-commit-config.yaml +0 -0
  12. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/AGENTS.md +0 -0
  13. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/LICENSE +0 -0
  14. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/README.md +0 -0
  15. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/SECURITY.md +0 -0
  16. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/SUGGESTIONS.md +0 -0
  17. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/examples/__init__.py +0 -0
  18. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/examples/bubble.py +0 -0
  19. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/examples/network.py +0 -0
  20. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/examples/sample_data.py +0 -0
  21. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/scripts/pre_commit.sh +0 -0
  22. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/__init__.py +0 -0
  23. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/area.py +0 -0
  24. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/bar.py +0 -0
  25. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/base_plot.py +0 -0
  26. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/box_violin.py +0 -0
  27. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/bubble.py +0 -0
  28. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/composite.py +0 -0
  29. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/heatmap.py +0 -0
  30. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/histogram.py +0 -0
  31. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/mcp/__init__.py +0 -0
  32. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/mcp/metadata.py +0 -0
  33. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/mcp/renderers.py +0 -0
  34. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/mcp_server.py +0 -0
  35. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/network/__init__.py +0 -0
  36. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/network/constants.py +0 -0
  37. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/network/scaling.py +0 -0
  38. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/pie.py +0 -0
  39. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/pivot.py +0 -0
  40. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/sankey.py +0 -0
  41. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/style_template.py +0 -0
  42. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/sunburst.py +0 -0
  43. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/table.py +0 -0
  44. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/timeserie.py +0 -0
  45. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/treemap.py +0 -0
  46. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/types.py +0 -0
  47. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/waffle.py +0 -0
  48. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/src/MatplotLibAPI/word_cloud.py +0 -0
  49. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/__init__.py +0 -0
  50. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/conftest.py +0 -0
  51. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_area.py +0 -0
  52. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_bar.py +0 -0
  53. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_box_violin.py +0 -0
  54. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_bubble.py +0 -0
  55. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_composite.py +0 -0
  56. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_dependencies.py +0 -0
  57. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_heatmap.py +0 -0
  58. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_histogram.py +0 -0
  59. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_mcp_server.py +0 -0
  60. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_network_preprocessing.py +0 -0
  61. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_pie.py +0 -0
  62. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_pivot.py +0 -0
  63. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_sankey.py +0 -0
  64. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_smoke.py +0 -0
  65. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_style_template.py +0 -0
  66. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_sunburst.py +0 -0
  67. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_table.py +0 -0
  68. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_timeseries.py +0 -0
  69. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_treemap.py +0 -0
  70. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_waffle.py +0 -0
  71. {matplotlibapi-4.0.3 → matplotlibapi-4.0.4}/tests/test_wordcloud.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MatplotLibAPI
3
- Version: 4.0.3
3
+ Version: 4.0.4
4
4
  License-File: LICENSE
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: kaleido
@@ -3,7 +3,7 @@ requires = ["hatchling"]
3
3
  build-backend = "hatchling.build"
4
4
  [project]
5
5
  name = "MatplotLibAPI"
6
- version = "4.0.3"
6
+ version = "4.0.4"
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.9"
9
9
  dependencies = [
@@ -1278,6 +1278,57 @@ class DataFrameAccessor:
1278
1278
  **kwargs,
1279
1279
  )
1280
1280
 
1281
+ def fplot_network(
1282
+ self,
1283
+ edge_source_col: str = "source",
1284
+ edge_target_col: str = "target",
1285
+ edge_weight_col: str = "weight",
1286
+ title: Optional[str] = None,
1287
+ style: Optional[StyleTemplate] = None,
1288
+ layout_seed: Optional[int] = None,
1289
+ figsize: Tuple[float, float] = FIG_SIZE,
1290
+ ) -> Figure:
1291
+ """Plot a network graph on a new figure.
1292
+
1293
+ Parameters
1294
+ ----------
1295
+ edge_source_col : str, optional
1296
+ Column for source nodes. The default is "source".
1297
+ edge_target_col : str, optional
1298
+ Column for target nodes. The default is "target".
1299
+ edge_weight_col : str, optional
1300
+ Column for edge weights. The default is "weight".
1301
+ title : str, optional
1302
+ Chart title.
1303
+ style : StyleTemplate, optional
1304
+ Styling template. The default is `NETWORK_STYLE_TEMPLATE`.
1305
+ layout_seed : int, optional
1306
+ Seed forwarded to the spring layout. The default is ``_DEFAULT["SPRING_LAYOUT_SEED"]``.
1307
+ figsize : tuple[float, float], optional
1308
+ Figure size. The default is FIG_SIZE.
1309
+
1310
+ Returns
1311
+ -------
1312
+ Figure
1313
+ The new Matplotlib figure with the plot.
1314
+ """
1315
+ kwargs: Dict[str, Any] = {}
1316
+ if layout_seed is not None:
1317
+ kwargs["layout_seed"] = layout_seed
1318
+
1319
+ from .network import NETWORK_STYLE_TEMPLATE, fplot_network
1320
+
1321
+ return fplot_network(
1322
+ pd_df=self._obj,
1323
+ edge_source_col=edge_source_col,
1324
+ edge_target_col=edge_target_col,
1325
+ edge_weight_col=edge_weight_col,
1326
+ title=title,
1327
+ style=style or NETWORK_STYLE_TEMPLATE,
1328
+ figsize=figsize,
1329
+ **kwargs,
1330
+ )
1331
+
1281
1332
  def aplot_network_components(
1282
1333
  self,
1283
1334
  edge_source_col: str = "source",
@@ -453,6 +453,33 @@ class NetworkGraph(BasePlot):
453
453
  """Return the degree assortativity coefficient of the graph."""
454
454
  return nx.degree_assortativity_coefficient(self._nx_graph)
455
455
 
456
+ @property
457
+ def degree_distribution(self) -> Dict[int, int]:
458
+ """Return the count of nodes for each degree.
459
+
460
+ Returns
461
+ -------
462
+ dict[int, int]
463
+ Mapping from node degree to number of nodes with that degree.
464
+ """
465
+ distribution: Dict[int, int] = defaultdict(int)
466
+ for _, degree in self._nx_graph.degree():
467
+ distribution[int(degree)] += 1
468
+ return dict(sorted(distribution.items()))
469
+
470
+ @property
471
+ def degree_sequence(self) -> List[int]:
472
+ """Return node degrees sorted in descending order.
473
+
474
+ Returns
475
+ -------
476
+ list[int]
477
+ Degree for each node sorted from highest to lowest.
478
+ """
479
+ return sorted(
480
+ (int(degree) for _, degree in self._nx_graph.degree()), reverse=True
481
+ )
482
+
456
483
  def add_node(self, node: Any, **attributes: Any):
457
484
  """Add a node with optional attributes.
458
485
 
@@ -501,6 +501,8 @@ def fplot_network(
501
501
  ).fplot(
502
502
  title=title,
503
503
  style=style,
504
+ edge_weight_col=edge_weight_col,
505
+ layout_seed=layout_seed,
504
506
  figsize=figsize,
505
507
  )
506
508
 
@@ -83,6 +83,44 @@ def test_accessor_fplot_network_components(load_sample_df):
83
83
  assert isinstance(fig, Figure)
84
84
 
85
85
 
86
+ def test_accessor_fplot_network(load_sample_df):
87
+ """Render a network figure via the pandas accessor."""
88
+
89
+ df = load_sample_df("network.csv")
90
+
91
+ fig = df.mpl.fplot_network(
92
+ edge_source_col="city_a",
93
+ edge_target_col="city_b",
94
+ edge_weight_col="distance_km",
95
+ )
96
+
97
+ assert isinstance(fig, Figure)
98
+
99
+
100
+ def test_accessor_fplot_network_forwards_layout_seed(monkeypatch):
101
+ """Forward ``layout_seed`` from accessor calls to network plotting."""
102
+
103
+ df = pd.DataFrame(
104
+ {
105
+ "source": ["a", "b", "c"],
106
+ "target": ["b", "c", "a"],
107
+ "weight": [1, 2, 3],
108
+ }
109
+ )
110
+ captured_layout_seed = []
111
+
112
+ def fake_aplot(self, *args, **kwargs): # type: ignore[override]
113
+ captured_layout_seed.append(kwargs.get("layout_seed"))
114
+ return plt.gca()
115
+
116
+ monkeypatch.setattr(NetworkGraph, "aplot", fake_aplot)
117
+
118
+ fig = df.mpl.fplot_network(layout_seed=123)
119
+
120
+ assert isinstance(fig, Figure)
121
+ assert captured_layout_seed == [123]
122
+
123
+
86
124
  def test_softmax_matches_expected_probabilities():
87
125
  """Return softmax probabilities consistent with NumPy operations."""
88
126
 
@@ -152,6 +190,28 @@ def test_scale_weights_respects_precomputed_deciles():
152
190
  assert _scale_weights(weights, deciles=deciles) == expected
153
191
 
154
192
 
193
+ def test_degree_distribution_returns_node_counts_by_degree():
194
+ """Return the count of nodes grouped by their degree."""
195
+
196
+ graph = NetworkGraph()
197
+ graph.add_edge("a", "b")
198
+ graph.add_edge("b", "c")
199
+ graph.add_node("isolated")
200
+
201
+ assert graph.degree_distribution == {0: 1, 1: 2, 2: 1}
202
+
203
+
204
+ def test_degree_sequence_returns_descending_node_degrees():
205
+ """Return node degrees sorted from highest to lowest."""
206
+
207
+ graph = NetworkGraph()
208
+ graph.add_edge("a", "b")
209
+ graph.add_edge("b", "c")
210
+ graph.add_node("isolated")
211
+
212
+ assert graph.degree_sequence == [2, 1, 1, 0]
213
+
214
+
155
215
  def test_network_layout_respects_precomputed_deciles() -> None:
156
216
  """Reuse provided deciles to produce stable layout scaling."""
157
217
 
File without changes
File without changes
File without changes
File without changes
File without changes