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.
- {hiveplotlib-0.26.2/src/hiveplotlib.egg-info → hiveplotlib-0.27.0}/PKG-INFO +13 -6
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/pyproject.toml +29 -8
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/__init__.py +4 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/axis.py +4 -1
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/converters.py +18 -15
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/international_trade.py +1 -1
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/toy_hive_plots.py +304 -6
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/toy_p2cps.py +2 -2
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/edges.py +48 -21
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/hiveplot.py +286 -75
- hiveplotlib-0.27.0/src/hiveplotlib/hiveplot_matrix.py +1274 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/node.py +26 -13
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/p2cp.py +20 -17
- hiveplotlib-0.27.0/src/hiveplotlib/utils.py +316 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/base.py +16 -5
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/bokeh.py +15 -15
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/datashader.py +106 -49
- hiveplotlib-0.27.0/src/hiveplotlib/viz/hiveplot_matrix.py +657 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/holoviews.py +12 -12
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/matplotlib.py +8 -7
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/plotly.py +37 -22
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0/src/hiveplotlib.egg-info}/PKG-INFO +13 -6
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/SOURCES.txt +2 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/requires.txt +14 -5
- hiveplotlib-0.26.2/src/hiveplotlib/utils.py +0 -96
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/LICENSE +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/MANIFEST.in +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/README.md +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/setup.cfg +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/setup.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/__init__.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_2019_8112.csv +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_metadata_2019_8112.json +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/__init__.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/hive_plot.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/exceptions/node.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/__init__.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib/viz/input_checks.py +0 -0
- {hiveplotlib-0.26.2 → hiveplotlib-0.27.0}/src/hiveplotlib.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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
|

|
|
@@ -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.
|
|
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(
|
|
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,
|
|
12
|
-
from hiveplotlib.node import node_collection_from_node_list
|
|
9
|
+
from hiveplotlib import Edges, NodeCollection
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
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:
|
|
19
|
+
graph: nx.Graph,
|
|
20
20
|
unique_id_name: str = "unique_id",
|
|
21
21
|
check_uniqueness: bool = True,
|
|
22
|
-
) ->
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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=
|
|
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(
|
|
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]] =
|
|
242
|
-
cutoffs:
|
|
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=
|
|
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.
|
|
66
|
+
df.loc[df["Label"] == 4, "Label"] = 3
|
|
67
67
|
|
|
68
68
|
return df
|
|
69
69
|
|