Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

server side examples #1

Merged
merged 14 commits into from
Oct 26, 2018
Merged

server side examples #1

merged 14 commits into from
Oct 26, 2018

Conversation

chriddyp
Copy link
Member

No description provided.

the docstring from each propType is transferred to the Python classes
`updatemode: ‘replace’` will completely clear and redraw the figure,
without keeping any state in the graph nor tweening between
transitions.

This is used for examples that update the data of the sunburst chart
through callbacks
@chriddyp
Copy link
Member Author

chriddyp commented Oct 15, 2018

I started this PR by trying to create a couple of examples that update the data of the graph through backend callbacks. The use case being: there is a radial graph with too many layers to view all at once (either because its too slow or just because its impossible to visualize); instead of displaying all of the layers at once, the user can pull up particular nodes (through dropdowns) or can progressively traverse the network by clicking on different slices.

In creating these examples, I found that the stateful nature of the graph was problematic:

  1. we don't want to tween
  2. we don't want selectedData to trigger an update
  3. and the central node text wasn't updating (some bug in the sunburst.update method?)

Instead of patching the update method, I decided to clear the DOM and just draw a new graph instead in the React layer. This behaviour is controlled through a new updatemode: 'animate' | 'replace' property.

Some notes:

  1. It's not clear to me where this should logic to live. Should there be another method in sunburst.js called 'replace' or 'create'? That would require some refactoring of the constructor that would be annoying.
  2. It would be helpful be to have control over the colors of the layers. That way, there would be some more object consistency as you click around. I'm not sure if that's easy or hard to add.
  3. The click example is a little flashy, I'm not sure why. Perhaps because the transition starts (on click) and then the graph is immediately replaced by the React wrapper.

If this approach looks good, then I can update the README.md code walk-throughs as well.

Here are gifs of the examples. The data is uniform and the colors don't change so focus on the text of the nodes on updates:
dash-sunburst-example-1

dash-sunburst-example-2

@chriddyp
Copy link
Member Author

cc @alexcjohnson also folks from @plotly/dash might be interested in general

@alexcjohnson
Copy link
Contributor

Thanks @chriddyp - that's right, you had asked about server-side data updates, I forgot about this use case and without it couldn't think of a plausible way to handle that. Thanks for getting this started. This reminds me actually of our discussion about transitionOpts plotly/plotly.js#1849 (comment) - but in this case I think we actually can figure out on our own when to animate and when not to.

the central node text wasn't updating (some bug in the sunburst.update method?)

That one's pretty easy... should be fixed by 8067b21 fb89325

we don't want to tween

What if, instead of adding updatemode, we change the logic in update to be: if the root node changes (its name I guess, or perhaps we also give it some other attribute that controls this) then we trigger a fresh non-animated draw; otherwise we animate. My thinking here is that yes, in principle you can just completely replace the chart (so in your example we would automatically replace because the root changed), but what would make this even more powerful is if your updated data still provides the direct ancestors of the selected node all the way up to the root, and you just prune away the invisible side branches of the tree. This would be cool not just because it would still support animation but also because (1) it would automatically preserve colors, and (2) the "zoom out" object in the middle would still exist. And it would only be a few extra nodes so shouldn't impact performance.

It would be helpful be to have control over the colors of the layers. That way, there would be some more object consistency as you click around. I'm not sure if that's easy or hard to add.

Absolutely, it would be easy to support node.color instead of only creating a color from the default colorway. But as mentioned above I think that may not be necessary at first. The object constancy would just come from the fact that there's implicit state in d3.scale.category20() as it remembers all the labels that have been fed to it in the past.

we don't want selectedData to trigger an update
The click example is a little flashy, I'm not sure why. Perhaps because the transition starts (on click) and then the graph is immediately replaced by the React wrapper.

Yep, that's probably the reason it's flashy. I suspect if we ensure root-preserving changes always animate, the flashes would go away even if we end up doing two updates back-to-back: one to the new selected node, the second to update the data based on the server's new data for that node. We may need some extra logic to short-circuit the first update once the second one starts, or it may already do that, I'm not sure.

I'm happy to give this approach a shot tomorrow if you think it sounds interesting.

@chriddyp
Copy link
Member Author

but what would make this even more powerful is if your updated data still provides the direct ancestors of the selected node all the way up to the root, and you just prune away the invisible side branches of the tree

Yeah, that would be very cool! This sounds like a good approach for growing a tree out progressively via clicking on the nodes.

I could still imagine a case where you would want to jump around in the tree. In that case, the root would still change but we might jump from level 1 to level 14. The real world use case might be a search box to look up a particular node in some huge network and when you've selected it, you'd display it's children.

In that case, would we need to keep track of the levels as well inside the object? That is, so that if we transition we don't tween and instead of directly appending the nodes to the internal representation of the network, we create some sparse array of the network?

@alexcjohnson
Copy link
Contributor

The real world use case might be a search box to look up a particular node in some huge network and when you've selected it, you'd display it's children.

That's a good point, you wouldn't want to animate if nothing displayed is the same before/after, even within the same network. OK, so the logic is a bit more complex than just "did the root node change", but I suspect we can still figure this out internal to the D3 component. Might be as simple as having it calculate the enter / exit joins, and see if anything is left behind as a node to update.

@alexcjohnson
Copy link
Contributor

@chriddyp the approach I described above did eventually work, but it was super awkward to manage changing both selectedPath and data in response to a single state change (level or node - specifically in usage_backend_update_via_controls.py). In the end I found the only reliable way to do it was to first update selectedPath, then add a callback on selectedPath to update data, so there are no ambiguous order of operations issues. It's not ideal though, in particular it forces a 2-step animation that's both slower and harder to follow as a user.

Multi-output callbacks would certainly help in this case. But I wonder, does each callback really need to fire independently? I naively would have thought that some piece of state changes and we'd fire whatever callbacks are necessary to propagate that change all the way through the dependency graph, before presenting just the final state back to the app. If we did that, then chained, parallel, and multi-output callbacks would all be functionally equivalent, and a single input change would only cause a single rerender. Is there a reason not to do it that way, or have we just not gotten to that yet?

Anyway check it out. I haven't updated the prose yet to reflect the new code, there will be a bunch of things to add based on the experience of making it work right in all these cases.

@alexcjohnson alexcjohnson merged commit a2265b1 into master Oct 26, 2018
@alexcjohnson alexcjohnson deleted the review branch October 26, 2018 14:19
@alexcjohnson
Copy link
Contributor

Merged - I'll make a new PR to update the docs

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants