hiveplotlib 0.26.2__tar.gz → 0.27.0__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 (40) hide show
  1. {hiveplotlib-0.26.2/src/hiveplotlib.egg-info → hiveplotlib-0.27.0}/PKG-INFO +13 -6
  2. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/pyproject.toml +29 -8
  3. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/__init__.py +4 -0
  4. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/axis.py +4 -1
  5. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/converters.py +18 -15
  6. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/international_trade.py +1 -1
  7. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/toy_hive_plots.py +304 -6
  8. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/toy_p2cps.py +2 -2
  9. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/edges.py +48 -21
  10. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/hiveplot.py +286 -75
  11. hiveplotlib-0.27.0/src/hiveplotlib/hiveplot_matrix.py +1274 -0
  12. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/node.py +26 -13
  13. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/p2cp.py +20 -17
  14. hiveplotlib-0.27.0/src/hiveplotlib/utils.py +316 -0
  15. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/base.py +16 -5
  16. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/bokeh.py +15 -15
  17. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/datashader.py +106 -49
  18. hiveplotlib-0.27.0/src/hiveplotlib/viz/hiveplot_matrix.py +657 -0
  19. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/holoviews.py +12 -12
  20. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/matplotlib.py +8 -7
  21. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/plotly.py +37 -22
  22. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0/src/hiveplotlib.egg-info}/PKG-INFO +13 -6
  23. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/SOURCES.txt +2 -0
  24. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/requires.txt +14 -5
  25. hiveplotlib-0.26.2/src/hiveplotlib/utils.py +0 -96
  26. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/LICENSE +0 -0
  27. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/MANIFEST.in +0 -0
  28. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/README.md +0 -0
  29. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/setup.cfg +0 -0
  30. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/setup.py +0 -0
  31. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/__init__.py +0 -0
  32. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_2019_8112.csv +0 -0
  33. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_metadata_2019_8112.json +0 -0
  34. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/__init__.py +0 -0
  35. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/hive_plot.py +0 -0
  36. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/node.py +0 -0
  37. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/__init__.py +0 -0
  38. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/input_checks.py +0 -0
  39. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/dependency_links.txt +0 -0
  40. {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hiveplotlib
3
- Version: 0.26.2
3
+ Version: 0.27.0
4
4
  Summary: Visualize Network Data with Hive Plots
5
5
  Author-email: Gary Koplik <gary.koplik@geomdata.com>
6
6
  License: Copyright (c) 2020 - 2024, Geometric Data Analytics, Inc.
@@ -41,20 +41,20 @@ Requires-Dist: matplotlib
41
41
  Requires-Dist: numpy
42
42
  Requires-Dist: pandas
43
43
  Provides-Extra: bokeh
44
- Requires-Dist: bokeh; extra == "bokeh"
44
+ Requires-Dist: bokeh>=3.0.0; extra == "bokeh"
45
45
  Provides-Extra: datashader
46
46
  Requires-Dist: dask[dataframe]; extra == "datashader"
47
47
  Requires-Dist: datashader; extra == "datashader"
48
48
  Requires-Dist: numba>0.54.0; extra == "datashader"
49
49
  Requires-Dist: seaborn; extra == "datashader"
50
50
  Provides-Extra: dev
51
- Requires-Dist: bokeh; extra == "dev"
51
+ Requires-Dist: bokeh>=3.0.0; extra == "dev"
52
52
  Requires-Dist: cartopy; extra == "dev"
53
53
  Requires-Dist: dask[dataframe]; extra == "dev"
54
54
  Requires-Dist: datashader; extra == "dev"
55
55
  Requires-Dist: flexitext; extra == "dev"
56
56
  Requires-Dist: geopandas; extra == "dev"
57
- Requires-Dist: holoviews; extra == "dev"
57
+ Requires-Dist: holoviews>=1.15; extra == "dev"
58
58
  Requires-Dist: jupyter; extra == "dev"
59
59
  Requires-Dist: networkx; extra == "dev"
60
60
  Requires-Dist: numba>0.54.0; extra == "dev"
@@ -63,6 +63,7 @@ Requires-Dist: pybadges; extra == "dev"
63
63
  Requires-Dist: seaborn; extra == "dev"
64
64
  Requires-Dist: tqdm; extra == "dev"
65
65
  Provides-Extra: docs
66
+ Requires-Dist: ipywidgets; extra == "docs"
66
67
  Requires-Dist: myst-parser; extra == "docs"
67
68
  Requires-Dist: nbsphinx; extra == "docs"
68
69
  Requires-Dist: pydata-sphinx-theme; extra == "docs"
@@ -70,11 +71,15 @@ Requires-Dist: sphinx; extra == "docs"
70
71
  Requires-Dist: sphinx-copybutton; extra == "docs"
71
72
  Requires-Dist: toml; extra == "docs"
72
73
  Provides-Extra: holoviews
73
- Requires-Dist: bokeh; extra == "holoviews"
74
- Requires-Dist: holoviews; extra == "holoviews"
74
+ Requires-Dist: bokeh>=3.0.0; extra == "holoviews"
75
+ Requires-Dist: holoviews>=1.15; extra == "holoviews"
75
76
  Requires-Dist: numba>0.54.0; extra == "holoviews"
76
77
  Provides-Extra: networkx
77
78
  Requires-Dist: networkx; extra == "networkx"
79
+ Provides-Extra: perf
80
+ Requires-Dist: pyinstrument; extra == "perf"
81
+ Requires-Dist: snakeviz; extra == "perf"
82
+ Requires-Dist: memory_profiler; extra == "perf"
78
83
  Provides-Extra: plotly
79
84
  Requires-Dist: plotly; extra == "plotly"
80
85
  Provides-Extra: ruff
@@ -86,6 +91,8 @@ Requires-Dist: pytest-html; extra == "testing"
86
91
  Requires-Dist: pytest-timeout; extra == "testing"
87
92
  Requires-Dist: pytest-xdist; extra == "testing"
88
93
  Requires-Dist: toml; extra == "testing"
94
+ Provides-Extra: ty
95
+ Requires-Dist: ty==0.0.20; extra == "ty"
89
96
  Dynamic: license-file
90
97
 
91
98
  ![Hiveplotlib Logo](https://hiveplotlib.readthedocs.io/stable/_static/hiveplotlib.svg)
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
  [project]
8
8
  name = "hiveplotlib"
9
9
  requires-python = ">=3.10"
10
- version = "0.26.2"
10
+ version = "0.27.0"
11
11
  authors = [
12
12
  {name="Gary Koplik", email="gary.koplik@geomdata.com"}
13
13
  ]
@@ -20,13 +20,13 @@ urls = {Documentation="https://hiveplotlib.readthedocs.io/stable/", Source="http
20
20
  dependencies = [
21
21
  "matplotlib",
22
22
  "numpy",
23
- "pandas"
23
+ "pandas",
24
24
  ]
25
25
 
26
26
  [project.optional-dependencies]
27
27
 
28
28
  bokeh = [
29
- "bokeh"
29
+ "bokeh>=3.0.0"
30
30
  ]
31
31
 
32
32
  datashader = [
@@ -38,13 +38,13 @@ datashader = [
38
38
 
39
39
  # developer dependencies
40
40
  dev = [
41
- "bokeh",
41
+ "bokeh>=3.0.0",
42
42
  "cartopy",
43
43
  "dask[dataframe]", # https://github.com/holoviz/datashader/issues/1319
44
44
  "datashader",
45
45
  "flexitext",
46
46
  "geopandas",
47
- "holoviews",
47
+ "holoviews>=1.15",
48
48
  "jupyter",
49
49
  "networkx",
50
50
  "numba>0.54.0", # https://github.com/numba/numba/issues/9708
@@ -56,6 +56,7 @@ dev = [
56
56
 
57
57
  # for building documentation
58
58
  docs = [
59
+ "ipywidgets", # so nbsphinx can handle notebooks with widget metadata (e.g. tqdm progress bars)
59
60
  "myst-parser",
60
61
  "nbsphinx",
61
62
  "pydata-sphinx-theme",
@@ -65,8 +66,8 @@ docs = [
65
66
  ]
66
67
 
67
68
  holoviews = [
68
- "bokeh",
69
- "holoviews",
69
+ "bokeh>=3.0.0",
70
+ "holoviews>=1.15",
70
71
  "numba>0.54.0", # https://github.com/numba/numba/issues/9708
71
72
  ]
72
73
 
@@ -74,13 +75,20 @@ networkx = [
74
75
  "networkx"
75
76
  ]
76
77
 
78
+ # performance profiling extras
79
+ perf = [
80
+ "pyinstrument",
81
+ "snakeviz",
82
+ "memory_profiler",
83
+ ]
84
+
77
85
  plotly = [
78
86
  "plotly"
79
87
  ]
80
88
 
81
89
  # ruff code hygeine checks
82
90
  ruff = [
83
- "ruff"
91
+ "ruff",
84
92
  ]
85
93
 
86
94
  testing = [
@@ -92,10 +100,23 @@ testing = [
92
100
  "toml",
93
101
  ]
94
102
 
103
+ # ty code hygeine checks
104
+ ty = [
105
+ "ty==0.0.20", # pinning for now, 0.0.21 would require non trivial type hint changes that affect the user
106
+ ]
107
+
95
108
  [tool.setuptools.packages.find]
96
109
  where = ["src"]
97
110
  include = ["hiveplotlib*"]
98
111
 
112
+ [tool.ty.src]
113
+ exclude = [
114
+ "docs",
115
+ "examples",
116
+ "runners",
117
+ "sandbox",
118
+ ]
119
+
99
120
  [tool.ruff]
100
121
  # ruff performs our linting and formatting.
101
122
  # if you receive an error you don't understand, you can run `ruff rule <error code>` to learn more.
@@ -12,5 +12,9 @@ from hiveplotlib.hiveplot import ( # noqa: F401
12
12
  HivePlot,
13
13
  hive_plot_n_axes,
14
14
  )
15
+ from hiveplotlib.hiveplot_matrix import ( # noqa: F401
16
+ HPM_SUPPORTED_VIZ_BACKENDS,
17
+ HivePlotMatrix,
18
+ )
15
19
  from hiveplotlib.node import Node, NodeCollection # noqa: F401
16
20
  from hiveplotlib.p2cp import P2CP, p2cp_n_axes # noqa: F401
@@ -6,6 +6,7 @@ Definition of ``Axis`` instance and helper static methods for generating and wor
6
6
 
7
7
  from typing import Hashable, Optional
8
8
 
9
+ import numpy as np
9
10
  import pandas as pd
10
11
 
11
12
  from hiveplotlib.utils import polar2cartesian
@@ -93,7 +94,9 @@ class Axis:
93
94
  self.sorting_variable = None
94
95
 
95
96
  # hold all the cartesian coordinates, polar rho, corresponding labels, and node metadata in a pandas dataframe
96
- self.node_placements = pd.DataFrame(columns=["x", "y", "unique_id", "rho"])
97
+ self.node_placements = pd.DataFrame(
98
+ columns=np.array(["x", "y", "unique_id", "rho"])
99
+ )
97
100
 
98
101
  # hold the current vmin and vmax used to place nodes on an axis in a ``HivePlot`` instance.
99
102
  self.vmin = None
@@ -4,22 +4,22 @@
4
4
  Converters from various data structures to ``hiveplotlib``-ready structures.
5
5
  """
6
6
 
7
- from typing import Tuple
8
-
9
7
  import pandas as pd
10
8
 
11
- from hiveplotlib import Edges, Node, NodeCollection
12
- from hiveplotlib.node import node_collection_from_node_list
9
+ from hiveplotlib import Edges, NodeCollection
13
10
 
14
- # say the graph type instead of calling `nx.Graph` to keep networkx dep out of this file.
15
- NetworkXGraph = "networkx.classes.graph.Graph instance"
11
+ try:
12
+ import networkx as nx
13
+ except ImportError as ie: # pragma: no cover
14
+ msg = "`networkx` dependencies not installed, but can be installed by running `pip install hiveplotlib[networkx]`"
15
+ raise ImportError(msg) from ie
16
16
 
17
17
 
18
18
  def networkx_to_nodes_edges(
19
- graph: NetworkXGraph,
19
+ graph: nx.Graph,
20
20
  unique_id_name: str = "unique_id",
21
21
  check_uniqueness: bool = True,
22
- ) -> Tuple[NodeCollection, Edges]: # type: ignore
22
+ ) -> tuple[NodeCollection, Edges]:
23
23
  """
24
24
  Take a ``networkx`` graph and return ``hiveplotlib``-friendly data structures.
25
25
 
@@ -31,11 +31,14 @@ def networkx_to_nodes_edges(
31
31
  :param check_uniqueness: whether or not to check that provided ``Node`` instances have unique IDs.
32
32
  :return: ``list`` of ``Node`` instances, ``(n, 2)`` ``np.ndarray`` of edges.
33
33
  """
34
- nodes = [Node(unique_id=i, data=data) for i, data in list(graph.nodes.data())]
35
- edge_keys = pd.DataFrame(list(graph.edges.keys()), columns=["from", "to"])
36
- edge_values = pd.DataFrame(list(graph.edges.values()))
37
- return node_collection_from_node_list(
38
- node_list=nodes,
39
- unique_id_name=unique_id_name,
34
+ node_data = []
35
+ for node, attributes in graph.nodes(data=True):
36
+ node_data.append({unique_id_name: node, **attributes})
37
+
38
+ node_df = pd.DataFrame(node_data)
39
+ edge_df = nx.to_pandas_edgelist(graph, source="from", target="to")
40
+ return NodeCollection(
41
+ data=node_df,
42
+ unique_id_column=unique_id_name,
40
43
  check_uniqueness=check_uniqueness,
41
- ), Edges(data=edge_keys.join(edge_values))
44
+ ), Edges(data=edge_df)
@@ -52,7 +52,7 @@ def international_trade_data(
52
52
  csv_years = [i.split("_")[2] for i in csv_files]
53
53
  csv_hs92 = [i.split("_")[-1] for i in csv_files]
54
54
  hiveplotlib_supported_values = pd.DataFrame(
55
- np.c_[csv_years, csv_hs92], columns=["Year", "Trade Code"]
55
+ np.c_[csv_years, csv_hs92], columns=np.array(["Year", "Trade Code"])
56
56
  )
57
57
 
58
58
  # check that our implied data and metadata files exist
@@ -13,6 +13,7 @@ from hiveplotlib import (
13
13
  NodeCollection,
14
14
  hive_plot_n_axes,
15
15
  )
16
+ from hiveplotlib.datasets.international_trade import international_trade_data
16
17
  from hiveplotlib.edges import Edges
17
18
  from hiveplotlib.hiveplot import HivePlot
18
19
  from hiveplotlib.node import dataframe_to_node_list
@@ -52,7 +53,7 @@ def example_base_hive_plot(
52
53
  rng.uniform(low=10, high=20, size=num_nodes),
53
54
  rng.uniform(low=20, high=30, size=num_nodes),
54
55
  ],
55
- columns=["low", "med", "high"],
56
+ columns=np.array(["low", "med", "high"]),
56
57
  )
57
58
 
58
59
  # make indices a column
@@ -106,7 +107,7 @@ def example_node_data(num_nodes: int = 100, seed: int = 0) -> pd.DataFrame:
106
107
  rng.uniform(low=10, high=19.99, size=num_nodes),
107
108
  rng.uniform(low=20, high=29.99, size=num_nodes),
108
109
  ],
109
- columns=["low", "med", "high"],
110
+ columns=np.array(["low", "med", "high"]),
110
111
  )
111
112
  # make indices a column
112
113
  return data.reset_index(names="unique_id")
@@ -162,7 +163,9 @@ def example_edge_data(
162
163
  node_ids = nodes.data[nodes.unique_id_column].to_numpy()
163
164
  rng = np.random.default_rng(seed)
164
165
  edge_array = rng.choice(node_ids, size=num_edges * 2).reshape(-1, 2)
165
- return pd.DataFrame(edge_array, columns=[from_column_name, to_column_name])
166
+ return pd.DataFrame(
167
+ edge_array, columns=np.array([from_column_name, to_column_name])
168
+ )
166
169
 
167
170
 
168
171
  def example_edges(
@@ -199,6 +202,85 @@ def example_edges(
199
202
  )
200
203
 
201
204
 
205
+ def example_trade_nodes_and_edges() -> Tuple[NodeCollection, Edges]:
206
+ """
207
+ Load the international trade nodes and edges.
208
+
209
+ Used in the
210
+ :doc:`Hive Plots with More Than 3 Groups </notebooks/hive_plots_more_than_three_groups>`
211
+ notebook.
212
+
213
+ Uses :py:func:`hiveplotlib.datasets.international_trade.international_trade_data` to load 2019 trade data for HS92
214
+ trade group 8112 ("beryllium, chromium, germanium, vanadium, gallium, hafnium, indium, niobium
215
+ (columbium), rhenium and thallium, articles thereof, and waste or scrap") from the
216
+ `Harvard Growth Lab <https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi%3A10.7910%2FDVN%2FT4CHWJ>`_.
217
+
218
+ **Node columns** (152 nodes):
219
+
220
+ - ``"country"``: ISO 3166-1 alpha-3 country code (unique ID).
221
+ - ``"continent"``: continent of the country (Africa, Asia, Europe, North America, Oceania, or South America).
222
+ - ``"export_value"``: total USD exported by that country within trade group 8112 in 2019.
223
+ Countries with no exports are assigned 0.
224
+
225
+ **Edge columns** (1158 edges):
226
+
227
+ Each edge represents non-zero trade from an origin country to a destination country.
228
+
229
+ - ``"origin_country"``: ISO 3166-1 alpha-3 code of the exporting country (from column).
230
+ - ``"destination_country"``: ISO 3166-1 alpha-3 code of the importing country (to column).
231
+ - ``"export_value"``: USD exported from the origin to the destination.
232
+ - ``"origin_continent"``: continent of the origin country.
233
+ - ``"destination_continent"``: continent of the destination country.
234
+
235
+ :return: tuple of ``(NodeCollection, Edges)`` representing the trade network.
236
+ """
237
+ data, _ = international_trade_data(year=2019, hs92_code=8112)
238
+
239
+ export_totals_per_country = (
240
+ data.loc[:, ["origin_country", "export_value"]]
241
+ .groupby("origin_country")
242
+ .sum()
243
+ .reset_index()
244
+ )
245
+
246
+ node_data = pd.concat(
247
+ [
248
+ data.loc[:, ["origin_country", "origin_continent"]].rename(
249
+ columns={
250
+ "origin_country": "country",
251
+ "origin_continent": "continent",
252
+ }
253
+ ),
254
+ data.loc[:, ["destination_country", "destination_continent"]].rename(
255
+ columns={
256
+ "destination_country": "country",
257
+ "destination_continent": "continent",
258
+ }
259
+ ),
260
+ ]
261
+ ).drop_duplicates()
262
+
263
+ node_data = node_data.merge(
264
+ export_totals_per_country,
265
+ how="left",
266
+ left_on="country",
267
+ right_on="origin_country",
268
+ ).drop(columns=["origin_country"])
269
+
270
+ # fill in countries with no exports as 0, not NaN
271
+ node_data.export_value = node_data.export_value.fillna(0)
272
+
273
+ nodes = NodeCollection(node_data, unique_id_column="country")
274
+
275
+ edges = Edges(
276
+ data,
277
+ from_column_name="origin_country",
278
+ to_column_name="destination_country",
279
+ )
280
+
281
+ return nodes, edges
282
+
283
+
202
284
  def example_nodes_and_edges(
203
285
  num_nodes: int = 100,
204
286
  num_edges: int = 200,
@@ -238,8 +320,8 @@ def example_hive_plot(
238
320
  num_nodes: int = 100,
239
321
  num_edges: int = 100,
240
322
  partition_data_column: Literal["low", "med", "high"] = "low",
241
- labels: Optional[List[Hashable]] = ("A", "B", "C"),
242
- cutoffs: Optional[Union[List[float], int]] = 3,
323
+ labels: Optional[List[Hashable]] = ["A", "B", "C"], # noqa: B006
324
+ cutoffs: Union[List[Union[int, float]], int] = 3,
243
325
  partition_variable_name: Optional[Hashable] = None,
244
326
  sorting_variables: Union[Hashable, Dict[Hashable, Hashable]] = "low",
245
327
  seed: int = 0,
@@ -310,7 +392,7 @@ def example_hive_plot(
310
392
  node_collection.data,
311
393
  left_on="to",
312
394
  right_on=node_unique_id_column,
313
- suffixes=[None, "_to"],
395
+ suffixes=(None, "_to"),
314
396
  )
315
397
  )
316
398
  for col in ["low", "med", "high"]:
@@ -329,3 +411,219 @@ def example_hive_plot(
329
411
  sorting_variables=sorting_variables,
330
412
  **hive_plot_kwargs,
331
413
  )
414
+
415
+
416
+ def example_hpm_nodes_and_edges(
417
+ num_groups: int = 3,
418
+ nodes_per_group: int = 10,
419
+ edge_tag_counts: Optional[Dict[str, int]] = None,
420
+ edge_structure: Optional[Dict[str, str]] = None,
421
+ seed: int = 42,
422
+ ) -> Tuple["NodeCollection", "Edges"]:
423
+ """
424
+ Generate a shared toy dataset that works well with ``HivePlotMatrix`` examples.
425
+
426
+ Returns a ``NodeCollection`` of ``num_groups * nodes_per_group`` nodes across
427
+ ``num_groups`` groups (A, B, C, ...) with structured numeric columns, and a
428
+ multi-tag ``Edges`` object.
429
+
430
+ **Node columns**:
431
+
432
+ - ``"unique_id"``: integers 0 to (num_groups * nodes_per_group - 1).
433
+ - ``"group"``: group label, assigned in consecutive blocks. With ``num_groups=3``,
434
+ nodes 0-9 are ``"A"``, 10-19 are ``"B"``, 20-29 are ``"C"``.
435
+ - ``"value1"``: *correlated with group* - each group draws from a distinct sub-interval
436
+ of [0, 10] in ascending order. Sorting by ``value1`` places groups at distinct,
437
+ separated positions along each axis if setting axes ``vmin`` and ``vmax`` values to the same fixed range.
438
+ - ``"value2"``: *uncorrelated noise* - all nodes draw from Uniform(0, 10). Sorting
439
+ by ``value2`` produces little visible group separation.
440
+ - ``"value3"``: *inversely correlated with group* - the mirror image of ``value1``.
441
+ The first group draws from the highest sub-interval, the last from the lowest.
442
+
443
+ **Default edge tags** (decreasing density):
444
+
445
+ - ``"official"``: 40 edges.
446
+ - ``"social"``: 30 edges.
447
+ - ``"informal"``: 20 edges.
448
+
449
+ :param num_groups: number of groups to generate (A, B, C, ...). Default 3.
450
+ :param nodes_per_group: number of nodes in each group. Default 10.
451
+ :param edge_tag_counts: dictionary mapping edge tag names to edge counts. Defaults to
452
+ ``{"official": 40, "social": 30, "informal": 20}``.
453
+ :param edge_structure: dictionary mapping edge tag names to structure types.
454
+ Supported types:
455
+
456
+ - ``"random"``: edges connect any two nodes uniformly at random (default).
457
+ - ``"intragroup"``: edges connect nodes within the same group only.
458
+ - ``"intergroup"``: edges connect nodes in different groups only.
459
+
460
+ Tags not listed in this dictionary default to ``"random"``.
461
+ :param seed: base random seed. Seeds ``seed``, ``seed+1``, ... are used for
462
+ successive edge tags respectively.
463
+ :return: tuple of ``(NodeCollection, Edges)``.
464
+ """
465
+ _valid_structures = {"random", "intragroup", "intergroup"}
466
+ if edge_tag_counts is None:
467
+ edge_tag_counts = {"official": 40, "social": 30, "informal": 20}
468
+ if edge_structure is None:
469
+ edge_structure = {}
470
+ for tag, structure in edge_structure.items():
471
+ if structure not in _valid_structures:
472
+ msg = (
473
+ f"edge_structure[{tag!r}] = {structure!r} is not valid. "
474
+ f"Must be one of {_valid_structures}."
475
+ )
476
+ raise ValueError(msg)
477
+ num_nodes = num_groups * nodes_per_group
478
+ rng = np.random.default_rng(seed)
479
+
480
+ # Deterministic block group assignment
481
+ group_labels = [chr(ord("A") + i) for i in range(num_groups)]
482
+ groups: list = []
483
+ for label in group_labels:
484
+ groups.extend([label] * nodes_per_group)
485
+
486
+ # Divide [0, 10] into num_groups equal sub-intervals
487
+ breakpoints = np.linspace(0, 10, num_groups + 1)
488
+
489
+ # value1: correlated - group i draws from [breakpoints[i], breakpoints[i+1]]
490
+ value1_parts = []
491
+ for i in range(num_groups):
492
+ lo, hi = breakpoints[i], breakpoints[i + 1]
493
+ value1_parts.append(rng.uniform(lo, hi, size=nodes_per_group))
494
+ value1 = np.concatenate(value1_parts)
495
+
496
+ value2 = rng.uniform(0, 10, size=num_nodes)
497
+
498
+ # value3: inversely correlated - group i draws from the reversed sub-interval
499
+ value3_parts = []
500
+ for i in range(num_groups):
501
+ j = num_groups - 1 - i
502
+ lo, hi = breakpoints[j], breakpoints[j + 1]
503
+ value3_parts.append(rng.uniform(lo, hi, size=nodes_per_group))
504
+ value3 = np.concatenate(value3_parts)
505
+
506
+ data = pd.DataFrame(
507
+ {
508
+ "unique_id": np.arange(num_nodes),
509
+ "group": groups,
510
+ "value1": value1,
511
+ "value2": value2,
512
+ "value3": value3,
513
+ }
514
+ )
515
+ nodes = NodeCollection(data=data, unique_id_column="unique_id")
516
+ node_ids = data["unique_id"].to_numpy()
517
+ groups_array = np.array(groups)
518
+
519
+ def _make_edges(n_edges: int, edge_seed: int, structure: str) -> pd.DataFrame:
520
+ edge_rng = np.random.default_rng(edge_seed)
521
+ if structure == "random":
522
+ return pd.DataFrame(
523
+ edge_rng.choice(node_ids, size=n_edges * 2).reshape(-1, 2),
524
+ columns=np.array(["from", "to"]),
525
+ )
526
+
527
+ from_nodes = edge_rng.choice(node_ids, size=n_edges)
528
+ to_nodes = np.empty(n_edges, dtype=node_ids.dtype)
529
+ for j in range(n_edges):
530
+ src_group = groups_array[from_nodes[j]]
531
+ if structure == "intragroup":
532
+ candidates = node_ids[groups_array == src_group]
533
+ else: # intergroup
534
+ candidates = node_ids[groups_array != src_group]
535
+ to_nodes[j] = edge_rng.choice(candidates)
536
+ return pd.DataFrame(
537
+ {"from": from_nodes, "to": to_nodes},
538
+ )
539
+
540
+ edges = Edges(
541
+ data={
542
+ tag: _make_edges(count, seed + i, edge_structure.get(tag, "random"))
543
+ for i, (tag, count) in enumerate(edge_tag_counts.items())
544
+ }
545
+ )
546
+ return nodes, edges
547
+
548
+
549
+ def example_minimal_hive_plot() -> HivePlot:
550
+ """
551
+ Generate a minimal ``HivePlot`` instance with edge color styling.
552
+
553
+ Uses :py:func:`example_hive_plot` with 6 nodes and 6 edges (seed 42) and adds
554
+ a uniform edge color of ``"#006BA4"`` to all axis pairs that have edges.
555
+
556
+ :return: ``HivePlot`` instance.
557
+ """
558
+ return example_hive_plot(
559
+ num_nodes=6,
560
+ num_edges=6,
561
+ seed=42,
562
+ all_edge_kwargs={"color": "#006BA4"},
563
+ )
564
+
565
+
566
+ def example_multi_tag_hive_plot() -> HivePlot:
567
+ """
568
+ Generate a ``HivePlot`` instance with two edge tags and ``repeat_axes=True``.
569
+
570
+ Creates 12 nodes (seed 10) with two independent sets of 15 edges (seeds 10 and 20)
571
+ stored under tags ``"tag_red"`` and ``"tag_blue"``. Edge colors are ``"#FF0000"``
572
+ and ``"#0000FF"`` respectively.
573
+
574
+ :return: ``HivePlot`` instance with two edge tags.
575
+ """
576
+ nodes = example_node_collection(num_nodes=12, seed=10)
577
+ edge_data_1 = example_edge_data(nodes=nodes, num_edges=15, seed=10)
578
+ edge_data_2 = example_edge_data(nodes=nodes, num_edges=15, seed=20)
579
+ multi_tag_edges = Edges(data={"tag_red": edge_data_1, "tag_blue": edge_data_2})
580
+ partition_variable = nodes.create_partition_variable(
581
+ data_column="low", labels=["A", "B", "C"]
582
+ )
583
+ hp = HivePlot(
584
+ nodes=nodes,
585
+ edges=multi_tag_edges,
586
+ partition_variable=partition_variable,
587
+ sorting_variables="low",
588
+ repeat_axes=True,
589
+ )
590
+ hp.edges.update_edge_viz_kwargs(tag="tag_red", color="#FF0000")
591
+ hp.edges.update_edge_viz_kwargs(tag="tag_blue", color="#0000FF")
592
+ return hp
593
+
594
+
595
+ def example_full_kwargs_hive_plot() -> HivePlot:
596
+ """
597
+ Generate a ``HivePlot`` instance with data-dependent edge kwargs, colormaps, and node styling.
598
+
599
+ Uses :py:func:`example_hive_plot` with 9 nodes, 10 edges (seed 99), and
600
+ ``repeat_axes=True``. Scaled edge data columns are created for alpha and linewidth,
601
+ and all edge visualization kwargs are set via
602
+ :py:meth:`~hiveplotlib.HivePlot.update_edge_plotting_keyword_arguments` using column
603
+ name references:
604
+
605
+ - **color**: the ``"low"`` edge data column mapped through a ``"cividis"`` colormap.
606
+ - **alpha**: the ``"alpha_scaled"`` column (``low / 10``, yielding values in ~0.3-1.0).
607
+ - **linewidth**: the ``"lw_scaled"`` column (``low / 3``, yielding values in ~1.0-3.3).
608
+
609
+ Node styling is applied via ``update_node_viz_kwargs`` with a ``"viridis"`` colormap mapped to the ``"low"``
610
+ data column.
611
+
612
+ :return: ``HivePlot`` instance with node and edge kwargs.
613
+ """
614
+ hp = example_hive_plot(num_nodes=9, num_edges=10, seed=99, repeat_axes=True)
615
+ # create scaled edge data columns for alpha (0-1 range) and linewidth
616
+ hp.edges.data["alpha_scaled"] = hp.edges.data["low"] / 10
617
+ hp.edges.data["lw_scaled"] = hp.edges.data["low"] / 3
618
+ # data-dependent edge kwargs: color, alpha, and linewidth all from edge data columns
619
+ hp.update_edge_plotting_keyword_arguments(
620
+ array="low",
621
+ cmap="cividis",
622
+ clim=(0, 10),
623
+ alpha="alpha_scaled",
624
+ linewidth="lw_scaled",
625
+ )
626
+ hp.update_node_viz_kwargs(
627
+ c="low", s=30, alpha=0.8, edgecolor="black", cmap="viridis"
628
+ )
629
+ return hp
@@ -34,7 +34,7 @@ def four_gaussian_blobs_3d(
34
34
  corners_to_keep = [0, 1, 2, 4]
35
35
 
36
36
  # name of the features we will create for each set of data generated below
37
- feature_names = ["X", "Y", "Z", "Label"]
37
+ feature_names = np.array(["X", "Y", "Z", "Label"])
38
38
 
39
39
  # set seed for consistent data
40
40
  rng = np.random.default_rng(random_seed)
@@ -63,7 +63,7 @@ def four_gaussian_blobs_3d(
63
63
  # make the labels ints
64
64
  df.Label = df.Label.astype(int)
65
65
  # replace the 4s with 3s so our labels are just range(4)
66
- df.Label.to_numpy()[df.Label.to_numpy() == 4] = 3
66
+ df.loc[df["Label"] == 4, "Label"] = 3
67
67
 
68
68
  return df
69
69