d3graph 2.9.4__tar.gz → 2.9.6__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.
- {d3graph-2.9.4/d3graph.egg-info → d3graph-2.9.6}/PKG-INFO +1 -1
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/__init__.py +1 -1
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/d3graph.py +78 -11
- d3graph-2.9.6/d3graph/d3js/d3graphscript.js +1603 -0
- d3graph-2.9.6/d3graph/d3js/index.html.j2 +203 -0
- d3graph-2.9.6/d3graph/d3js/style.css +236 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/examples.py +11 -9
- {d3graph-2.9.4 → d3graph-2.9.6/d3graph.egg-info}/PKG-INFO +1 -1
- d3graph-2.9.4/d3graph/d3js/d3graphscript.js +0 -840
- d3graph-2.9.4/d3graph/d3js/index.html.j2 +0 -213
- d3graph-2.9.4/d3graph/d3js/style.css +0 -45
- {d3graph-2.9.4 → d3graph-2.9.6}/LICENSE +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/MANIFEST.in +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/README.md +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/d3js/d3.v3.js +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/examples_docs.py +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/examples_rankings.py +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph/examples_rankings_Pvalue.py +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph.egg-info/SOURCES.txt +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph.egg-info/dependency_links.txt +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph.egg-info/requires.txt +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/d3graph.egg-info/top_level.txt +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/pyproject.toml +0 -0
- {d3graph-2.9.4 → d3graph-2.9.6}/setup.cfg +0 -0
|
@@ -65,9 +65,9 @@ class d3graph:
|
|
|
65
65
|
def __init__(self,
|
|
66
66
|
collision: float = 0.5,
|
|
67
67
|
charge: int = 600,
|
|
68
|
+
link_tension: float = 1,
|
|
68
69
|
slider=None,
|
|
69
70
|
support: str = 'text',
|
|
70
|
-
link_tension: float = 1,
|
|
71
71
|
sticky: bool = True,
|
|
72
72
|
verbose: int = 20) -> None:
|
|
73
73
|
"""Initialize d3graph.
|
|
@@ -106,7 +106,7 @@ class d3graph:
|
|
|
106
106
|
if clean_config and hasattr(self, 'config'): del self.config
|
|
107
107
|
|
|
108
108
|
def show(self,
|
|
109
|
-
figsize=[
|
|
109
|
+
figsize=[None, None],
|
|
110
110
|
title: str = 'd3graph',
|
|
111
111
|
filepath: str = None,
|
|
112
112
|
showfig: bool = True,
|
|
@@ -115,7 +115,7 @@ class d3graph:
|
|
|
115
115
|
set_slider: bool = 0,
|
|
116
116
|
click={'fill': None, 'stroke': 'black', 'size': 1.3, 'stroke-width': 3},
|
|
117
117
|
background_color = '#FFFFFF',
|
|
118
|
-
dark_mode =
|
|
118
|
+
dark_mode = True,
|
|
119
119
|
notebook: bool = False,
|
|
120
120
|
save_button: bool = True,
|
|
121
121
|
link_tension: float = None,
|
|
@@ -126,15 +126,15 @@ class d3graph:
|
|
|
126
126
|
canvas_edge_threshold: int = 2000,
|
|
127
127
|
show_density: bool = False,
|
|
128
128
|
density_grid_size: int = 60,
|
|
129
|
-
density_blur: int =
|
|
130
|
-
density_opacity: float = 0.
|
|
129
|
+
density_blur: int = 15,
|
|
130
|
+
density_opacity: float = 0.8,
|
|
131
131
|
show_controls: bool = True,
|
|
132
132
|
) -> None:
|
|
133
133
|
"""Build and show the graph.
|
|
134
134
|
|
|
135
135
|
Parameters
|
|
136
136
|
----------
|
|
137
|
-
figsize : tuple, (default: (
|
|
137
|
+
figsize : tuple, (default: (None, None))
|
|
138
138
|
Size of the figure in the browser, (width, height).
|
|
139
139
|
(None, None): Use the screen resolution.
|
|
140
140
|
title : String, (default: None)
|
|
@@ -262,7 +262,7 @@ class d3graph:
|
|
|
262
262
|
# Make slider
|
|
263
263
|
self.setup_slider()
|
|
264
264
|
# Create json
|
|
265
|
-
json_data = json_create(self.G)
|
|
265
|
+
json_data = json_create(self.G, compute_stats=self.config.get('show_controls', True))
|
|
266
266
|
# Create html with json file embedded
|
|
267
267
|
html = self.write_html(json_data, overwrite=overwrite)
|
|
268
268
|
# Display the chart
|
|
@@ -848,8 +848,8 @@ class d3graph:
|
|
|
848
848
|
# Hide slider
|
|
849
849
|
show_slider = ['', ''] if self.config['show_slider'] else ['<!--', '-->']
|
|
850
850
|
# Set width and height to screen resolution if None.
|
|
851
|
-
width = 'window.
|
|
852
|
-
height = '
|
|
851
|
+
width = 'window.innerWidth' if self.config['figsize'][0] is None else self.config['figsize'][0]
|
|
852
|
+
height = 'null' if self.config['figsize'][1] is None else self.config['figsize'][1]
|
|
853
853
|
# Get support
|
|
854
854
|
support = self.config.get('support')
|
|
855
855
|
if support is None: support = get_support('text')
|
|
@@ -897,7 +897,6 @@ class d3graph:
|
|
|
897
897
|
html = index_template.render(content)
|
|
898
898
|
|
|
899
899
|
index_file = self.config['filepath']
|
|
900
|
-
# index_file.write_text(index_template.render(content))
|
|
901
900
|
if overwrite and index_file:
|
|
902
901
|
logger.info(f'Write to path: [{index_file.absolute()}]')
|
|
903
902
|
logger.info(f'File already exists and will be overwritten: [{index_file}]')
|
|
@@ -1049,13 +1048,20 @@ def check_logger(verbose: [str, int] = 'info'):
|
|
|
1049
1048
|
|
|
1050
1049
|
|
|
1051
1050
|
# %% Write network in json file
|
|
1052
|
-
def json_create(G: nx.Graph) -> str:
|
|
1051
|
+
def json_create(G: nx.Graph, compute_stats: bool = True) -> str:
|
|
1053
1052
|
"""Create json from Graph.
|
|
1054
1053
|
|
|
1055
1054
|
Parameters
|
|
1056
1055
|
----------
|
|
1057
1056
|
G : Networkx object
|
|
1058
1057
|
Graph G
|
|
1058
|
+
compute_stats : bool, (default: True)
|
|
1059
|
+
Compute PageRank, HITS (hub/authority), degree/closeness/betweenness centrality
|
|
1060
|
+
per node and embed them as node_pagerank / node_hits_hub / node_hits_authority /
|
|
1061
|
+
node_degree_centrality / node_closeness_centrality / node_betweenness_centrality,
|
|
1062
|
+
normalized to [0, 1], for the interactive stats side panel. Skipped when the
|
|
1063
|
+
panel won't be shown (show_controls=False) to avoid the wasted computation.
|
|
1064
|
+
Betweenness is approximated (sampled) above 500 nodes to bound generation time.
|
|
1059
1065
|
|
|
1060
1066
|
Returns
|
|
1061
1067
|
-------
|
|
@@ -1072,6 +1078,59 @@ def json_create(G: nx.Graph) -> str:
|
|
|
1072
1078
|
source.append(node_id[edge[0] == node_ui][0])
|
|
1073
1079
|
target.append(node_id[edge[1] == node_ui][0])
|
|
1074
1080
|
|
|
1081
|
+
# Common network statistics for the optional interactive stats panel
|
|
1082
|
+
# (recolors nodes by the selected metric). Computed once here, server-side,
|
|
1083
|
+
# rather than in the browser, since networkx already has these and it keeps
|
|
1084
|
+
# the JS side simple (just reads a precomputed per-node number).
|
|
1085
|
+
if compute_stats and len(node_ui) > 0:
|
|
1086
|
+
def _normalize_dict(d):
|
|
1087
|
+
vals = np.array(list(d.values()), dtype=float)
|
|
1088
|
+
vmin, vmax = vals.min(), vals.max()
|
|
1089
|
+
if (vmax - vmin) < 1e-12:
|
|
1090
|
+
return {k: 0.0 for k in d}
|
|
1091
|
+
return {k: float((v - vmin) / (vmax - vmin)) for k, v in d.items()}
|
|
1092
|
+
|
|
1093
|
+
try:
|
|
1094
|
+
pagerank_norm = _normalize_dict(nx.pagerank(G, weight='weight'))
|
|
1095
|
+
except Exception:
|
|
1096
|
+
pagerank_norm = {n: 0.0 for n in G.nodes()}
|
|
1097
|
+
|
|
1098
|
+
try:
|
|
1099
|
+
hubs, authorities = nx.hits(G, max_iter=1000)
|
|
1100
|
+
hubs_norm = _normalize_dict(hubs)
|
|
1101
|
+
authorities_norm = _normalize_dict(authorities)
|
|
1102
|
+
except Exception:
|
|
1103
|
+
hubs_norm = {n: 0.0 for n in G.nodes()}
|
|
1104
|
+
authorities_norm = {n: 0.0 for n in G.nodes()}
|
|
1105
|
+
|
|
1106
|
+
try:
|
|
1107
|
+
degree_norm = _normalize_dict(nx.degree_centrality(G))
|
|
1108
|
+
except Exception:
|
|
1109
|
+
degree_norm = {n: 0.0 for n in G.nodes()}
|
|
1110
|
+
|
|
1111
|
+
try:
|
|
1112
|
+
closeness_norm = _normalize_dict(nx.closeness_centrality(G))
|
|
1113
|
+
except Exception:
|
|
1114
|
+
closeness_norm = {n: 0.0 for n in G.nodes()}
|
|
1115
|
+
|
|
1116
|
+
try:
|
|
1117
|
+
# Betweenness is O(V*E) (Brandes) - exact for smaller graphs, but that
|
|
1118
|
+
# gets slow fast on larger ones. Above 500 nodes, approximate via a
|
|
1119
|
+
# fixed-seed sample of source nodes so generation time stays bounded.
|
|
1120
|
+
n_nodes = G.number_of_nodes()
|
|
1121
|
+
k = min(100, n_nodes) if n_nodes > 500 else None
|
|
1122
|
+
betweenness_norm = _normalize_dict(
|
|
1123
|
+
nx.betweenness_centrality(G, k=k, weight='weight', seed=42 if k else None))
|
|
1124
|
+
except Exception:
|
|
1125
|
+
betweenness_norm = {n: 0.0 for n in G.nodes()}
|
|
1126
|
+
else:
|
|
1127
|
+
pagerank_norm = {n: 0.0 for n in G.nodes()}
|
|
1128
|
+
hubs_norm = {n: 0.0 for n in G.nodes()}
|
|
1129
|
+
authorities_norm = {n: 0.0 for n in G.nodes()}
|
|
1130
|
+
degree_norm = {n: 0.0 for n in G.nodes()}
|
|
1131
|
+
closeness_norm = {n: 0.0 for n in G.nodes()}
|
|
1132
|
+
betweenness_norm = {n: 0.0 for n in G.nodes()}
|
|
1133
|
+
|
|
1075
1134
|
# Set edge properties
|
|
1076
1135
|
links = pd.DataFrame([*G.edges.values()]).T.to_dict()
|
|
1077
1136
|
links_new = []
|
|
@@ -1108,6 +1167,14 @@ def json_create(G: nx.Graph) -> str:
|
|
|
1108
1167
|
nodes[i]['node_color_edge'] = nodes[i].pop('edge_color')
|
|
1109
1168
|
nodes[i]['node_fontcolor'] = nodes[i].pop('fontcolor')
|
|
1110
1169
|
nodes[i]['node_fontsize'] = nodes[i].pop('fontsize')
|
|
1170
|
+
# Stats for the interactive side panel (see compute_stats above)
|
|
1171
|
+
nid = node_ui[i]
|
|
1172
|
+
nodes[i]['node_pagerank'] = pagerank_norm.get(nid, 0.0)
|
|
1173
|
+
nodes[i]['node_hits_hub'] = hubs_norm.get(nid, 0.0)
|
|
1174
|
+
nodes[i]['node_hits_authority'] = authorities_norm.get(nid, 0.0)
|
|
1175
|
+
nodes[i]['node_degree_centrality'] = degree_norm.get(nid, 0.0)
|
|
1176
|
+
nodes[i]['node_closeness_centrality'] = closeness_norm.get(nid, 0.0)
|
|
1177
|
+
nodes[i]['node_betweenness_centrality'] = betweenness_norm.get(nid, 0.0)
|
|
1111
1178
|
# Combine all information into new list
|
|
1112
1179
|
nodes_new[i] = nodes[i]
|
|
1113
1180
|
data = {'links': links_new, 'nodes': nodes_new}
|