Skip to content

Commit 7c8cb7b

Browse files
authored
Merge branch 'dev' into playwright
2 parents bda4eec + 6ae16ce commit 7c8cb7b

11 files changed

Lines changed: 172 additions & 13 deletions

File tree

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ The following API is currently supported:
297297
- [`vis.contour`](#viscontour) : contour plots
298298
- [`vis.quiver`](#visquiver) : quiver plots
299299
- [`vis.mesh`](#vismesh) : mesh plots
300+
- [`vis.sankey`](#vissankey) : sankey (flow) diagrams
300301
- [`vis.dual_axis_lines`](#visdual_axis_lines) : double y axis line plots
301302
- [`vis.graph`](#visgraph) : network graphs
302303

@@ -696,6 +697,27 @@ The following `opts` are supported:
696697
- `opts.opacity`: opacity of polygons (`number` between 0 and 1)
697698
- `opts.layoutopts` : `dict` of any additional options that the graph backend accepts for a layout. For example `layoutopts = {'plotly': {'legend': {'x':0, 'y':0}}}`.
698699

700+
#### vis.sankey
701+
This function draws a Sankey (flow) diagram. Flows are defined by three
702+
equal-length arrays:
703+
704+
- `source`: source node index of each link (`N` array of ints)
705+
- `target`: target node index of each link (`N` array of ints)
706+
- `value` : magnitude of each link (`N` array of non-negative numbers)
707+
708+
`labels` is an optional list of node names. If omitted, nodes are referenced
709+
by their index alone.
710+
711+
The following `opts` are supported:
712+
713+
- `opts.labels` : list of node labels (alternative to the `labels` arg)
714+
- `opts.pad` : node padding in px (`number`; default = 15)
715+
- `opts.thickness` : node thickness in px (`number`; default = 20)
716+
- `opts.orientation`: `'h'` (default) or `'v'`
717+
- `opts.nodecolor` : node color(s) (`string` or list of strings)
718+
- `opts.linkcolor` : link color(s) (`string` or list of strings)
719+
- `opts.layoutopts` : `dict` of any additional options that the graph backend accepts for a layout.
720+
699721
#### vis.dual_axis_lines
700722
This function will create a line plot using plotly with different Y-Axis.
701723

cypress/integration/pane.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const basic_examples = [
1414
['Violin Plot', 'plot_violin_basic'],
1515
// ["Mesh Plot", "plot_special_mesh"], // disabled due to webgl
1616
['Graph Plot', 'plot_special_graph'],
17+
['Sankey Plot', 'plot_special_sankey'],
1718
['Matplotlib Plot', 'misc_plot_matplot'],
1819
['Latex Plot', 'misc_plot_latex'],
1920
['Video Pane', 'misc_video_tensor'],

example/components/plot_special.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,20 @@ def plot_special_graph(viz, env, args):
5959
},
6060
env=env,
6161
)
62+
63+
64+
# sankey (flow) diagram
65+
def plot_special_sankey(viz, env, args):
66+
title = args[0] if len(args) > 0 else None
67+
labels = ["raw", "cleaned", "labeled", "train", "val", "test"]
68+
source = [0, 1, 2, 2, 2]
69+
target = [1, 2, 3, 4, 5]
70+
value = [1000, 900, 720, 144, 36]
71+
viz.sankey(
72+
source=source,
73+
target=target,
74+
value=value,
75+
labels=labels,
76+
opts=dict(title=title),
77+
env=env,
78+
)

example/demo.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
plot_special_mesh,
8181
plot_special_sunburst,
8282
plot_special_graph,
83+
plot_special_sankey,
8384
)
8485
from components.properties import properties_basic, properties_callbacks
8586
from components.misc import (
@@ -188,6 +189,7 @@ def run_demo(viz, env, args):
188189
plot_special_mesh(viz, env, args)
189190
plot_special_sunburst(viz, env, args)
190191
plot_special_graph(viz, env, args)
192+
plot_special_sankey(viz, env, args)
191193

192194
# ============ #
193195
# violin plots #

js/main.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,21 +442,26 @@ const App = () => {
442442
});
443443

444444
setStoreMeta((prev) => {
445-
const layoutLists = new Map(storeMeta.layoutLists);
445+
const layoutLists = new Map(prev.layoutLists);
446446
layoutLists.delete(env2delete);
447-
let EnvIds = selection.envIDs.filter((env) => env !== env2delete);
447+
let EnvIds = prev.envList.filter((env) => env !== env2delete);
448448
return {
449449
...prev,
450450
envList: EnvIds,
451451
layoutLists: layoutLists,
452452
};
453453
});
454454

455-
setStoreData((prev) => ({
456-
...prev,
457-
panes: {},
458-
layout: [],
459-
}));
455+
setStoreData((prev) => {
456+
if (selection.envIDs.includes(env2delete)) {
457+
return {
458+
...prev,
459+
panes: {},
460+
layout: [],
461+
};
462+
}
463+
return prev;
464+
});
460465

461466
sendEnvDelete(env2delete, previousEnv);
462467
};

js/panes/PlotPane.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,7 @@ var PlotPane = (props) => {
197197
layout.title = { text: layout.title };
198198
}
199199
if (layout.title.text) {
200-
layout.margin.t = 65;
201-
layout.title.y = 0.9;
200+
layout.margin.t = 85;
202201
} else {
203202
layout.margin.t = 30;
204203
}

js/topbar/EnvControls.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ function EnvControls(props) {
7676
});
7777
});
7878

79+
const validEnvIDs = envIDs.filter((env) => slist.indexOf(env) !== -1);
7980
const currentIdx = envIDs.length > 0 ? slist.indexOf(envIDs[0]) : -1;
8081
const hasSingleSelectedEnv = envIDs.length === 1 && currentIdx !== -1;
8182
const onPrevEnv = () => {
@@ -112,11 +113,10 @@ function EnvControls(props) {
112113
wordBreak: 'break-all',
113114
}}
114115
placeholder={<i>Select environment(s)</i>}
115-
searchPlaceholder="search"
116116
treeLine
117117
maxTagTextLength={1000}
118118
inputValue={null}
119-
value={envIDs}
119+
value={validEnvIDs}
120120
treeData={env_options2}
121121
treeNodeFilterProp="title"
122122
treeDataSimpleMode={{ id: 'key', rootPId: 0 }}

py/visdom/__init__.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2933,6 +2933,109 @@ def mesh(self, X, Y=None, win=None, env=None, opts=None):
29332933
}
29342934
)
29352935

2936+
@pytorch_wrap
2937+
def sankey(self, source, target, value, labels=None, win=None, env=None, opts=None):
2938+
"""
2939+
This function draws a Sankey (flow) diagram. Flows are defined by three
2940+
equal-length arrays:
2941+
2942+
- `source`: source node index of each link (`N` array of ints)
2943+
- `target`: target node index of each link (`N` array of ints)
2944+
- `value` : magnitude of each link (`N` array of non-negative numbers)
2945+
2946+
`labels` is an optional list of node names. If omitted, nodes are
2947+
referenced by their index alone.
2948+
2949+
The following `opts` are supported:
2950+
2951+
- `opts.labels` : list of node labels (alternative to the `labels` arg)
2952+
- `opts.pad` : node padding in px (`number`; default = 15)
2953+
- `opts.thickness` : node thickness in px (`number`; default = 20)
2954+
- `opts.orientation`: `'h'` (default) or `'v'`
2955+
- `opts.nodecolor` : node color(s) (`string` or list of strings)
2956+
- `opts.linkcolor` : link color(s) (`string` or list of strings)
2957+
- `opts.layoutopts` : `dict` of additional backend layout options, e.g.
2958+
`layoutopts = {'plotly': {'font': {'size': 10}}}`.
2959+
"""
2960+
opts = {} if opts is None else opts
2961+
_title2str(opts)
2962+
_assert_opts(opts)
2963+
2964+
source = np.asarray(source)
2965+
target = np.asarray(target)
2966+
value = np.asarray(value)
2967+
for name, arr in (("source", source), ("target", target), ("value", value)):
2968+
assert arr.ndim <= 1, "sankey {} must be 1-D, got shape {}".format(
2969+
name, arr.shape
2970+
)
2971+
source = source.ravel()
2972+
target = target.ravel()
2973+
value = value.ravel()
2974+
assert (
2975+
len(source) == len(target) == len(value)
2976+
), "source, target and value must have the same length"
2977+
assert (source >= 0).all(), "sankey source indices must be non-negative"
2978+
assert (target >= 0).all(), "sankey target indices must be non-negative"
2979+
assert (
2980+
source == source.astype(int)
2981+
).all(), "sankey source indices must be integers"
2982+
assert (
2983+
target == target.astype(int)
2984+
).all(), "sankey target indices must be integers"
2985+
assert (value >= 0).all(), "sankey link values must be non-negative"
2986+
2987+
labels = labels if labels is not None else opts.get("labels")
2988+
if labels is not None and len(source) > 0:
2989+
num_nodes = max(int(source.max()), int(target.max())) + 1
2990+
assert len(labels) >= num_nodes, (
2991+
"labels must cover every referenced node "
2992+
"({} labels for {} nodes)".format(len(labels), num_nodes)
2993+
)
2994+
2995+
pad = opts.get("pad", 15)
2996+
thickness = opts.get("thickness", 20)
2997+
orientation = opts.get("orientation", "h")
2998+
assert (
2999+
isinstance(pad, numbers.Number) and pad >= 0
3000+
), "opts.pad must be a non-negative number"
3001+
assert (
3002+
isinstance(thickness, numbers.Number) and thickness > 0
3003+
), "opts.thickness must be a positive number"
3004+
assert orientation in ("h", "v"), "opts.orientation must be 'h' or 'v'"
3005+
3006+
node = {"pad": pad, "thickness": thickness}
3007+
if labels is not None:
3008+
node["label"] = list(labels)
3009+
if opts.get("nodecolor") is not None:
3010+
node["color"] = opts.get("nodecolor")
3011+
3012+
link = {
3013+
"source": source.astype(int).tolist(),
3014+
"target": target.astype(int).tolist(),
3015+
"value": value.tolist(),
3016+
}
3017+
if opts.get("linkcolor") is not None:
3018+
link["color"] = opts.get("linkcolor")
3019+
3020+
data = [
3021+
{
3022+
"type": "sankey",
3023+
"orientation": orientation,
3024+
"node": node,
3025+
"link": link,
3026+
}
3027+
]
3028+
3029+
return self._send(
3030+
{
3031+
"data": data,
3032+
"win": win,
3033+
"eid": env,
3034+
"layout": _opts2layout(opts),
3035+
"opts": opts,
3036+
}
3037+
)
3038+
29363039
@pytorch_wrap
29373040
def dual_axis_lines(self, X=None, Y1=None, Y2=None, opts=None, win=None, env=None):
29383041
"""

py/visdom/__init__.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ class Visdom:
224224
env: _OptStr = ...,
225225
opts: _OptOps = ...,
226226
) -> _SendReturn: ...
227+
def sankey(
228+
self,
229+
source: Tensor,
230+
target: Tensor,
231+
value: Tensor,
232+
labels: Optional[List] = ...,
233+
win: _OptStr = ...,
234+
env: _OptStr = ...,
235+
opts: _OptOps = ...,
236+
) -> _SendReturn: ...
227237
def graph(
228238
self,
229239
edges: List,

py/visdom/static/js/main.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)