-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sankey feature branch #1591
Sankey feature branch #1591
Conversation
@monfera this is looking awesome! A few things popped out to me while playing with it:
|
src/traces/sankey/attributes.js
Outdated
].join(' ') | ||
}, | ||
|
||
valueunit: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valuesuffix
would be best here, similar to ?axis.ticksuffix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, switching to this!
src/traces/sankey/attributes.js
Outdated
}, | ||
|
||
// followmouse for UX testing | ||
followmouse: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪
src/traces/sankey/attributes.js
Outdated
|
||
textfont: fontAttrs, | ||
|
||
nodes: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice. I like this API 😄
src/traces/sankey/attributes.js
Outdated
description: 'The nodes of the Sankey plot.' | ||
}, | ||
|
||
links: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done, very plotly-esque.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, tried to apply things learnt off the bat with this new chart; still room for improv!
src/traces/sankey/defaults.js
Outdated
usedNodeCount = 0, | ||
indexMap = []; | ||
|
||
var defaultPalette = d3.scale.category20(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess our 10-color default palette isn't ideal for Sankey. But I would still vote 👍 for using it for Sankey for consistency.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good in principle, I'll try and add some screenshots to get a comparison.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually looks OK! A slight loss is that repetition is more noticeable as it has fewer colors, but users may specify per-node color and use a d3
or colorbrewer
categorical palette however they choose. I'll switch to the plotly
palette for consistency.
src/traces/sankey/defaults.js
Outdated
return Lib.coerce(nodeIn, nodeOut, attributes.nodes, attr, dflt); | ||
} | ||
|
||
for(i = 0; i < nodesIn.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty heavy operation. It would be nice to move it (or some of it) to the calc step.
src/traces/sankey/defaults.js
Outdated
|
||
|
||
foundUse = false; | ||
for(j = 0; j < traceOut.links.length && !foundUse; j++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. I meant this one.
Defaults should still have to loop over all nodes and links to coerce their attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for my education, since as you say, defaults must loop through it anyway (tho not nested) would putting it in calc
result in more efficient code, or are you referring to the convention where computational things are usually in calc
? I put it in defaults
without much thought simply b/c filtering of unrendered elements is typically done in defaults
and didn't think of it as 'calculation'.
src/traces/sankey/index.js
Outdated
Plot.moduleType = 'trace'; | ||
Plot.name = 'sankey'; | ||
Plot.basePlotModule = require('./base_plot'); | ||
Plot.categories = ['gl']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the catch, gl
was a leftover, I'll still need to eyeball the diffs as the PR ripens. I did
Plot.categories = [];
with which everything worked fine. I had to specify some set (even if the empty set) otherwise registry.js
throws. I also tried
Plot.categories = ['sankey'];
but it failed on some coercion.
src/traces/sankey/index.js
Outdated
description: [ | ||
'Sankey plots for network flow data analysis.', | ||
'The nodes are specified in `nodes` and the links between sources and targets in `links`.', | ||
'The colors are set in `line.color`.' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👓 you mean in nodes[i].color
and links[i].color
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes... thanks for the catch!
src/traces/sankey/plot.js
Outdated
} | ||
} | ||
|
||
var log = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, this and some other things such as index-sankey
are only for current dev. Will remove as it nears merging; now just helpful w/ churning out codepen with small bundle size.
@alexcjohnson thanks for the comments!
Thanks again for the feedback and I'll keep my thoughts humming on these, also appreciating further thoughts or decisions. |
@alexcjohnson regarding your comment on the colors, there's a new version, not yet in this squashed branch, that has an improved color handling: http://codepen.io/monfera/full/aWbVev/ The values (e.g. TWh values here) are in the node color, and if the node color is too light, then it's darkened for better contrast on the white tooltip background. It may still be at odds with what you want so my question above stands. |
It's still a tiny bit different from what we do elsewhere, though that sometimes makes a mistake: The rationale was that we picked a color from the trace that made a visible point against the plot background - the user wouldn't have made a point that wasn't visible! so it should be visible against a semitransparent version of the same. Obviously the premise there ("we picked a color from the trace that made a visible point against the plot background") isn't always correct. Better in that particular case would be to have chosen the red, rather than the white marker fill, but that's a separate issue, there could be cases where no color from the point/trace is really sufficiently contrasting. So actually, if you make sure that the algorithm you're using works for arbitrary background color (white, black, some grey in the middle...) then I think I'd like to use it everywhere. I didn't look at your implementation at all, but in case you didn't see it go by there's a recently added/modified routine Color.contrast made explicitly for purposes like this. |
Overlap: yes the codepen is partly a demonstration for the overlap effect. We had no client requirement for multi-edges, though @jackparmer thought we should have this feature, so this PR was born which shows the improvement. IOW the original Sankey implementation had no support for multi-edges and with the PR we do have it, although not in a perfect way. It's OK if there are lots of items and bands are narrow, but noticeable if bands are wide and Y difference is significant. Solving it might be half day to a day, should I go ahead with it or let's consider it in a subsequent merge? Color: Yes I'm already using |
@alexcjohnson I forgot to add that yes, both the overlap avoidance, and the avoidance of bulges and the sometimes appearing circular artifacts around the nodes would be best taken care of together, because both would involve a rewrite of the current |
@alexcjohnson thanks for the useful comments! I take it from your answer that |
yeah, I think that's right - I'm not sure we've ever explicitly stated that, but for example if someone specifies the width and height of the div they plot into, we translate that into the plot width and height, and I expect they would not want the plot overflowing that. Also for iframe embeds, if the plot is autosized we will size it to the iframe and disable scrolling.
Sounds fine, at least as a starting point. |
Hi @alexcjohnson, hope you see your feedback acted on in the updated example such as
As @etpinard indicated, the I think Please let me know if you see any glaring unergonomic thing or incompliance, or if I haven't addressed some of your feedback. |
Super! I won't be able to look at this until ~Friday so if you want to move ahead before that I'll defer to Etienne. Thanks for the changes!
…----
Alex Johnson
617-319-2964
On Apr 25, 2017, at 11:59, Robert Monfera ***@***.***> wrote:
Hi @alexcjohnson, hope you see your feedback acted on in the updated example such as
no circular bulges when moving nodes too close
no partial overlap or gap with multiedges
left-swept node labels in the rightmost layer
better text-shadow to make node labels stand apart, no matter the surrounding background color and or mixed link color (there's no need for text-shadow in the vertical orientation as text is inside node then)
toggle button for black background - it'll swap the default rgba(0,0,0,0.2) link color to rgba(255, 255, 255, 0.6) which isn't noticeable - yielding the same perceived color - but necessary for the right balance of translucency and contrast (you'd notice very much if there were no link color change when switching; an example of which is the manually specified red/blue that was taylored for white background and isn't as good on black background)
to me, these goals seem to be met:
the hover tooltip is readable with the defaults and various colors, both with dark and light background (toggle w/ the top button on the left)
the tooltip is translucent, which is useful as it can be large depending on the contents, and in my experience, not having some translucency is disturbing as it hides the presence of link flows behind it
the node / link colors are present in one form or another on the tooltips
looks pleasing (subjective)
As @etpinard indicated, the Follow mouse will give way to the Sticky tooltip option (see button) as it's more in line with the rest of the trace types.
I think Please let me know if you see any glaring unergonomic thing or incompliance, or if I haven't addressed some of your feedback.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
(cherry picked from commit b48bf7c)
src/traces/sankey/attributes.js
Outdated
min: 1, | ||
dflt: 20, | ||
role: 'style', | ||
description: 'Sets the thickness of the `nodes`.' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in px?
src/traces/sankey/defaults.js
Outdated
@@ -41,26 +41,26 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout | |||
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); | |||
} | |||
|
|||
coerce('node.label'); | |||
coerce('node.label', []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a constant, can it be the attribute dflt
?
src/traces/sankey/defaults.js
Outdated
@@ -83,11 +83,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout | |||
}; | |||
|
|||
if(traceOut.node.label.some(missing)) { | |||
Lib.log('Some of the nodes are neither sources nor targets, please remove them.'); | |||
Lib.warn('Some of the nodes are neither sources nor targets, please remove them.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor point, the user may have different ways to solve the problem (eg providing the missing links, instead of removing disconnected nodes). I would have the warning simply describe what we did and why, like "Some of the nodes are neither sources nor targets, they will not be displayed." (if indeed that's what will happen) Your message on encountering cycles does a great job of this.
src/traces/sankey/base_plot.js
Outdated
} | ||
}; | ||
|
||
exports.toSVG = function() {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪
src/traces/sankey/defaults.js
Outdated
|
||
var nodes = nodeList.map(function() {return [];}); | ||
|
||
for(var i = 0; i < Math.min(sources.length, targets.length); i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I already commented on this, but can we move the computations that scale as the length of the data arrays in calc.js
?
src/traces/sankey/plot.js
Outdated
} | ||
|
||
function nodeHoveredStyle(sankeyNode, d, sankey) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪 those blank lines at the start of new blocks in accordance with standard style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it!
['Source:', d.link.source.label].join(' '), | ||
['Target:', d.link.target.label].join(' ') | ||
].filter(renderableValuePresent).join('<br>'), | ||
color: Color.addOpacity(d.tinyColorHue, 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
}); | ||
|
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should at the very least add some restyle
tests checking that we can toggle visible: true || false
correctly and that sankey subplots get clear correctly on deleteTraces
.
Some hover labels tests and click/hover event data assertions would be much appreciated too.
src/traces/sankey/index.js
Outdated
Plot.moduleType = 'trace'; | ||
Plot.name = 'sankey'; | ||
Plot.basePlotModule = require('./base_plot'); | ||
Plot.categories = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is trace-wide opacity
a thing for sankey traces? If not, categories
should include the noOpacity
flag like we did for parcoords in #1506
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No trace- wide opacity so I'll add it, thanks!
Including this in this week's |
…ction line if it's the only empty line in the function body
}); | ||
} | ||
|
||
module.exports = function calc(gd, trace) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A very nice first step. Thanks @monfera !
test/jasmine/tests/sankey_test.js
Outdated
.then(function() { | ||
expect(gd.data.length).toEqual(1); | ||
expect(d3.selectAll('.sankey').size()).toEqual(1); | ||
return Plotly.plot(gd, mockCopy2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing addTraces
would be better. Sequential Plotly.plot
calls is a deprecated pattern.
var gd = createGraphDiv(); | ||
var mockCopy = Lib.extendDeep({}, mock); | ||
|
||
Plotly.plot(gd, mockCopy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Nicely done @monfera good enough to 💃 I'll add a few more hover label tests while addressing #1591 (review) after this PR is in |
Thank you @etpinard and @alexcjohnson for your reviews and @etpinard for your upcoming additions! |
🏆 👏 🎉 Awesome stuff @monfera !! |
An example: http://s.codepen.io/monfera/debug/aWbVev/DqrDdEVVJngr
Add-on features:
- use of force for drag&drop without manual overlap fiddling
- multiple force ticks per frame for faster response
- starting/stopping force per graph layer (column)
- use of force for handling apparently non-circular shapes
- vertical layout
- option for multi-edge handling d3/d3-sankey#19
- plotly tooltips and raising various plotly events
- plotly-style color contrast: text becomes white on darker backgrounds
- highlight all connecting links and nodes when hovering a node
Current limitations:
- positions aren't saved (layout lost on update)
- positions aren't specifiable yet (no attributes for this yet)
- cheesy horizontal/vertical switch
- screenshot fails for a couple of reasons
- no test cases included yet (except a couple semi-mocks)
- tooltip doesn't clear on drag once there was a
Plotly.restyle
(works afternewPlot
)- color contrast on the bright red node isn't good
Notes: