hiveplotlib 0.27.0__tar.gz → 0.28.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.27.0 → hiveplotlib-0.28.0}/LICENSE +2 -2
- {hiveplotlib-0.27.0/src/hiveplotlib.egg-info → hiveplotlib-0.28.0}/PKG-INFO +26 -21
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/README.md +14 -9
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/pyproject.toml +11 -11
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/__init__.py +0 -1
- hiveplotlib-0.28.0/src/hiveplotlib/converters.py +166 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/international_trade.py +1 -1
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/toy_hive_plots.py +96 -25
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_metadata_2019_8112.json +1 -1
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/edges.py +8 -2
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/exceptions/hive_plot.py +11 -0
- hiveplotlib-0.28.0/src/hiveplotlib/graph_features/__init__.py +858 -0
- hiveplotlib-0.28.0/src/hiveplotlib/graph_features/networkx/__init__.py +103 -0
- hiveplotlib-0.28.0/src/hiveplotlib/graph_features/networkx/_helpers.py +177 -0
- hiveplotlib-0.28.0/src/hiveplotlib/graph_features/networkx/edge_metrics.py +259 -0
- hiveplotlib-0.28.0/src/hiveplotlib/graph_features/networkx/node_metrics.py +913 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/hiveplot.py +817 -432
- hiveplotlib-0.28.0/src/hiveplotlib/hiveplot_matrix.py +2274 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/hiveplot_matrix.py +2 -2
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0/src/hiveplotlib.egg-info}/PKG-INFO +26 -21
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib.egg-info/SOURCES.txt +5 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib.egg-info/requires.txt +7 -7
- hiveplotlib-0.27.0/src/hiveplotlib/converters.py +0 -44
- hiveplotlib-0.27.0/src/hiveplotlib/hiveplot_matrix.py +0 -1274
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/MANIFEST.in +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/setup.cfg +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/setup.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/axis.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/__init__.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/toy_p2cps.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/datasets/trade_data_harvard_growth_lab/international_exports_2019_8112.csv +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/exceptions/__init__.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/exceptions/node.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/node.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/p2cp.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/utils.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/__init__.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/base.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/bokeh.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/datashader.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/holoviews.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/input_checks.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/matplotlib.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib/viz/plotly.py +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib.egg-info/dependency_links.txt +0 -0
- {hiveplotlib-0.27.0 → hiveplotlib-0.28.0}/src/hiveplotlib.egg-info/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2020 -
|
|
1
|
+
Copyright (c) 2020 - 2026, Gary Koplik
|
|
2
2
|
All rights reserved.
|
|
3
3
|
|
|
4
4
|
Redistribution and use in source and binary forms, with or without modification,
|
|
@@ -11,7 +11,7 @@ Redistributions in binary form must reproduce the above copyright notice,
|
|
|
11
11
|
this list of conditions and the following disclaimer in the documentation
|
|
12
12
|
and/or other materials provided with the distribution.
|
|
13
13
|
|
|
14
|
-
Neither the name of
|
|
14
|
+
Neither the name of Gary Koplik nor the names of any contributors
|
|
15
15
|
may be used to endorse or promote products derived from this software
|
|
16
16
|
without specific prior written permission.
|
|
17
17
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hiveplotlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.28.0
|
|
4
4
|
Summary: Visualize Network Data with Hive Plots
|
|
5
|
-
Author-email: Gary Koplik <gary.koplik@
|
|
6
|
-
License: Copyright (c) 2020 -
|
|
5
|
+
Author-email: Gary Koplik <gary.koplik@gmail.com>
|
|
6
|
+
License: Copyright (c) 2020 - 2026, Gary Koplik
|
|
7
7
|
All rights reserved.
|
|
8
8
|
|
|
9
9
|
Redistribution and use in source and binary forms, with or without modification,
|
|
@@ -16,7 +16,7 @@ License: Copyright (c) 2020 - 2024, Geometric Data Analytics, Inc.
|
|
|
16
16
|
this list of conditions and the following disclaimer in the documentation
|
|
17
17
|
and/or other materials provided with the distribution.
|
|
18
18
|
|
|
19
|
-
Neither the name of
|
|
19
|
+
Neither the name of Gary Koplik nor the names of any contributors
|
|
20
20
|
may be used to endorse or promote products derived from this software
|
|
21
21
|
without specific prior written permission.
|
|
22
22
|
|
|
@@ -33,7 +33,7 @@ License: Copyright (c) 2020 - 2024, Geometric Data Analytics, Inc.
|
|
|
33
33
|
THE POSSIBILITY OF SUCH DAMAGE.
|
|
34
34
|
|
|
35
35
|
Project-URL: Documentation, https://hiveplotlib.readthedocs.io/stable/
|
|
36
|
-
Project-URL: Source, https://gitlab.com/
|
|
36
|
+
Project-URL: Source, https://gitlab.com/hiveplotlib/hiveplotlib
|
|
37
37
|
Requires-Python: >=3.10
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
@@ -48,19 +48,18 @@ 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>=3.0.0; extra == "dev"
|
|
52
51
|
Requires-Dist: cartopy; extra == "dev"
|
|
53
|
-
Requires-Dist: dask[dataframe]; extra == "dev"
|
|
54
|
-
Requires-Dist: datashader; extra == "dev"
|
|
55
52
|
Requires-Dist: flexitext; extra == "dev"
|
|
56
53
|
Requires-Dist: geopandas; extra == "dev"
|
|
54
|
+
Requires-Dist: hiveplotlib[bokeh]; extra == "dev"
|
|
55
|
+
Requires-Dist: hiveplotlib[datashader]; extra == "dev"
|
|
56
|
+
Requires-Dist: hiveplotlib[holoviews]; extra == "dev"
|
|
57
|
+
Requires-Dist: hiveplotlib[networkx]; extra == "dev"
|
|
58
|
+
Requires-Dist: hiveplotlib[plotly]; extra == "dev"
|
|
57
59
|
Requires-Dist: holoviews>=1.15; extra == "dev"
|
|
58
60
|
Requires-Dist: jupyter; extra == "dev"
|
|
59
|
-
Requires-Dist:
|
|
60
|
-
Requires-Dist: numba>0.54.0; extra == "dev"
|
|
61
|
-
Requires-Dist: plotly; extra == "dev"
|
|
61
|
+
Requires-Dist: nx-parallel; extra == "dev"
|
|
62
62
|
Requires-Dist: pybadges; extra == "dev"
|
|
63
|
-
Requires-Dist: seaborn; extra == "dev"
|
|
64
63
|
Requires-Dist: tqdm; extra == "dev"
|
|
65
64
|
Provides-Extra: docs
|
|
66
65
|
Requires-Dist: ipywidgets; extra == "docs"
|
|
@@ -76,6 +75,7 @@ Requires-Dist: holoviews>=1.15; extra == "holoviews"
|
|
|
76
75
|
Requires-Dist: numba>0.54.0; extra == "holoviews"
|
|
77
76
|
Provides-Extra: networkx
|
|
78
77
|
Requires-Dist: networkx; extra == "networkx"
|
|
78
|
+
Requires-Dist: scipy; extra == "networkx"
|
|
79
79
|
Provides-Extra: perf
|
|
80
80
|
Requires-Dist: pyinstrument; extra == "perf"
|
|
81
81
|
Requires-Dist: snakeviz; extra == "perf"
|
|
@@ -99,13 +99,14 @@ Dynamic: license-file
|
|
|
99
99
|
|
|
100
100
|
A plotting package for generating and visualizing static Hive Plots in Python.
|
|
101
101
|
|
|
102
|
-

|
|
103
103
|
|
|
104
|
-
[](https://matplotlib.org/)
|
|
105
|
+
[](https://docs.bokeh.org/en/latest/)
|
|
106
|
+
[](https://holoviews.org/index.html)
|
|
107
|
+
[](https://plotly.com/python/)
|
|
108
|
+
[](https://datashader.org/)
|
|
109
|
+
[](https://networkx.org/)
|
|
109
110
|
|
|
110
111
|
# Installation
|
|
111
112
|
|
|
@@ -129,6 +130,10 @@ and [plotly](https://plotly.com/python/) visualizations, which can be installed
|
|
|
129
130
|
`hiveplotlib` also supports large network visualization via the [datashader](https://datashader.org/) backend,
|
|
130
131
|
which can be installed as `pip install hiveplotlib[datashader]`.
|
|
131
132
|
|
|
133
|
+
Beyond the visualization backends, `hiveplotlib` integrates with [networkx](https://networkx.org/): build a hive plot
|
|
134
|
+
directly from a `networkx` graph and compute graph metrics on it. Install this integration with
|
|
135
|
+
`pip install hiveplotlib[networkx]`.
|
|
136
|
+
|
|
132
137
|
# How to Use and Examples
|
|
133
138
|
|
|
134
139
|
For more on how to use the software and examples, see the
|
|
@@ -140,7 +145,7 @@ We recommend starting with our
|
|
|
140
145
|
[Quick Start Hive Plots](https://hiveplotlib.readthedocs.io/stable/notebooks/quick_hive_plots.html) pages.
|
|
141
146
|
|
|
142
147
|
All the example notebooks are available for download as `jupyter` notebooks in the repository under the
|
|
143
|
-
[examples](https://gitlab.com/
|
|
148
|
+
[examples](https://gitlab.com/hiveplotlib/hiveplotlib/-/tree/master/examples) directory.
|
|
144
149
|
|
|
145
150
|
To install this environment and associated `jupyter` kernel used to run the notebooks, clone the repository and run:
|
|
146
151
|
|
|
@@ -158,7 +163,7 @@ For more on Hive Plots, see our
|
|
|
158
163
|
|
|
159
164
|
For additional resources, see:
|
|
160
165
|
|
|
161
|
-
- [
|
|
166
|
+
- [https://hiveplot.com/](https://hiveplot.com/)
|
|
162
167
|
|
|
163
168
|
- Krzywinski M, Birol I, Jones S, Marra M (2011). Hive Plots — Rational Approach to
|
|
164
169
|
Visualizing Networks. Briefings in Bioinformatics (early access 9 December 2011,
|
|
@@ -166,7 +171,7 @@ doi: 10.1093/bib/bbr069).
|
|
|
166
171
|
|
|
167
172
|
# Contributing
|
|
168
173
|
|
|
169
|
-
For more on contributing to the project, see [CONTRIBUTING.md](https://gitlab.com/
|
|
174
|
+
For more on contributing to the project, see [CONTRIBUTING.md](https://gitlab.com/hiveplotlib/hiveplotlib/-/blob/master/CONTRIBUTING.md)
|
|
170
175
|
|
|
171
176
|
## Acknowledgements
|
|
172
177
|
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A plotting package for generating and visualizing static Hive Plots in Python.
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
|
-
[](https://matplotlib.org/)
|
|
8
|
+
[](https://docs.bokeh.org/en/latest/)
|
|
9
|
+
[](https://holoviews.org/index.html)
|
|
10
|
+
[](https://plotly.com/python/)
|
|
11
|
+
[](https://datashader.org/)
|
|
12
|
+
[](https://networkx.org/)
|
|
12
13
|
|
|
13
14
|
# Installation
|
|
14
15
|
|
|
@@ -32,6 +33,10 @@ and [plotly](https://plotly.com/python/) visualizations, which can be installed
|
|
|
32
33
|
`hiveplotlib` also supports large network visualization via the [datashader](https://datashader.org/) backend,
|
|
33
34
|
which can be installed as `pip install hiveplotlib[datashader]`.
|
|
34
35
|
|
|
36
|
+
Beyond the visualization backends, `hiveplotlib` integrates with [networkx](https://networkx.org/): build a hive plot
|
|
37
|
+
directly from a `networkx` graph and compute graph metrics on it. Install this integration with
|
|
38
|
+
`pip install hiveplotlib[networkx]`.
|
|
39
|
+
|
|
35
40
|
# How to Use and Examples
|
|
36
41
|
|
|
37
42
|
For more on how to use the software and examples, see the
|
|
@@ -43,7 +48,7 @@ We recommend starting with our
|
|
|
43
48
|
[Quick Start Hive Plots](https://hiveplotlib.readthedocs.io/stable/notebooks/quick_hive_plots.html) pages.
|
|
44
49
|
|
|
45
50
|
All the example notebooks are available for download as `jupyter` notebooks in the repository under the
|
|
46
|
-
[examples](https://gitlab.com/
|
|
51
|
+
[examples](https://gitlab.com/hiveplotlib/hiveplotlib/-/tree/master/examples) directory.
|
|
47
52
|
|
|
48
53
|
To install this environment and associated `jupyter` kernel used to run the notebooks, clone the repository and run:
|
|
49
54
|
|
|
@@ -61,7 +66,7 @@ For more on Hive Plots, see our
|
|
|
61
66
|
|
|
62
67
|
For additional resources, see:
|
|
63
68
|
|
|
64
|
-
- [
|
|
69
|
+
- [https://hiveplot.com/](https://hiveplot.com/)
|
|
65
70
|
|
|
66
71
|
- Krzywinski M, Birol I, Jones S, Marra M (2011). Hive Plots — Rational Approach to
|
|
67
72
|
Visualizing Networks. Briefings in Bioinformatics (early access 9 December 2011,
|
|
@@ -69,7 +74,7 @@ doi: 10.1093/bib/bbr069).
|
|
|
69
74
|
|
|
70
75
|
# Contributing
|
|
71
76
|
|
|
72
|
-
For more on contributing to the project, see [CONTRIBUTING.md](https://gitlab.com/
|
|
77
|
+
For more on contributing to the project, see [CONTRIBUTING.md](https://gitlab.com/hiveplotlib/hiveplotlib/-/blob/master/CONTRIBUTING.md)
|
|
73
78
|
|
|
74
79
|
## Acknowledgements
|
|
75
80
|
|
|
@@ -7,14 +7,14 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "hiveplotlib"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
|
-
version = "0.
|
|
10
|
+
version = "0.28.0"
|
|
11
11
|
authors = [
|
|
12
|
-
{name="Gary Koplik", email="gary.koplik@
|
|
12
|
+
{name="Gary Koplik", email="gary.koplik@gmail.com"}
|
|
13
13
|
]
|
|
14
14
|
description = "Visualize Network Data with Hive Plots"
|
|
15
15
|
readme = "README.md"
|
|
16
16
|
license = {file = "LICENSE"}
|
|
17
|
-
urls = {Documentation="https://hiveplotlib.readthedocs.io/stable/", Source="https://gitlab.com/
|
|
17
|
+
urls = {Documentation="https://hiveplotlib.readthedocs.io/stable/", Source="https://gitlab.com/hiveplotlib/hiveplotlib" }
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
dependencies = [
|
|
@@ -38,19 +38,18 @@ datashader = [
|
|
|
38
38
|
|
|
39
39
|
# developer dependencies
|
|
40
40
|
dev = [
|
|
41
|
-
"bokeh>=3.0.0",
|
|
42
41
|
"cartopy",
|
|
43
|
-
"dask[dataframe]", # https://github.com/holoviz/datashader/issues/1319
|
|
44
|
-
"datashader",
|
|
45
42
|
"flexitext",
|
|
46
43
|
"geopandas",
|
|
44
|
+
"hiveplotlib[bokeh]",
|
|
45
|
+
"hiveplotlib[datashader]",
|
|
46
|
+
"hiveplotlib[holoviews]",
|
|
47
|
+
"hiveplotlib[networkx]",
|
|
48
|
+
"hiveplotlib[plotly]",
|
|
47
49
|
"holoviews>=1.15",
|
|
48
50
|
"jupyter",
|
|
49
|
-
"
|
|
50
|
-
"numba>0.54.0", # https://github.com/numba/numba/issues/9708
|
|
51
|
-
"plotly",
|
|
51
|
+
"nx-parallel", # CI-testable networkx dispatch backend for `graph_metric_backend` tests
|
|
52
52
|
"pybadges",
|
|
53
|
-
"seaborn",
|
|
54
53
|
"tqdm",
|
|
55
54
|
]
|
|
56
55
|
|
|
@@ -72,7 +71,8 @@ holoviews = [
|
|
|
72
71
|
]
|
|
73
72
|
|
|
74
73
|
networkx = [
|
|
75
|
-
"networkx"
|
|
74
|
+
"networkx",
|
|
75
|
+
"scipy", # for some graph feature computations
|
|
76
76
|
]
|
|
77
77
|
|
|
78
78
|
# performance profiling extras
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# converters.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Converters from various data structures to ``hiveplotlib``-ready structures.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
from hiveplotlib import Edges, NodeCollection
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import networkx as nx
|
|
15
|
+
except ImportError as ie: # pragma: no cover
|
|
16
|
+
msg = "`networkx` dependencies not installed, but can be installed by running `pip install hiveplotlib[networkx]`"
|
|
17
|
+
raise ImportError(msg) from ie
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def networkx_to_nodes_edges(
|
|
21
|
+
graph: nx.Graph,
|
|
22
|
+
unique_id_name: str = "unique_id",
|
|
23
|
+
check_uniqueness: bool = True,
|
|
24
|
+
) -> tuple[NodeCollection, Edges]:
|
|
25
|
+
"""
|
|
26
|
+
Take a ``networkx`` graph and return ``hiveplotlib``-friendly data structures.
|
|
27
|
+
|
|
28
|
+
Specifically, returns a ``NodeCollection`` instance of node data and an ``Edges`` instance of
|
|
29
|
+
edge data. These outputs can be fed directly into :py:func:`~hiveplotlib.HivePlot`.
|
|
30
|
+
|
|
31
|
+
:param graph: ``networkx`` graph (any of :py:class:`~networkx.Graph`, :py:class:`~networkx.DiGraph`,
|
|
32
|
+
:py:class:`~networkx.MultiGraph`, or :py:class:`~networkx.MultiDiGraph`).
|
|
33
|
+
:param unique_id_name: name to use for unique IDs.
|
|
34
|
+
:param check_uniqueness: whether or not to check that provided ``Node`` instances have unique IDs.
|
|
35
|
+
:return: a ``(NodeCollection, Edges)`` pair of ``hiveplotlib`` data structures.
|
|
36
|
+
"""
|
|
37
|
+
node_data = []
|
|
38
|
+
for node, attributes in graph.nodes(data=True):
|
|
39
|
+
node_data.append({unique_id_name: node, **attributes})
|
|
40
|
+
|
|
41
|
+
node_df = pd.DataFrame(node_data)
|
|
42
|
+
edge_df = nx.to_pandas_edgelist(graph, source="from", target="to")
|
|
43
|
+
return NodeCollection(
|
|
44
|
+
data=node_df,
|
|
45
|
+
unique_id_column=unique_id_name,
|
|
46
|
+
check_uniqueness=check_uniqueness,
|
|
47
|
+
), Edges(data=edge_df)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def nodes_edges_to_networkx(
|
|
51
|
+
nodes: NodeCollection,
|
|
52
|
+
edges: Edges,
|
|
53
|
+
*,
|
|
54
|
+
directed: bool = True,
|
|
55
|
+
multigraph: bool = True,
|
|
56
|
+
tag_attribute_name: str = "tag",
|
|
57
|
+
source_attribute_name: Optional[str] = None,
|
|
58
|
+
) -> nx.Graph:
|
|
59
|
+
"""
|
|
60
|
+
Convert a (``NodeCollection``, ``Edges``) pair to a ``networkx`` graph.
|
|
61
|
+
|
|
62
|
+
The concrete return type is determined by ``directed`` and ``multigraph``:
|
|
63
|
+
|
|
64
|
+
- ``directed=True``, ``multigraph=True`` → :py:class:`networkx.MultiDiGraph` (default).
|
|
65
|
+
- ``directed=True``, ``multigraph=False`` → :py:class:`networkx.DiGraph`.
|
|
66
|
+
- ``directed=False``, ``multigraph=True`` → :py:class:`networkx.MultiGraph`.
|
|
67
|
+
- ``directed=False``, ``multigraph=False`` → :py:class:`networkx.Graph`.
|
|
68
|
+
|
|
69
|
+
The defaults preserve duplicate edges faithfully and assume directed edges.
|
|
70
|
+
|
|
71
|
+
All non-(from / to) ``Edges`` DataFrame columns become edge attributes on the resulting graph. All
|
|
72
|
+
non-``unique_id_column`` node DataFrame columns become node attributes. The visualization-only attributes
|
|
73
|
+
``node_viz_kwargs`` / ``edge_viz_kwargs`` are *not* included.
|
|
74
|
+
|
|
75
|
+
Multi-tag :py:class:`hiveplotlib.Edges`: when more than one tag is present, the original tag is written as an edge
|
|
76
|
+
attribute under ``tag_attribute_name`` (default ``"tag"``). If that name collides with an existing column on any
|
|
77
|
+
tag's edge DataFrame, a :class:`ValueError` is raised. Single-tag input does not write a tag attribute.
|
|
78
|
+
|
|
79
|
+
Optional source-row annotation: pass a string for ``source_attribute_name`` to tag each edge with a
|
|
80
|
+
``(tag, row_index)`` tuple pointing back to its source row in the input :py:class:`hiveplotlib.Edges`. This is
|
|
81
|
+
useful for joining graph-level computations (e.g. edge metrics) back onto the original edge DataFrame (including
|
|
82
|
+
when there are multiple parallel edges in a multigraph). Default ``None`` skips this annotation.
|
|
83
|
+
|
|
84
|
+
.. note::
|
|
85
|
+
When ``multigraph=False`` but duplicate ``(from, to)`` pairs exist, ``networkx``'s :py:class:`networkx.Graph` /
|
|
86
|
+
:py:class:`networkx.DiGraph` will silently merge duplicates (last write wins). For an undirected
|
|
87
|
+
:py:class:`networkx.Graph`, reciprocal ``(a, b)`` / ``(b, a)`` rows likewise merge into a single undirected edge
|
|
88
|
+
(last write wins), so the merged edge carries only the surviving row's attributes (e.g. a ``weight``). Build
|
|
89
|
+
with ``multigraph=True`` to retain both edges as parallel edges, or pre-aggregate the weights into a single edge
|
|
90
|
+
beforehand.
|
|
91
|
+
|
|
92
|
+
:param nodes: :py:class:`hiveplotlib.NodeCollection` of node data.
|
|
93
|
+
:param edges: :py:class:`hiveplotlib.Edges` of edge data (single-tag or multi-tag).
|
|
94
|
+
:param directed: whether to return a :py:class:`networkx.DiGraph`-family graph. Default ``True``.
|
|
95
|
+
:param multigraph: whether to return a ``Multi*``-family graph. Default ``True``.
|
|
96
|
+
:param tag_attribute_name: edge-attribute name to use when storing the original tag on multi-tag inputs. Default
|
|
97
|
+
``"tag"``.
|
|
98
|
+
:param source_attribute_name: optional edge-attribute name under which to store a ``(tag, row_index)`` source-row
|
|
99
|
+
identifier for every edge, useful for joining graph-level computations back onto the original edge DataFrame.
|
|
100
|
+
Default ``None`` skips annotation.
|
|
101
|
+
:return: ``networkx`` graph (one of :py:class:`networkx.Graph`, :py:class:`networkx.DiGraph`,
|
|
102
|
+
:py:class:`networkx.MultiGraph`, or :py:class:`networkx.MultiDiGraph`).
|
|
103
|
+
:raises ValueError: if ``tag_attribute_name`` collides with an existing edge column on a multi-tag
|
|
104
|
+
:py:class:`hiveplotlib.Edges` instance.
|
|
105
|
+
:raises ValueError: if ``source_attribute_name`` is a string and collides with an existing edge column on any tag.
|
|
106
|
+
"""
|
|
107
|
+
if directed and multigraph:
|
|
108
|
+
graph_class = nx.MultiDiGraph
|
|
109
|
+
elif directed:
|
|
110
|
+
graph_class = nx.DiGraph
|
|
111
|
+
elif multigraph:
|
|
112
|
+
graph_class = nx.MultiGraph
|
|
113
|
+
else:
|
|
114
|
+
graph_class = nx.Graph
|
|
115
|
+
g = graph_class()
|
|
116
|
+
|
|
117
|
+
unique_id_column = nodes.unique_id_column
|
|
118
|
+
node_attr_cols = [c for c in nodes.data.columns if c != unique_id_column]
|
|
119
|
+
if node_attr_cols:
|
|
120
|
+
attr_records = nodes.data[node_attr_cols].to_dict(orient="records")
|
|
121
|
+
g.add_nodes_from(zip(nodes.data[unique_id_column], attr_records, strict=True))
|
|
122
|
+
else:
|
|
123
|
+
g.add_nodes_from(nodes.data[unique_id_column])
|
|
124
|
+
|
|
125
|
+
from_col = edges.from_column_name
|
|
126
|
+
to_col = edges.to_column_name
|
|
127
|
+
multi_tag = len(edges._data) > 1
|
|
128
|
+
|
|
129
|
+
if multi_tag:
|
|
130
|
+
for tag, df in edges._data.items():
|
|
131
|
+
if tag_attribute_name in df.columns:
|
|
132
|
+
msg = (
|
|
133
|
+
f"Cannot write tag attribute under name '{tag_attribute_name}' "
|
|
134
|
+
f"because tag '{tag}' already has a column with that name. "
|
|
135
|
+
"Pass `tag_attribute_name='<new_name>'` to choose a different "
|
|
136
|
+
"attribute name."
|
|
137
|
+
)
|
|
138
|
+
raise ValueError(msg)
|
|
139
|
+
|
|
140
|
+
if source_attribute_name is not None:
|
|
141
|
+
for tag, df in edges._data.items():
|
|
142
|
+
if source_attribute_name in df.columns:
|
|
143
|
+
msg = (
|
|
144
|
+
f"Cannot write source attribute under name '{source_attribute_name}' "
|
|
145
|
+
f"because tag '{tag}' already has a column with that name. "
|
|
146
|
+
"Pass `source_attribute_name='<new_name>'` to choose a different "
|
|
147
|
+
"attribute name."
|
|
148
|
+
)
|
|
149
|
+
raise ValueError(msg)
|
|
150
|
+
|
|
151
|
+
for tag, df in edges._data.items():
|
|
152
|
+
edge_attr_cols = [c for c in df.columns if c not in (from_col, to_col)]
|
|
153
|
+
attr_records = (
|
|
154
|
+
df[edge_attr_cols].to_dict(orient="records")
|
|
155
|
+
if edge_attr_cols
|
|
156
|
+
else [{} for _ in range(len(df))]
|
|
157
|
+
)
|
|
158
|
+
if multi_tag:
|
|
159
|
+
for attrs in attr_records:
|
|
160
|
+
attrs[tag_attribute_name] = tag
|
|
161
|
+
if source_attribute_name is not None:
|
|
162
|
+
for attrs, row_index in zip(attr_records, df.index, strict=True):
|
|
163
|
+
attrs[source_attribute_name] = (tag, row_index)
|
|
164
|
+
g.add_edges_from(zip(df[from_col], df[to_col], attr_records, strict=True))
|
|
165
|
+
|
|
166
|
+
return g
|
|
@@ -21,7 +21,7 @@ def international_trade_data(
|
|
|
21
21
|
roughly 300mb. However, the raw data are available at the
|
|
22
22
|
`Harvard Growth Lab's website <https://doi.org/10.7910/DVN/T4CHWJ>`_, and the runner to produce the necessary
|
|
23
23
|
files to use this reader function is available in the
|
|
24
|
-
`repository <https://gitlab.com/
|
|
24
|
+
`repository <https://gitlab.com/hiveplotlib/hiveplotlib/-/blob/master/runners/make_trade_network_dataset.py>`_
|
|
25
25
|
(``make_trade_network_dataset.py``).
|
|
26
26
|
|
|
27
27
|
If you are using the runner to make your own trade datasets that you will read in locally with this
|
|
@@ -11,8 +11,8 @@ from hiveplotlib import (
|
|
|
11
11
|
BaseHivePlot,
|
|
12
12
|
Node,
|
|
13
13
|
NodeCollection,
|
|
14
|
-
hive_plot_n_axes,
|
|
15
14
|
)
|
|
15
|
+
from hiveplotlib.axis import Axis
|
|
16
16
|
from hiveplotlib.datasets.international_trade import international_trade_data
|
|
17
17
|
from hiveplotlib.edges import Edges
|
|
18
18
|
from hiveplotlib.hiveplot import HivePlot
|
|
@@ -23,18 +23,17 @@ def example_base_hive_plot(
|
|
|
23
23
|
num_nodes: int = 15,
|
|
24
24
|
num_edges: int = 30,
|
|
25
25
|
seed: int = 0,
|
|
26
|
-
**hive_plot_n_axes_kwargs,
|
|
27
26
|
) -> BaseHivePlot:
|
|
28
27
|
"""
|
|
29
28
|
Generate example hive plot with ``"Low"``, ``"Medium"``, and ``"High"`` axes (plus repeat axes).
|
|
30
29
|
|
|
31
|
-
Nodes and edges will be generated and placed randomly.
|
|
30
|
+
Nodes and edges will be generated and placed randomly. The resulting ``BaseHivePlot`` has 6 axes
|
|
31
|
+
(each main axis paired with a ``"_repeat"`` sibling) arranged at 120° apart with 40° between each
|
|
32
|
+
pair of repeat axes.
|
|
32
33
|
|
|
33
34
|
:param num_nodes: number of nodes to generate.
|
|
34
35
|
:param num_edges: number of edges to generate.
|
|
35
36
|
:param seed: random seed to use when generating nodes and edges.
|
|
36
|
-
:param hive_plot_n_axes_kwargs: additional keyword arguments for the underlying
|
|
37
|
-
:py:func:`hiveplotlib.hive_plot_n_axes()` call.
|
|
38
37
|
:return: resulting ``BaseHivePlot`` instance.
|
|
39
38
|
"""
|
|
40
39
|
color_dict = {
|
|
@@ -61,20 +60,62 @@ def example_base_hive_plot(
|
|
|
61
60
|
|
|
62
61
|
edges = rng.choice(data["unique_id"].to_numpy(), size=num_edges * 2).reshape(-1, 2)
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
63
|
+
nodes = NodeCollection(data=data, unique_id_column="unique_id")
|
|
64
|
+
axis_assignments = {
|
|
65
|
+
"Low": np.arange(num_nodes)[: num_nodes // 3],
|
|
66
|
+
"Medium": np.arange(num_nodes)[num_nodes // 3 : 2 * num_nodes // 3],
|
|
67
|
+
"High": np.arange(num_nodes)[2 * num_nodes // 3 :],
|
|
68
|
+
}
|
|
69
|
+
sorting_variable = {"Low": "low", "Medium": "med", "High": "high"}
|
|
70
|
+
|
|
71
|
+
# 3 partitions evenly spaced 120° apart, with the first axis center at -30°
|
|
72
|
+
base_angles = {"Low": -30, "Medium": 90, "High": 210}
|
|
73
|
+
angle_between_repeat_axes = 40
|
|
74
|
+
|
|
75
|
+
hp = BaseHivePlot()
|
|
76
|
+
hp.add_nodes(nodes)
|
|
77
|
+
|
|
78
|
+
for partition, base_angle in base_angles.items():
|
|
79
|
+
main_axis_angle = base_angle - angle_between_repeat_axes / 2
|
|
80
|
+
repeat_axis_angle = base_angle + angle_between_repeat_axes / 2
|
|
81
|
+
hp.add_axes(
|
|
82
|
+
[
|
|
83
|
+
Axis(axis_id=partition, angle=main_axis_angle),
|
|
84
|
+
Axis(axis_id=f"{partition}_repeat", angle=repeat_axis_angle),
|
|
85
|
+
]
|
|
86
|
+
)
|
|
87
|
+
partition_node_df = data.iloc[axis_assignments[partition]]
|
|
88
|
+
for axis_id in (partition, f"{partition}_repeat"):
|
|
89
|
+
hp.place_nodes_on_axis(
|
|
90
|
+
axis_id=axis_id,
|
|
91
|
+
node_df=partition_node_df,
|
|
92
|
+
sorting_feature_to_use=sorting_variable[partition],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# connect each main axis to its repeat (intra-partition edges).
|
|
96
|
+
# Use one direction only so each edge is filed exactly once.
|
|
97
|
+
for partition in base_angles:
|
|
98
|
+
hp.connect_axes(
|
|
99
|
+
edges=edges,
|
|
100
|
+
axis_id_1=partition,
|
|
101
|
+
axis_id_2=f"{partition}_repeat",
|
|
102
|
+
a2_to_a1=False,
|
|
103
|
+
warn_on_no_edges=False,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# connect consecutive partitions counterclockwise via repeat→next-main pairs.
|
|
107
|
+
cyclic_pairs = [
|
|
108
|
+
("Low_repeat", "Medium"),
|
|
109
|
+
("Medium_repeat", "High"),
|
|
110
|
+
("High_repeat", "Low"),
|
|
111
|
+
]
|
|
112
|
+
for axis_id_1, axis_id_2 in cyclic_pairs:
|
|
113
|
+
hp.connect_axes(
|
|
114
|
+
edges=edges,
|
|
115
|
+
axis_id_1=axis_id_1,
|
|
116
|
+
axis_id_2=axis_id_2,
|
|
117
|
+
warn_on_no_edges=False,
|
|
118
|
+
)
|
|
78
119
|
|
|
79
120
|
# set colors according to above-defined color dictionary
|
|
80
121
|
# (so we can replicate more easily in other viz later)
|
|
@@ -319,6 +360,7 @@ def example_nodes_and_edges(
|
|
|
319
360
|
def example_hive_plot(
|
|
320
361
|
num_nodes: int = 100,
|
|
321
362
|
num_edges: int = 100,
|
|
363
|
+
drop_duplicate_edges: bool = False,
|
|
322
364
|
partition_data_column: Literal["low", "med", "high"] = "low",
|
|
323
365
|
labels: Optional[List[Hashable]] = ["A", "B", "C"], # noqa: B006
|
|
324
366
|
cutoffs: Union[List[Union[int, float]], int] = 3,
|
|
@@ -346,6 +388,10 @@ def example_hive_plot(
|
|
|
346
388
|
:param num_nodes: how many nodes to randomly generate. Node unique IDs will be the integers 0, 1, ... ,
|
|
347
389
|
``num_nodes - 1``.
|
|
348
390
|
:param num_edges: how many example edges to randomly generate.
|
|
391
|
+
:param drop_duplicate_edges: whether to drop row-wise duplicate ``(from, to)`` edges from the randomly generated
|
|
392
|
+
edge set before building the hive plot. Default ``False`` keeps every generated edge. Setting ``True`` removes
|
|
393
|
+
same-direction duplicates, so the resulting hive plot may have fewer than ``num_edges`` edges when the random
|
|
394
|
+
generation produced repeats.
|
|
349
395
|
:param partition_data_column: which column of data in the underlying ``data`` attribute to use to partition the
|
|
350
396
|
node data. Node data generated via :py:func:`hiveplotlib.datasets.toy_hive_plots.example_node_data()`.
|
|
351
397
|
:param labels: labels assigned to each bin. Only referenced when ``cutoffs`` is not ``None``. ``None``
|
|
@@ -375,11 +421,14 @@ def example_hive_plot(
|
|
|
375
421
|
seed=seed,
|
|
376
422
|
unique_id_column=node_unique_id_column,
|
|
377
423
|
)
|
|
378
|
-
|
|
424
|
+
edge_df = example_edge_data(
|
|
379
425
|
nodes=node_collection,
|
|
380
426
|
num_edges=num_edges,
|
|
381
427
|
seed=seed,
|
|
382
428
|
)
|
|
429
|
+
if drop_duplicate_edges:
|
|
430
|
+
edge_df = edge_df.drop_duplicates(subset=["from", "to"], ignore_index=True)
|
|
431
|
+
edges = Edges(data=edge_df)
|
|
383
432
|
|
|
384
433
|
temp_df = (
|
|
385
434
|
edges._data[0]
|
|
@@ -418,6 +467,7 @@ def example_hpm_nodes_and_edges(
|
|
|
418
467
|
nodes_per_group: int = 10,
|
|
419
468
|
edge_tag_counts: Optional[Dict[str, int]] = None,
|
|
420
469
|
edge_structure: Optional[Dict[str, str]] = None,
|
|
470
|
+
drop_duplicate_edges: bool = False,
|
|
421
471
|
seed: int = 42,
|
|
422
472
|
) -> Tuple["NodeCollection", "Edges"]:
|
|
423
473
|
"""
|
|
@@ -458,6 +508,13 @@ def example_hpm_nodes_and_edges(
|
|
|
458
508
|
- ``"intergroup"``: edges connect nodes in different groups only.
|
|
459
509
|
|
|
460
510
|
Tags not listed in this dictionary default to ``"random"``.
|
|
511
|
+
:param drop_duplicate_edges: whether to drop same-direction duplicate ``(from, to)`` edges before building the
|
|
512
|
+
``Edges`` object. Default ``False`` keeps every randomly generated edge. Setting ``True`` deduplicates across
|
|
513
|
+
the *union* of all tags (keeping the first occurrence by tag insertion order, then row order), so an edge
|
|
514
|
+
appearing under multiple tags is retained only under the earliest tag. This mirrors how graph-metric
|
|
515
|
+
computation collapses parallel edges when ``graph_multigraph=False``, so the resulting dataset will not emit
|
|
516
|
+
duplicate-edge warnings when computing edge metrics. Tags may end up with fewer edges than requested in
|
|
517
|
+
``edge_tag_counts``.
|
|
461
518
|
:param seed: base random seed. Seeds ``seed``, ``seed+1``, ... are used for
|
|
462
519
|
successive edge tags respectively.
|
|
463
520
|
:return: tuple of ``(NodeCollection, Edges)``.
|
|
@@ -537,12 +594,26 @@ def example_hpm_nodes_and_edges(
|
|
|
537
594
|
{"from": from_nodes, "to": to_nodes},
|
|
538
595
|
)
|
|
539
596
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
597
|
+
edge_frames = {
|
|
598
|
+
tag: _make_edges(count, seed + i, edge_structure.get(tag, "random"))
|
|
599
|
+
for i, (tag, count) in enumerate(edge_tag_counts.items())
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if drop_duplicate_edges:
|
|
603
|
+
# dedup across the union of all tags, keeping the first occurrence by tag
|
|
604
|
+
# insertion order (then row order), matching the multigraph edge collapse.
|
|
605
|
+
combined = pd.concat(
|
|
606
|
+
[df.assign(_tag=tag) for tag, df in edge_frames.items()],
|
|
607
|
+
ignore_index=True,
|
|
608
|
+
).drop_duplicates(subset=["from", "to"], keep="first")
|
|
609
|
+
edge_frames = {
|
|
610
|
+
tag: combined.loc[combined["_tag"] == tag, ["from", "to"]].reset_index(
|
|
611
|
+
drop=True
|
|
612
|
+
)
|
|
613
|
+
for tag in edge_frames
|
|
544
614
|
}
|
|
545
|
-
|
|
615
|
+
|
|
616
|
+
edges = Edges(data=edge_frames)
|
|
546
617
|
return nodes, edges
|
|
547
618
|
|
|
548
619
|
|