d3graph 2.6.2__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.2/d3graph.egg-info → d3graph-2.7.0}/PKG-INFO +1 -1
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/__init__.py +1 -2
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/d3graph.py +40 -8
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/d3js/d3graphscript.js +89 -67
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/d3js/index.html.j2 +2 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/d3js/style.css +6 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/examples.py +103 -14
- {d3graph-2.6.2 → d3graph-2.7.0/d3graph.egg-info}/PKG-INFO +1 -1
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph.egg-info/SOURCES.txt +1 -2
- d3graph-2.6.2/tests/test_d3graph.py +0 -39
- {d3graph-2.6.2 → d3graph-2.7.0}/LICENSE +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/MANIFEST.in +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/README.md +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/d3js/d3.v3.js +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph/examples_docs.py +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph.egg-info/dependency_links.txt +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph.egg-info/requires.txt +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/d3graph.egg-info/top_level.txt +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/pyproject.toml +0 -0
- {d3graph-2.6.2 → d3graph-2.7.0}/setup.cfg +0 -0
|
@@ -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'],
|
|
@@ -1193,6 +1223,7 @@ def adjmat2dict(adjmat: pd.DataFrame,
|
|
|
1193
1223
|
df['label_color']=label_color
|
|
1194
1224
|
df['label_fontsize']=label_fontsize
|
|
1195
1225
|
df['edge_style']=edge_style
|
|
1226
|
+
df['edge_color']=edge_color
|
|
1196
1227
|
df['edge_opacity'] = edge_opacity
|
|
1197
1228
|
df['tooltip'] = df['weight'].astype(str)
|
|
1198
1229
|
|
|
@@ -1357,6 +1388,7 @@ def _normalize_size(getsizes, minscale=0.5, maxscale=4, scaler: str = 'zscore'):
|
|
|
1357
1388
|
# Instead of Min-Max scaling, that shrinks any distribution in the [0, 1] interval, scaling the variables to
|
|
1358
1389
|
# Z-scores is better. Min-Max Scaling is too sensitive to outlier observations and generates unseen problems.
|
|
1359
1390
|
|
|
1391
|
+
getsizes = getsizes.copy()
|
|
1360
1392
|
# Set sizes to 0 if not available
|
|
1361
1393
|
getsizes[np.isinf(getsizes)]=0
|
|
1362
1394
|
getsizes[np.isnan(getsizes)]=0
|
|
@@ -1734,4 +1766,4 @@ def get_support(support):
|
|
|
1734
1766
|
script = script + '\n' + "<div data-ea-publisher='erdogantgithubio' data-ea-type='{TYPE}' data-ea-style='stickybox'></div>".replace('{TYPE}', support)
|
|
1735
1767
|
|
|
1736
1768
|
# Return
|
|
1737
|
-
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,33 +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
|
|
|
85
106
|
link.append("title").text(function(d) { return d.tooltip; });
|
|
86
107
|
|
|
87
108
|
// ADD TEXT ON THE EDGES (PART 1/2)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.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; });
|
|
97
117
|
|
|
98
118
|
//Do the same with the circles for the nodes
|
|
99
119
|
var node = svg.selectAll(".node")
|
|
@@ -103,26 +123,37 @@ function d3graphscript(config = {
|
|
|
103
123
|
.call(drag)
|
|
104
124
|
.on('dblclick', connectedNodes); // HIGHLIGHT ON/OFF
|
|
105
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
|
+
|
|
106
139
|
{{ CLICK_COMMENT }} node.on('click', color_on_click); // ON CLICK HANDLER
|
|
107
140
|
|
|
108
141
|
|
|
109
142
|
node.append("circle")
|
|
110
|
-
.attr("r", function(d) { return d.node_size; })
|
|
111
|
-
.style("fill", function(d) {return d.node_color;})
|
|
112
|
-
.style("opacity", function(d) {return d.node_opacity;})
|
|
113
|
-
.style("stroke-width", function(d) {return d.node_size_edge;})
|
|
114
|
-
.style("stroke", function(d) {return d.node_color_edge;})
|
|
115
|
-
// .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
|
|
116
148
|
|
|
117
149
|
// Text in nodes
|
|
118
150
|
node.append("text")
|
|
119
151
|
.attr("dx", 10)
|
|
120
152
|
.attr("dy", ".35em")
|
|
121
|
-
.text(function(d) {return d.node_name})
|
|
122
|
-
.style("font-size", function(d) {return d.node_fontsize + "px";})
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// .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");
|
|
126
157
|
|
|
127
158
|
let showInHover = ["node_tooltip"]; // Tooltip
|
|
128
159
|
node.append("title")
|
|
@@ -142,10 +173,9 @@ function d3graphscript(config = {
|
|
|
142
173
|
.attr("cy", function(d) { return d.y; });
|
|
143
174
|
d3.selectAll("text").attr("x", function(d) { return d.x; })
|
|
144
175
|
.attr("y", function(d) { return d.y; })
|
|
145
|
-
|
|
176
|
+
linkText.attr("x", function(d) { return (d.source.x + d.target.x) / 2; }) // ADD TEXT ON THE EDGES (PART 2/2)
|
|
146
177
|
.attr("y", function(d) { return (d.source.y + d.target.y) / 2; })
|
|
147
178
|
.attr("text-anchor", "middle");
|
|
148
|
-
;
|
|
149
179
|
|
|
150
180
|
node.each(collide(config.collision)); //COLLISION DETECTION. High means a big fight to get untouchable nodes (default=0.5)
|
|
151
181
|
|
|
@@ -160,8 +190,6 @@ function d3graphscript(config = {
|
|
|
160
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' }
|
|
161
191
|
]
|
|
162
192
|
|
|
163
|
-
//console.log(JSON.stringify(link))
|
|
164
|
-
|
|
165
193
|
svg.append("defs").selectAll("marker")
|
|
166
194
|
.data(data_marker)
|
|
167
195
|
.enter()
|
|
@@ -169,17 +197,13 @@ function d3graphscript(config = {
|
|
|
169
197
|
.attr('id', function(d){ return 'marker_' + d.name})
|
|
170
198
|
.attr('markerHeight', 10)
|
|
171
199
|
.attr('markerWidth', 10)
|
|
172
|
-
//.attr('markerUnits', 'strokeWidth')
|
|
173
200
|
.attr("markerUnits", "userSpaceOnUse") // Fix marker width
|
|
174
201
|
.attr('orient', 'auto')
|
|
175
|
-
//.attr('refX', -15) // Offset marker-start
|
|
176
202
|
.attr('refX', 15) // Offset marker-end
|
|
177
203
|
.attr('refY', 0)
|
|
178
204
|
.attr('viewBox', function(d){ return d.viewbox })
|
|
179
205
|
.append('svg:path')
|
|
180
|
-
//.attr("transform", "rotate(180)") // Marker-start mirrored
|
|
181
206
|
.attr('d', function(d){ return d.path }) // Marker type
|
|
182
|
-
//.style("fill", function(d) {return d.marker_color;}) // Marker color
|
|
183
207
|
.style("fill", '#808080') // Marker color
|
|
184
208
|
.style("stroke", '#808080') // Marker edge-color
|
|
185
209
|
.style("opacity", 0.95) // Marker opacity
|
|
@@ -221,7 +245,7 @@ function d3graphscript(config = {
|
|
|
221
245
|
// collision detection end
|
|
222
246
|
|
|
223
247
|
|
|
224
|
-
//Toggle stores whether the highlighting is on
|
|
248
|
+
//Toggle stores whether the highlighting is on
|
|
225
249
|
var toggle = 0;
|
|
226
250
|
//Create an array logging what is connected to what
|
|
227
251
|
var linkedByIndex = {};
|
|
@@ -237,25 +261,27 @@ function d3graphscript(config = {
|
|
|
237
261
|
}
|
|
238
262
|
|
|
239
263
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
;
|
|
251
277
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
+
;}
|
|
259
285
|
|
|
260
286
|
|
|
261
287
|
|
|
@@ -269,7 +295,6 @@ function d3graphscript(config = {
|
|
|
269
295
|
link.style("opacity", function(o) {
|
|
270
296
|
return d.index == o.source.index | d.index == o.target.index ? 1 : 0.1;
|
|
271
297
|
});
|
|
272
|
-
//Reduce the op
|
|
273
298
|
toggle = 1;
|
|
274
299
|
} else {
|
|
275
300
|
//Put them back to opacity=1
|
|
@@ -279,14 +304,12 @@ function d3graphscript(config = {
|
|
|
279
304
|
toggle = 0;
|
|
280
305
|
}
|
|
281
306
|
}
|
|
282
|
-
//*************************************************************
|
|
283
307
|
|
|
284
308
|
|
|
285
309
|
//adjust threshold
|
|
286
310
|
function threshold() {
|
|
287
311
|
let thresh = this.value;
|
|
288
312
|
|
|
289
|
-
// console.log('Setting threshold', thresh)
|
|
290
313
|
graph.links.splice(0, graph.links.length);
|
|
291
314
|
linkText = linkText.data([]); // CLEAR EDGE-LABELS: Clear the linkText selection
|
|
292
315
|
linkText.exit().remove(); // CLEAR EDGE-LABELS: Clear the linkText elements from the DOM
|
|
@@ -314,12 +337,11 @@ function d3graphscript(config = {
|
|
|
314
337
|
link.exit().remove();
|
|
315
338
|
link.enter().insert("line", ".node").attr("class", "link");
|
|
316
339
|
link.style("stroke-width", function(d) {return d.edge_width;}); // LINK-WIDTH AFTER BREAKING WITH SLIDER
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
link.style("stroke", function(d) {return d.
|
|
321
|
-
link.style("
|
|
322
|
-
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
|
|
323
345
|
|
|
324
346
|
// Update EDGE-LABELS
|
|
325
347
|
linkText = linkText.data(graph.links);
|
|
@@ -336,4 +358,4 @@ function d3graphscript(config = {
|
|
|
336
358
|
force.start();
|
|
337
359
|
}
|
|
338
360
|
|
|
339
|
-
}
|
|
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,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
|
|
File without changes
|
|
File without changes
|