d3graph 2.6.1__tar.gz → 2.7.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.
- {d3graph-2.6.1/d3graph.egg-info → d3graph-2.7.0}/PKG-INFO +8 -38
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/__init__.py +1 -2
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/d3graph.py +47 -18
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/d3js/d3graphscript.js +91 -67
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/d3js/index.html.j2 +2 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/d3js/style.css +6 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/examples.py +103 -14
- {d3graph-2.6.1 → d3graph-2.7.0/d3graph.egg-info}/PKG-INFO +8 -38
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph.egg-info/SOURCES.txt +1 -3
- {d3graph-2.6.1 → d3graph-2.7.0}/pyproject.toml +20 -3
- d3graph-2.7.0/setup.cfg +4 -0
- d3graph-2.6.1/setup.cfg +0 -10
- d3graph-2.6.1/tests/test_d3graph.py +0 -39
- {d3graph-2.6.1 → d3graph-2.7.0}/LICENSE +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/MANIFEST.in +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/README.md +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/d3js/d3.v3.js +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph/examples_docs.py +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph.egg-info/dependency_links.txt +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph.egg-info/requires.txt +0 -0
- {d3graph-2.6.1 → d3graph-2.7.0}/d3graph.egg-info/top_level.txt +0 -0
|
@@ -1,49 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: d3graph
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Python package to create interactive network based on d3js.
|
|
5
5
|
Author-email: Erdogan Taskesen <erdogant@gmail.com>
|
|
6
|
-
License:
|
|
7
|
-
|
|
8
|
-
::
|
|
9
|
-
|
|
10
|
-
Copyright (C) 2000-2022,
|
|
11
|
-
Erdogan Taskesen <erdogant@gmail.com>
|
|
12
|
-
All rights reserved.
|
|
13
|
-
|
|
14
|
-
Redistribution and use in source and binary forms, with or without
|
|
15
|
-
modification, are permitted provided that the following conditions are
|
|
16
|
-
met:
|
|
17
|
-
|
|
18
|
-
* Redistributions of source code must retain the above copyright
|
|
19
|
-
notice, this list of conditions and the following disclaimer.
|
|
20
|
-
|
|
21
|
-
* Redistributions in binary form must reproduce the above
|
|
22
|
-
copyright notice, this list of conditions and the following
|
|
23
|
-
disclaimer in the documentation and/or other materials provided
|
|
24
|
-
with the distribution.
|
|
25
|
-
|
|
26
|
-
* Neither the name of the d3graph nor the names of its
|
|
27
|
-
contributors may be used to endorse or promote products derived
|
|
28
|
-
from this software without specific prior written permission.
|
|
29
|
-
|
|
30
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
31
|
-
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
32
|
-
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
33
|
-
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
34
|
-
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
35
|
-
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
36
|
-
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
37
|
-
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
38
|
-
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
39
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
40
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
41
7
|
Project-URL: Homepage, https://erdogant.github.io/d3graph
|
|
42
8
|
Project-URL: Download, https://github.com/erdogant/d3graph/archive/{version}.tar.gz
|
|
43
|
-
Keywords: Python,
|
|
9
|
+
Keywords: Python,network,graph,network analysis,visualization,interactive,d3js,d3graph,data visualization,web,modular,customizable
|
|
44
10
|
Classifier: Programming Language :: Python :: 3
|
|
45
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
46
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: Unix
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
47
17
|
Requires-Python: >=3
|
|
48
18
|
Description-Content-Type: text/markdown
|
|
49
19
|
License-File: LICENSE
|
|
@@ -15,7 +15,7 @@ from d3graph.d3graph import (
|
|
|
15
15
|
|
|
16
16
|
__author__ = 'Erdogan Tasksen'
|
|
17
17
|
__email__ = 'erdogant@gmail.com'
|
|
18
|
-
__version__ = '2.
|
|
18
|
+
__version__ = '2.7.0'
|
|
19
19
|
|
|
20
20
|
# Setup root logger
|
|
21
21
|
_logger = logging.getLogger('d3graph')
|
|
@@ -63,7 +63,6 @@ Examples
|
|
|
63
63
|
References
|
|
64
64
|
----------
|
|
65
65
|
* D3Graph: erdogant.medium.com
|
|
66
|
-
* D3Blocks: https://medium.com/data-science-collective/d3blocks-the-python-library-to-create-interactive-standalone-and-beautiful-d3-js-charts-ef8c65286e86
|
|
67
66
|
* Github : https://github.com/erdogant/d3graph
|
|
68
67
|
* Documentation: https://erdogant.github.io/d3graph/
|
|
69
68
|
|
|
@@ -12,7 +12,7 @@ import webbrowser
|
|
|
12
12
|
from json import dumps
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from sys import platform
|
|
15
|
-
from tempfile import
|
|
15
|
+
from tempfile import gettempdir
|
|
16
16
|
from unicodedata import normalize
|
|
17
17
|
import uuid
|
|
18
18
|
|
|
@@ -27,9 +27,6 @@ from packaging import version
|
|
|
27
27
|
import datazets as dz
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
|
-
if not logger.hasHandlers():
|
|
31
|
-
logging.basicConfig(level=logging.INFO, format='[{asctime}] [{name}] [{levelname}] {msg}', style='{', datefmt='%d-%m-%Y %H:%M:%S')
|
|
32
|
-
|
|
33
30
|
|
|
34
31
|
# %%
|
|
35
32
|
class d3graph:
|
|
@@ -58,8 +55,7 @@ class d3graph:
|
|
|
58
55
|
|
|
59
56
|
References
|
|
60
57
|
----------
|
|
61
|
-
*
|
|
62
|
-
* D3Blocks: https://medium.com/data-science-collective/d3blocks-the-python-library-to-create-interactive-standalone-and-beautiful-d3-js-charts-ef8c65286e86
|
|
58
|
+
* Medium: https://erdogant.medium.com/
|
|
63
59
|
* Github : https://github.com/erdogant/d3graph
|
|
64
60
|
* Documentation: https://erdogant.github.io/d3graph/
|
|
65
61
|
|
|
@@ -70,8 +66,17 @@ class d3graph:
|
|
|
70
66
|
charge: int = 600,
|
|
71
67
|
slider=None,
|
|
72
68
|
support: str = 'text',
|
|
69
|
+
link_tension: float = 1,
|
|
70
|
+
sticky: bool = True,
|
|
73
71
|
verbose: int = 20) -> None:
|
|
74
|
-
"""Initialize d3graph.
|
|
72
|
+
"""Initialize d3graph.
|
|
73
|
+
|
|
74
|
+
Parameters (additional to existing)
|
|
75
|
+
------------------------------------
|
|
76
|
+
sticky : bool, (default: False)
|
|
77
|
+
When True, nodes stay fixed in place after being dragged.
|
|
78
|
+
Right-click a fixed node to release it back into the simulation.
|
|
79
|
+
"""
|
|
75
80
|
if slider is None: slider = [None, None]
|
|
76
81
|
# Cleaning
|
|
77
82
|
self._clean()
|
|
@@ -85,6 +90,8 @@ class d3graph:
|
|
|
85
90
|
self.config['charge'] = -abs(charge)
|
|
86
91
|
self.config['slider'] = slider
|
|
87
92
|
self.config['support'] = get_support(support)
|
|
93
|
+
self.config['link_tension'] = link_tension
|
|
94
|
+
self.config['sticky'] = sticky
|
|
88
95
|
# Set paths
|
|
89
96
|
self.config['curpath'] = os.path.dirname(os.path.abspath(__file__))
|
|
90
97
|
self.config['d3_library'] = os.path.abspath(os.path.join(self.config['curpath'], 'd3js/d3.v3.js'))
|
|
@@ -110,6 +117,8 @@ class d3graph:
|
|
|
110
117
|
dark_mode = False,
|
|
111
118
|
notebook: bool = False,
|
|
112
119
|
save_button: bool = True,
|
|
120
|
+
link_tension: float = None,
|
|
121
|
+
sticky: bool = None,
|
|
113
122
|
) -> None:
|
|
114
123
|
"""Build and show the graph.
|
|
115
124
|
|
|
@@ -150,6 +159,11 @@ class d3graph:
|
|
|
150
159
|
save_button : bool, (default: True)
|
|
151
160
|
True: Save button is shown in the HTML to save the image in svg.
|
|
152
161
|
False: No save button is shown in the HTML.
|
|
162
|
+
sticky : bool, (default: None)
|
|
163
|
+
When True, nodes stay fixed in place after being dragged (overrides the value set in __init__).
|
|
164
|
+
When False, nodes are released after dragging (default simulation behaviour).
|
|
165
|
+
When None, the value set in __init__ is used.
|
|
166
|
+
Right-click a fixed node to release it back into the simulation.
|
|
153
167
|
|
|
154
168
|
Returns
|
|
155
169
|
-------
|
|
@@ -171,6 +185,12 @@ class d3graph:
|
|
|
171
185
|
self.config['save_button'] = save_button
|
|
172
186
|
self.config['background_color'] = background_color
|
|
173
187
|
self.config['dark_mode'] = dark_mode
|
|
188
|
+
# Allow show() to override the link_tension set at __init__ time
|
|
189
|
+
if link_tension is not None:
|
|
190
|
+
self.config['link_tension'] = link_tension
|
|
191
|
+
# Override sticky only when explicitly passed
|
|
192
|
+
if sticky is not None:
|
|
193
|
+
self.config['sticky'] = sticky
|
|
174
194
|
# if self.config.get('filepath', None) != 'd3graph.html':
|
|
175
195
|
self.config['filepath'] = self.set_path(filepath)
|
|
176
196
|
|
|
@@ -308,6 +328,10 @@ class d3graph:
|
|
|
308
328
|
self.config['label'] = label
|
|
309
329
|
self.config['label_color'] = label_color
|
|
310
330
|
self.config['label_fontsize'] = label_fontsize
|
|
331
|
+
|
|
332
|
+
if not hasattr(self, 'adjmat'):
|
|
333
|
+
logger.error('adjmat is missing. Initialize first with d3 = d3graph(adjmat)')
|
|
334
|
+
return
|
|
311
335
|
|
|
312
336
|
if (not directed) and (marker_end is not None) or (marker_start is not None):
|
|
313
337
|
logger.info('Set directed=True to see the markers!')
|
|
@@ -447,6 +471,10 @@ class d3graph:
|
|
|
447
471
|
'edge_color': edge_color of the node
|
|
448
472
|
|
|
449
473
|
"""
|
|
474
|
+
if not hasattr(self, 'adjmat'):
|
|
475
|
+
logger.error('adjmat is missing. Initialize first with d3 = d3graph(adjmat)')
|
|
476
|
+
return
|
|
477
|
+
|
|
450
478
|
if minmax is None: minmax = [8, 13]
|
|
451
479
|
node_names = self.adjmat.columns.astype(str)
|
|
452
480
|
nodecount = self.adjmat.shape[0]
|
|
@@ -763,6 +791,8 @@ class d3graph:
|
|
|
763
791
|
'max_slider': self.config['slider'][1],
|
|
764
792
|
'directed': self.config['directed'],
|
|
765
793
|
'collision': self.config['collision'],
|
|
794
|
+
'link_tension': self.config.get('link_tension', 1.0),
|
|
795
|
+
'sticky': self.config.get('sticky', False),
|
|
766
796
|
'CLICK_COMMENT': CLICK_COMMENT,
|
|
767
797
|
'CLICK_FILL': click_properties['fill'],
|
|
768
798
|
'CLICK_STROKE': click_properties['stroke'],
|
|
@@ -823,10 +853,10 @@ class d3graph:
|
|
|
823
853
|
dirname = os.path.join(gettempdir(), 'd3graph')
|
|
824
854
|
else:
|
|
825
855
|
dirname, filename = os.path.split(filepath)
|
|
826
|
-
|
|
856
|
+
|
|
827
857
|
if filename in (None, ''):
|
|
828
858
|
filename = 'd3graph.html'
|
|
829
|
-
|
|
859
|
+
|
|
830
860
|
if dirname in (None, ''):
|
|
831
861
|
dirname = os.path.join(gettempdir(), 'd3graph')
|
|
832
862
|
|
|
@@ -1009,6 +1039,7 @@ def json_create(G: nx.Graph) -> str:
|
|
|
1009
1039
|
links[i]['label'] = links[i]['label']
|
|
1010
1040
|
links[i]['label_color'] = links[i]['label_color']
|
|
1011
1041
|
links[i]['label_fontsize'] = links[i]['label_fontsize']
|
|
1042
|
+
links[i]['tooltip'] = links[i]['tooltip']
|
|
1012
1043
|
links_new.append(links[i])
|
|
1013
1044
|
|
|
1014
1045
|
# Set node properties
|
|
@@ -1125,6 +1156,7 @@ def adjmat2dict(adjmat: pd.DataFrame,
|
|
|
1125
1156
|
'label': Text label for the edge.
|
|
1126
1157
|
'label_color': color of the label.
|
|
1127
1158
|
'label_fontsize': fontsize of the label.
|
|
1159
|
+
'tooltip': Text that is shown when hovering over the edge.
|
|
1128
1160
|
|
|
1129
1161
|
"""
|
|
1130
1162
|
# Convert adjacency matrix into vector
|
|
@@ -1191,16 +1223,10 @@ def adjmat2dict(adjmat: pd.DataFrame,
|
|
|
1191
1223
|
df['label_color']=label_color
|
|
1192
1224
|
df['label_fontsize']=label_fontsize
|
|
1193
1225
|
df['edge_style']=edge_style
|
|
1226
|
+
df['edge_color']=edge_color
|
|
1194
1227
|
df['edge_opacity'] = edge_opacity
|
|
1228
|
+
df['tooltip'] = df['weight'].astype(str)
|
|
1195
1229
|
|
|
1196
|
-
# Handle node_source edge color
|
|
1197
|
-
if edge_color == 'node_source' and node_color_map is not None:
|
|
1198
|
-
df['edge_color'] = df['source'].map(node_color_map)
|
|
1199
|
-
elif edge_color == 'node_target' and node_color_map is not None:
|
|
1200
|
-
df['edge_color'] = df['target'].map(node_color_map)
|
|
1201
|
-
else:
|
|
1202
|
-
df['edge_color'] = edge_color
|
|
1203
|
-
|
|
1204
1230
|
# Creation dictionary
|
|
1205
1231
|
source_target = list(zip(df['source'], df['target']))
|
|
1206
1232
|
if len(source_target)==0:
|
|
@@ -1218,6 +1244,7 @@ def adjmat2dict(adjmat: pd.DataFrame,
|
|
|
1218
1244
|
'label': df['label'].iloc[i],
|
|
1219
1245
|
'label_color': df['label_color'].iloc[i],
|
|
1220
1246
|
'label_fontsize': df['label_fontsize'].iloc[i],
|
|
1247
|
+
'tooltip': df['tooltip'].iloc[i]
|
|
1221
1248
|
} for
|
|
1222
1249
|
i, edge in enumerate(source_target)}
|
|
1223
1250
|
|
|
@@ -1294,6 +1321,7 @@ def edges2G(edge_properties: dict, G: nx.Graph = None) -> nx.Graph:
|
|
|
1294
1321
|
label=edge_properties[edge]['label'],
|
|
1295
1322
|
label_color=edge_properties[edge]['label_color'],
|
|
1296
1323
|
label_fontsize=edge_properties[edge]['label_fontsize'],
|
|
1324
|
+
tooltip=edge_properties[edge]['tooltip']
|
|
1297
1325
|
)
|
|
1298
1326
|
return G
|
|
1299
1327
|
|
|
@@ -1360,6 +1388,7 @@ def _normalize_size(getsizes, minscale=0.5, maxscale=4, scaler: str = 'zscore'):
|
|
|
1360
1388
|
# Instead of Min-Max scaling, that shrinks any distribution in the [0, 1] interval, scaling the variables to
|
|
1361
1389
|
# Z-scores is better. Min-Max Scaling is too sensitive to outlier observations and generates unseen problems.
|
|
1362
1390
|
|
|
1391
|
+
getsizes = getsizes.copy()
|
|
1363
1392
|
# Set sizes to 0 if not available
|
|
1364
1393
|
getsizes[np.isinf(getsizes)]=0
|
|
1365
1394
|
getsizes[np.isnan(getsizes)]=0
|
|
@@ -1737,4 +1766,4 @@ def get_support(support):
|
|
|
1737
1766
|
script = script + '\n' + "<div data-ea-publisher='erdogantgithubio' data-ea-type='{TYPE}' data-ea-style='stickybox'></div>".replace('{TYPE}', support)
|
|
1738
1767
|
|
|
1739
1768
|
# Return
|
|
1740
|
-
return script
|
|
1769
|
+
return script
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
function d3graphscript(config = {
|
|
3
2
|
// Default values
|
|
4
3
|
width: 800,
|
|
@@ -7,6 +6,8 @@ function d3graphscript(config = {
|
|
|
7
6
|
distance: 0,
|
|
8
7
|
directed: false,
|
|
9
8
|
collision: 0.5,
|
|
9
|
+
link_tension: 1,
|
|
10
|
+
sticky: false,
|
|
10
11
|
background_color: '#FFFFFF'
|
|
11
12
|
}) {
|
|
12
13
|
|
|
@@ -14,6 +15,7 @@ function d3graphscript(config = {
|
|
|
14
15
|
var width = config.width;
|
|
15
16
|
var height = config.height;
|
|
16
17
|
var background_color = config.background_color || '#FFFFFF';
|
|
18
|
+
var sticky = config.sticky || false;
|
|
17
19
|
|
|
18
20
|
// Set the body background color
|
|
19
21
|
document.body.style.backgroundColor = background_color;
|
|
@@ -25,20 +27,42 @@ function d3graphscript(config = {
|
|
|
25
27
|
.charge(config.charge)
|
|
26
28
|
.linkDistance((d) => d.edge_distance || config.distance)
|
|
27
29
|
//.linkDistance((d) => config.distance > 0 ? config.distance : d.edge_weight)
|
|
30
|
+
.linkStrength(config.link_tension !== undefined ? config.link_tension : 1)
|
|
28
31
|
.size([width, height]);
|
|
29
32
|
|
|
30
|
-
// DRAGGING
|
|
33
|
+
// ---- DRAGGING ----
|
|
34
|
+
// Sticky mode: dragstart fixes the node so the simulation stops pulling it.
|
|
35
|
+
// dragend keeps it pinned (dashed stroke indicator).
|
|
36
|
+
// Right-click a pinned node to release it back into the simulation.
|
|
37
|
+
// Normal mode: standard free-drag behaviour is preserved.
|
|
38
|
+
|
|
31
39
|
function dragstarted(d) {
|
|
32
40
|
d3.event.sourceEvent.stopPropagation();
|
|
33
41
|
d3.select(this).classed("dragging", true);
|
|
42
|
+
if (sticky) {
|
|
43
|
+
d.fixed = true;
|
|
44
|
+
force.start();
|
|
45
|
+
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
function dragged(d) {
|
|
37
|
-
|
|
49
|
+
if (sticky) {
|
|
50
|
+
d.x = d.px = d3.event.x;
|
|
51
|
+
d.y = d.py = d3.event.y;
|
|
52
|
+
} else {
|
|
53
|
+
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
|
|
54
|
+
}
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
function dragended(d) {
|
|
41
58
|
d3.select(this).classed("dragging", false);
|
|
59
|
+
if (sticky) {
|
|
60
|
+
// Keep the node fixed and apply a visual "pinned" cue (dashed border)
|
|
61
|
+
d.fixed = true;
|
|
62
|
+
d3.select(this).select("circle")
|
|
63
|
+
.style("stroke-dasharray", "4,2")
|
|
64
|
+
.style("stroke-width", function(d) { return Math.max(parseFloat(d.node_size_edge) || 1, 2); });
|
|
65
|
+
}
|
|
42
66
|
}
|
|
43
67
|
|
|
44
68
|
var drag = force.drag()
|
|
@@ -47,7 +71,7 @@ function d3graphscript(config = {
|
|
|
47
71
|
.on("drag", dragged)
|
|
48
72
|
.on("dragend", dragended);
|
|
49
73
|
|
|
50
|
-
// DRAGGING
|
|
74
|
+
// ---- END DRAGGING ----
|
|
51
75
|
|
|
52
76
|
//Append a SVG to the body of the html page. Assign this SVG as an object to svg
|
|
53
77
|
var svg = d3.select("body").append("svg")
|
|
@@ -58,8 +82,6 @@ function d3graphscript(config = {
|
|
|
58
82
|
.on("dblclick.zoom", null)
|
|
59
83
|
.append("g")
|
|
60
84
|
|
|
61
|
-
//.on("dblclick", threshold); // EXPLODE ALL CONNECTED POINTS
|
|
62
|
-
|
|
63
85
|
graphRec = JSON.parse(JSON.stringify(graph));
|
|
64
86
|
|
|
65
87
|
//Creates the graph data structure out of the json data
|
|
@@ -67,31 +89,31 @@ function d3graphscript(config = {
|
|
|
67
89
|
.links(graph.links)
|
|
68
90
|
.start();
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
// Create all the line svgs but without locations yet
|
|
71
93
|
var link = svg.selectAll(".link")
|
|
72
94
|
.data(graph.links)
|
|
73
95
|
.enter().append("line")
|
|
74
96
|
.attr("class", "link")
|
|
75
97
|
.attr('marker-start', function(d){ return 'url(#marker_' + d.marker_start + ')' })
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
.attr("marker-end", function(d) {
|
|
99
|
+
if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
|
|
78
100
|
.style("stroke-width", function(d) {return d.edge_width;}) // LINK-WIDTH
|
|
79
101
|
.style("stroke", function(d) {return d.edge_color;}) // EDGE-COLORS
|
|
80
102
|
.style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
|
|
81
103
|
.style("opacity", function(d) {return d.edge_opacity;}) // EDGE-OPACITY
|
|
82
|
-
// .style("stroke-width", 1); // WIDTH OF THE LINKS
|
|
83
104
|
;
|
|
84
105
|
|
|
106
|
+
link.append("title").text(function(d) { return d.tooltip; });
|
|
107
|
+
|
|
85
108
|
// ADD TEXT ON THE EDGES (PART 1/2)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.text(function(d) { return d.label; });
|
|
109
|
+
var linkText = svg.selectAll(".link-text")
|
|
110
|
+
.data(graph.links)
|
|
111
|
+
.enter().append("text")
|
|
112
|
+
.attr("class", "link-text")
|
|
113
|
+
.attr("font-size", function(d) {return d.label_fontsize + "px";})
|
|
114
|
+
.style("fill", function(d) {return d.label_color;})
|
|
115
|
+
.style("font-family", "Arial")
|
|
116
|
+
.text(function(d) { return d.label; });
|
|
95
117
|
|
|
96
118
|
//Do the same with the circles for the nodes
|
|
97
119
|
var node = svg.selectAll(".node")
|
|
@@ -101,26 +123,37 @@ function d3graphscript(config = {
|
|
|
101
123
|
.call(drag)
|
|
102
124
|
.on('dblclick', connectedNodes); // HIGHLIGHT ON/OFF
|
|
103
125
|
|
|
126
|
+
// Right-click handler: release a pinned node back into the simulation (sticky mode only)
|
|
127
|
+
if (sticky) {
|
|
128
|
+
node.on('contextmenu', function(d) {
|
|
129
|
+
d3.event.preventDefault();
|
|
130
|
+
d.fixed = false;
|
|
131
|
+
// Remove pinned visual cue
|
|
132
|
+
d3.select(this).select("circle")
|
|
133
|
+
.style("stroke-dasharray", null)
|
|
134
|
+
.style("stroke-width", function(d) { return d.node_size_edge; });
|
|
135
|
+
force.resume();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
104
139
|
{{ CLICK_COMMENT }} node.on('click', color_on_click); // ON CLICK HANDLER
|
|
105
140
|
|
|
106
141
|
|
|
107
142
|
node.append("circle")
|
|
108
|
-
.attr("r", function(d) { return d.node_size; })
|
|
109
|
-
.style("fill", function(d) {return d.node_color;})
|
|
110
|
-
.style("opacity", function(d) {return d.node_opacity;})
|
|
111
|
-
.style("stroke-width", function(d) {return d.node_size_edge;})
|
|
112
|
-
.style("stroke", function(d) {return d.node_color_edge;})
|
|
113
|
-
// .style("stroke", '#000') // NODE-EDGE-COLOR (all black)
|
|
143
|
+
.attr("r", function(d) { return d.node_size; }) // NODE SIZE
|
|
144
|
+
.style("fill", function(d) {return d.node_color;}) // NODE-COLOR
|
|
145
|
+
.style("opacity", function(d) {return d.node_opacity;}) // NODE-OPACITY
|
|
146
|
+
.style("stroke-width", function(d) {return d.node_size_edge;}) // NODE-EDGE-SIZE
|
|
147
|
+
.style("stroke", function(d) {return d.node_color_edge;}) // NODE-COLOR-EDGE
|
|
114
148
|
|
|
115
149
|
// Text in nodes
|
|
116
150
|
node.append("text")
|
|
117
151
|
.attr("dx", 10)
|
|
118
152
|
.attr("dy", ".35em")
|
|
119
|
-
.text(function(d) {return d.node_name})
|
|
120
|
-
.style("font-size", function(d) {return d.node_fontsize + "px";})
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// .style("stroke", "gray");
|
|
153
|
+
.text(function(d) {return d.node_name}) // NODE-TEXT
|
|
154
|
+
.style("font-size", function(d) {return d.node_fontsize + "px";}) // NODE FONT SIZE
|
|
155
|
+
.style("fill", function(d) {return d.node_fontcolor;}) // NODE FONT COLOR
|
|
156
|
+
.style("font-family", "monospace");
|
|
124
157
|
|
|
125
158
|
let showInHover = ["node_tooltip"]; // Tooltip
|
|
126
159
|
node.append("title")
|
|
@@ -140,10 +173,9 @@ function d3graphscript(config = {
|
|
|
140
173
|
.attr("cy", function(d) { return d.y; });
|
|
141
174
|
d3.selectAll("text").attr("x", function(d) { return d.x; })
|
|
142
175
|
.attr("y", function(d) { return d.y; })
|
|
143
|
-
|
|
176
|
+
linkText.attr("x", function(d) { return (d.source.x + d.target.x) / 2; }) // ADD TEXT ON THE EDGES (PART 2/2)
|
|
144
177
|
.attr("y", function(d) { return (d.source.y + d.target.y) / 2; })
|
|
145
178
|
.attr("text-anchor", "middle");
|
|
146
|
-
;
|
|
147
179
|
|
|
148
180
|
node.each(collide(config.collision)); //COLLISION DETECTION. High means a big fight to get untouchable nodes (default=0.5)
|
|
149
181
|
|
|
@@ -158,8 +190,6 @@ function d3graphscript(config = {
|
|
|
158
190
|
, { id: 3, name: 'stub', path: 'M 0,0 m -1,-5 L 1,-5 L 1,5 L -1,5 Z', viewbox: '-1 -5 2 10' }
|
|
159
191
|
]
|
|
160
192
|
|
|
161
|
-
//console.log(JSON.stringify(link))
|
|
162
|
-
|
|
163
193
|
svg.append("defs").selectAll("marker")
|
|
164
194
|
.data(data_marker)
|
|
165
195
|
.enter()
|
|
@@ -167,17 +197,13 @@ function d3graphscript(config = {
|
|
|
167
197
|
.attr('id', function(d){ return 'marker_' + d.name})
|
|
168
198
|
.attr('markerHeight', 10)
|
|
169
199
|
.attr('markerWidth', 10)
|
|
170
|
-
//.attr('markerUnits', 'strokeWidth')
|
|
171
200
|
.attr("markerUnits", "userSpaceOnUse") // Fix marker width
|
|
172
201
|
.attr('orient', 'auto')
|
|
173
|
-
//.attr('refX', -15) // Offset marker-start
|
|
174
202
|
.attr('refX', 15) // Offset marker-end
|
|
175
203
|
.attr('refY', 0)
|
|
176
204
|
.attr('viewBox', function(d){ return d.viewbox })
|
|
177
205
|
.append('svg:path')
|
|
178
|
-
//.attr("transform", "rotate(180)") // Marker-start mirrored
|
|
179
206
|
.attr('d', function(d){ return d.path }) // Marker type
|
|
180
|
-
//.style("fill", function(d) {return d.marker_color;}) // Marker color
|
|
181
207
|
.style("fill", '#808080') // Marker color
|
|
182
208
|
.style("stroke", '#808080') // Marker edge-color
|
|
183
209
|
.style("opacity", 0.95) // Marker opacity
|
|
@@ -219,7 +245,7 @@ function d3graphscript(config = {
|
|
|
219
245
|
// collision detection end
|
|
220
246
|
|
|
221
247
|
|
|
222
|
-
//Toggle stores whether the highlighting is on
|
|
248
|
+
//Toggle stores whether the highlighting is on
|
|
223
249
|
var toggle = 0;
|
|
224
250
|
//Create an array logging what is connected to what
|
|
225
251
|
var linkedByIndex = {};
|
|
@@ -235,25 +261,27 @@ function d3graphscript(config = {
|
|
|
235
261
|
}
|
|
236
262
|
|
|
237
263
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
264
|
+
// COLOR ON CLICK
|
|
265
|
+
function color_on_click() {
|
|
266
|
+
// Give the original color back to all nodes
|
|
267
|
+
d3.selectAll(".node")
|
|
268
|
+
.select("circle")
|
|
269
|
+
.style("fill", function(d) {return d.node_color;})
|
|
270
|
+
.style("opacity", function(d) {return d.node_opacity;})
|
|
271
|
+
.style("stroke", function(d) {return d.node_color_edge;})
|
|
272
|
+
.style("stroke-width", function(d) {return d.node_size_edge;})
|
|
273
|
+
// Restore pinned cue on still-fixed nodes
|
|
274
|
+
.style("stroke-dasharray", function(d) { return (sticky && d.fixed) ? "4,2" : null; })
|
|
275
|
+
.attr("r", function(d) { return d.node_size; })
|
|
276
|
+
;
|
|
249
277
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
278
|
+
// Set the color on the clicked node
|
|
279
|
+
d3.select(this).select("circle")
|
|
280
|
+
.style("fill", {{ CLICK_FILL }})
|
|
281
|
+
.style("stroke", "{{ CLICK_STROKE }}")
|
|
282
|
+
.style("stroke-width", {{ CLICK_STROKEW }})
|
|
283
|
+
.attr("r", function(d) { return d.node_size*{{ CLICK_SIZE }}; })
|
|
284
|
+
;}
|
|
257
285
|
|
|
258
286
|
|
|
259
287
|
|
|
@@ -267,7 +295,6 @@ function d3graphscript(config = {
|
|
|
267
295
|
link.style("opacity", function(o) {
|
|
268
296
|
return d.index == o.source.index | d.index == o.target.index ? 1 : 0.1;
|
|
269
297
|
});
|
|
270
|
-
//Reduce the op
|
|
271
298
|
toggle = 1;
|
|
272
299
|
} else {
|
|
273
300
|
//Put them back to opacity=1
|
|
@@ -277,14 +304,12 @@ function d3graphscript(config = {
|
|
|
277
304
|
toggle = 0;
|
|
278
305
|
}
|
|
279
306
|
}
|
|
280
|
-
//*************************************************************
|
|
281
307
|
|
|
282
308
|
|
|
283
309
|
//adjust threshold
|
|
284
310
|
function threshold() {
|
|
285
311
|
let thresh = this.value;
|
|
286
312
|
|
|
287
|
-
// console.log('Setting threshold', thresh)
|
|
288
313
|
graph.links.splice(0, graph.links.length);
|
|
289
314
|
linkText = linkText.data([]); // CLEAR EDGE-LABELS: Clear the linkText selection
|
|
290
315
|
linkText.exit().remove(); // CLEAR EDGE-LABELS: Clear the linkText elements from the DOM
|
|
@@ -312,12 +337,11 @@ function d3graphscript(config = {
|
|
|
312
337
|
link.exit().remove();
|
|
313
338
|
link.enter().insert("line", ".node").attr("class", "link");
|
|
314
339
|
link.style("stroke-width", function(d) {return d.edge_width;}); // LINK-WIDTH AFTER BREAKING WITH SLIDER
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
link.style("stroke", function(d) {return d.
|
|
319
|
-
link.style("
|
|
320
|
-
link.style("opacity", function(d) {return d.edge_opacity;}); // EDGE-OPACITY AFTER BREAKING WITH SLIDER
|
|
340
|
+
link.style("marker-end", function(d) { // Include the markers.
|
|
341
|
+
if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
|
|
342
|
+
link.style("stroke", function(d) {return d.edge_color;}); // EDGE-COLOR AFTER BREAKING WITH SLIDER
|
|
343
|
+
link.style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
|
|
344
|
+
link.style("opacity", function(d) {return d.edge_opacity;}); // EDGE-OPACITY AFTER BREAKING WITH SLIDER
|
|
321
345
|
|
|
322
346
|
// Update EDGE-LABELS
|
|
323
347
|
linkText = linkText.data(graph.links);
|
|
@@ -334,4 +358,4 @@ function d3graphscript(config = {
|
|
|
334
358
|
force.start();
|
|
335
359
|
}
|
|
336
360
|
|
|
337
|
-
}
|
|
361
|
+
}
|
|
@@ -1,6 +1,108 @@
|
|
|
1
1
|
# Import library
|
|
2
2
|
from d3graph import d3graph, vec2adjmat
|
|
3
3
|
|
|
4
|
+
# Initialize with default settings
|
|
5
|
+
d3 = d3graph(link_tension=0.2)
|
|
6
|
+
|
|
7
|
+
# Load example data
|
|
8
|
+
df = d3.import_example('stormofswords')
|
|
9
|
+
adjmat = vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# sticky=True (default) — drag to pin, right-click to release
|
|
13
|
+
d3 = d3graph(adjmat, sticky=True)
|
|
14
|
+
d3.show()
|
|
15
|
+
|
|
16
|
+
# sticky=False — classic spring-back behaviour
|
|
17
|
+
d3 = d3graph(adjmat, sticky=False)
|
|
18
|
+
d3.show()
|
|
19
|
+
|
|
20
|
+
# Can also be overridden per-render:
|
|
21
|
+
d3.show(adjmat, sticky=False)
|
|
22
|
+
d3.show()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# %%
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Import library
|
|
29
|
+
from d3graph import d3graph, vec2adjmat
|
|
30
|
+
|
|
31
|
+
# Initialize with default settings
|
|
32
|
+
d3 = d3graph(sticky=True, link_tension=0.2, collision=1)
|
|
33
|
+
|
|
34
|
+
# Load example data
|
|
35
|
+
df = d3.import_example('stormofswords')
|
|
36
|
+
|
|
37
|
+
# Convert df to adjmat
|
|
38
|
+
adjmat = vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
|
|
39
|
+
# adjmat = np.exp(adjmat)
|
|
40
|
+
# adjmat = adjmat-1
|
|
41
|
+
|
|
42
|
+
# Create the network
|
|
43
|
+
d3.graph(adjmat)
|
|
44
|
+
|
|
45
|
+
# Set Node properties
|
|
46
|
+
d3.set_node_properties(edge_color='#000000', cmap='Blues', minmax=[5, 13], fontcolor='#808080')
|
|
47
|
+
# Show the graph
|
|
48
|
+
# d3.show()
|
|
49
|
+
|
|
50
|
+
# d3.set_edge_properties(directed=False, marker_end='arrow', edge_color='#ff0000', edge_opacity=[0.1, 0.2])
|
|
51
|
+
|
|
52
|
+
# Set edge properties
|
|
53
|
+
d3.set_edge_properties(directed=True, marker_end='arrow')
|
|
54
|
+
d3.show()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Import library
|
|
60
|
+
# from d3graph import d3graph, vec2adjmat
|
|
61
|
+
|
|
62
|
+
# Initialize with default settings
|
|
63
|
+
# d3 = d3graph(support=None)
|
|
64
|
+
|
|
65
|
+
# Load example data
|
|
66
|
+
# df = d3.import_example('stormofswords')
|
|
67
|
+
|
|
68
|
+
# Convert df to adjmat
|
|
69
|
+
# adjmat = vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
|
|
70
|
+
# adjmat = np.exp(adjmat)
|
|
71
|
+
# adjmat = adjmat-1
|
|
72
|
+
|
|
73
|
+
# Create the network
|
|
74
|
+
# d3.graph(adjmat)
|
|
75
|
+
|
|
76
|
+
# # Set Node properties
|
|
77
|
+
# # d3.set_node_properties(edge_color='#000000', cmap='Blues', minmax=[5, 13], fontcolor='#808080')
|
|
78
|
+
# d3.set_edge_properties(edge_color='#FF0000')
|
|
79
|
+
|
|
80
|
+
# # Show the graph
|
|
81
|
+
# d3.show()
|
|
82
|
+
|
|
83
|
+
# # %%
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# %% opacity
|
|
87
|
+
from d3graph import d3graph, adjmat2vec, vec2adjmat
|
|
88
|
+
# intialize to load example dataset
|
|
89
|
+
d3 = d3graph()
|
|
90
|
+
#
|
|
91
|
+
df = d3.import_example(data='energy')
|
|
92
|
+
df=vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
|
|
93
|
+
# d3.graph(df, size='degree', opacity='degree', color='cluster', scaler='zscore')
|
|
94
|
+
d3.graph(df)
|
|
95
|
+
|
|
96
|
+
# d3.set_node_properties(opacity='centrality')
|
|
97
|
+
d3.show(filepath='c://temp/network.html')
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# %%
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Import library
|
|
104
|
+
from d3graph import d3graph, vec2adjmat
|
|
105
|
+
|
|
4
106
|
# Initialize with default settings
|
|
5
107
|
d3 = d3graph(support=None)
|
|
6
108
|
|
|
@@ -74,7 +176,7 @@ df_edges_c['isin']=0
|
|
|
74
176
|
|
|
75
177
|
for index, edge in df_edges.iterrows():
|
|
76
178
|
Iloc = np.sum(edge==df_edges_c[df_edges.columns], axis=1)==len(df_edges.columns)
|
|
77
|
-
df_edges_c['isin']
|
|
179
|
+
df_edges_c.loc[Iloc, 'isin'] = 1
|
|
78
180
|
|
|
79
181
|
assert df_edges_c['isin'].sum()==df_edges.shape[0]
|
|
80
182
|
|
|
@@ -162,19 +264,6 @@ d3.set_node_properties(marker=['circle', 'circle', 'circle', 'rect', 'rect', 're
|
|
|
162
264
|
d3.show(filepath=r'c:\temp\\d3graph\circle.html', set_slider=5, save_button=False)
|
|
163
265
|
|
|
164
266
|
|
|
165
|
-
# %% opacity
|
|
166
|
-
from d3graph import d3graph, adjmat2vec
|
|
167
|
-
# intialize to load example dataset
|
|
168
|
-
d3 = d3graph()
|
|
169
|
-
#
|
|
170
|
-
df = d3.import_example(data='energy')
|
|
171
|
-
df=vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
|
|
172
|
-
# d3.graph(df, size='degree', opacity='degree', color='cluster', scaler='zscore')
|
|
173
|
-
d3.graph(df)
|
|
174
|
-
|
|
175
|
-
# d3.set_node_properties(opacity='centrality')
|
|
176
|
-
d3.show(filepath='c://temp/network.html')
|
|
177
|
-
|
|
178
267
|
|
|
179
268
|
# %% Edge distance
|
|
180
269
|
|
|
@@ -1,49 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: d3graph
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Python package to create interactive network based on d3js.
|
|
5
5
|
Author-email: Erdogan Taskesen <erdogant@gmail.com>
|
|
6
|
-
License:
|
|
7
|
-
|
|
8
|
-
::
|
|
9
|
-
|
|
10
|
-
Copyright (C) 2000-2022,
|
|
11
|
-
Erdogan Taskesen <erdogant@gmail.com>
|
|
12
|
-
All rights reserved.
|
|
13
|
-
|
|
14
|
-
Redistribution and use in source and binary forms, with or without
|
|
15
|
-
modification, are permitted provided that the following conditions are
|
|
16
|
-
met:
|
|
17
|
-
|
|
18
|
-
* Redistributions of source code must retain the above copyright
|
|
19
|
-
notice, this list of conditions and the following disclaimer.
|
|
20
|
-
|
|
21
|
-
* Redistributions in binary form must reproduce the above
|
|
22
|
-
copyright notice, this list of conditions and the following
|
|
23
|
-
disclaimer in the documentation and/or other materials provided
|
|
24
|
-
with the distribution.
|
|
25
|
-
|
|
26
|
-
* Neither the name of the d3graph nor the names of its
|
|
27
|
-
contributors may be used to endorse or promote products derived
|
|
28
|
-
from this software without specific prior written permission.
|
|
29
|
-
|
|
30
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
31
|
-
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
32
|
-
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
33
|
-
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
34
|
-
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
35
|
-
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
36
|
-
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
37
|
-
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
38
|
-
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
39
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
40
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
41
7
|
Project-URL: Homepage, https://erdogant.github.io/d3graph
|
|
42
8
|
Project-URL: Download, https://github.com/erdogant/d3graph/archive/{version}.tar.gz
|
|
43
|
-
Keywords: Python,
|
|
9
|
+
Keywords: Python,network,graph,network analysis,visualization,interactive,d3js,d3graph,data visualization,web,modular,customizable
|
|
44
10
|
Classifier: Programming Language :: Python :: 3
|
|
45
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
46
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: Unix
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
47
17
|
Requires-Python: >=3
|
|
48
18
|
Description-Content-Type: text/markdown
|
|
49
19
|
License-File: LICENSE
|
|
@@ -2,7 +2,6 @@ LICENSE
|
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
4
|
pyproject.toml
|
|
5
|
-
setup.cfg
|
|
6
5
|
d3graph/__init__.py
|
|
7
6
|
d3graph/d3graph.py
|
|
8
7
|
d3graph/examples.py
|
|
@@ -15,5 +14,4 @@ d3graph.egg-info/top_level.txt
|
|
|
15
14
|
d3graph/d3js/d3.v3.js
|
|
16
15
|
d3graph/d3js/d3graphscript.js
|
|
17
16
|
d3graph/d3js/index.html.j2
|
|
18
|
-
d3graph/d3js/style.css
|
|
19
|
-
tests/test_d3graph.py
|
|
17
|
+
d3graph/d3js/style.css
|
|
@@ -9,12 +9,29 @@ authors = [{ name = "Erdogan Taskesen", email = "erdogant@gmail.com" },]
|
|
|
9
9
|
description = "Python package to create interactive network based on d3js."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3"
|
|
12
|
-
license =
|
|
13
|
-
keywords = [
|
|
12
|
+
license = "BSD-3-Clause"
|
|
13
|
+
keywords = [
|
|
14
|
+
"Python",
|
|
15
|
+
"network",
|
|
16
|
+
"graph",
|
|
17
|
+
"network analysis",
|
|
18
|
+
"visualization",
|
|
19
|
+
"interactive",
|
|
20
|
+
"d3js",
|
|
21
|
+
"d3graph",
|
|
22
|
+
"data visualization",
|
|
23
|
+
"web",
|
|
24
|
+
"modular",
|
|
25
|
+
"customizable"
|
|
26
|
+
]
|
|
14
27
|
classifiers = [
|
|
15
28
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"License :: OSI Approved :: BSD License",
|
|
17
29
|
"Operating System :: OS Independent",
|
|
30
|
+
"Intended Audience :: Education",
|
|
31
|
+
"Intended Audience :: Science/Research",
|
|
32
|
+
"Operating System :: Unix",
|
|
33
|
+
"Operating System :: Microsoft :: Windows",
|
|
34
|
+
"Operating System :: MacOS",
|
|
18
35
|
]
|
|
19
36
|
dependencies = [
|
|
20
37
|
'pandas',
|
d3graph-2.7.0/setup.cfg
ADDED
d3graph-2.6.1/setup.cfg
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from copy import deepcopy
|
|
2
|
-
import numpy as np
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from d3graph import d3graph, adjmat2vec, vec2adjmat
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def test_instantiate_d3graph_no_args() -> None:
|
|
8
|
-
"""Test instantiation works with defaults"""
|
|
9
|
-
d3 = d3graph()
|
|
10
|
-
assert isinstance(d3, type(d3graph()))
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_clean(d3, helpers) -> None:
|
|
14
|
-
"""Test _clean method deletes the attributes in clean_fields"""
|
|
15
|
-
clean_fields: tuple = ('adjmat', 'config', 'edge_properties', 'G', 'node_properties')
|
|
16
|
-
|
|
17
|
-
# Set attrs to dummy value (i.e., 0) and assert they exist in the object
|
|
18
|
-
original_attrs = {field: 0 for field in clean_fields}
|
|
19
|
-
|
|
20
|
-
# Convert the array to a DataFrame for comparison
|
|
21
|
-
def test_adjmat2vec_and_back() -> None:
|
|
22
|
-
edges = np.array([('Cloudy', 'Sprinkler'), ('Cloudy', 'Rain'), ('Sprinkler', 'Wet_Grass'), ('Rain', 'Wet_Grass')])
|
|
23
|
-
weight = [1,2,3,4]
|
|
24
|
-
df_edges = pd.DataFrame(edges, columns=['source', 'target'])
|
|
25
|
-
df_edges['weight']=weight
|
|
26
|
-
|
|
27
|
-
df_edges_c = adjmat2vec(vec2adjmat(source=df_edges['source'], target=df_edges['target'], weight=df_edges['weight']))
|
|
28
|
-
adjmat = vec2adjmat(source=df_edges_c['source'], target=df_edges_c['target'], weight=df_edges_c['weight'])
|
|
29
|
-
df_edges_c = adjmat2vec(adjmat)
|
|
30
|
-
|
|
31
|
-
df_edges_c = df_edges_c.sort_values(by='weight')
|
|
32
|
-
df_edges_c['isin']=0
|
|
33
|
-
|
|
34
|
-
for index, edge in df_edges.iterrows():
|
|
35
|
-
Iloc = np.sum(edge==df_edges_c[df_edges.columns], axis=1)==len(df_edges.columns)
|
|
36
|
-
df_edges_c['isin'][Iloc]=1
|
|
37
|
-
|
|
38
|
-
assert df_edges_c['isin'].sum()==df_edges.shape[0]
|
|
39
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|