-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[WIP] Feature: Animated 1D plots #2729
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
Conversation
Hello @TomNicholas! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found: There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻 Comment last updated at 2019-04-30 16:06:37 UTC |
xarray/plot/plot.py
Outdated
anim = Animation([line_block, title_block], timeline=timeline) | ||
# TODO I think ax should be passed to timeline_slider args | ||
# but that just plots a single huge timeline and no line plot?! | ||
anim.controls(timeline_slider_args={'text': animate_over}) |
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 can pass 'valfmt':'%s'
to format datetime. It will require some code to be smarter depending on the type of data being formated. I haven't worked with datetime much, so I'm not sure the best practices about formatting 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.
If it's in the title; maybe it doesn't need to be in the slider?
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 can pass 'valfmt':'%s' to format datetime.
This gives you the date in year-month-day hour:minute:seconds.milliseconds format, but doesn't truncate all the decimal points with the milliseconds. I also haven't used datetime64
objects much so I don't really know how to fix that in a general manner.
If it's in the title; maybe it doesn't need to be in the slider?
I think it should be in both, although in this example the title looks good, in my experience then once you slice high-dimensional data the title becomes really complicated and it's better to definitely have it presented nicely by the timeline too.
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've written in a special case for datetime objects, which truncates them thanks to pandas.to_datetime()
. Unfortunately the string still gets partly obscured by the play/pause button, but I think fixing that would require either truncating the string to a fixed maximum size or changing animatplot to choose the button placement based on the size of the string. I've updated the gif in the original PR to show what it looks like now.
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.
@TomNicholas This looks like it's on the right track. That gif is awesome!
I am in favour of including this feature. It makes sense that you need to update the _infer_*
functions. I would ignore doing 2D arrays (i.e. no hue).
Re complexity: It;s probably worth extracting everything to a animate_line
function in animate.py
? Things should be cleaner that way. The only code you have in common is the one that sets axes scales , limits & labels I think. Everything else has already been abstracted to _infer_line_data
xarray/plot/plot.py
Outdated
|
||
if ndims == 2 and add_legend: | ||
if animate_over is not None: | ||
# TODO how might this work for multiple lines? |
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 we can ignore this for now.
xarray/plot/plot.py
Outdated
ax.set_title(darray._title_for_slice()) | ||
else: | ||
# Would be nicer if we had something like in GH issue #266 | ||
frame_titles = [darray[{animate_over: i}]._title_for_slice() |
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.
instead of looping over a possibly very-large dimension, maybe write a generator?
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's currently not much point in doing this, as the first thing animatplot will do with the generator is expand it into a list anyway.
xarray/plot/plot.py
Outdated
anim = Animation([line_block, title_block], timeline=timeline) | ||
# TODO I think ax should be passed to timeline_slider args | ||
# but that just plots a single huge timeline and no line plot?! | ||
anim.controls(timeline_slider_args={'text': animate_over}) |
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.
If it's in the title; maybe it doesn't need to be in the slider?
I think this is a cool example, and lots of users will probably like it. A high-level question: why was animatplot chosen over matplotlib's animation module? |
I've had a go at that in the most recent commits.
Great - it's definitely something I personally want to do all the time.
Because animatplot already abstracts away the creation of the animation, so plotting an animation becomes almost a simple substitution Another advantage is that animatplot's block abstraction should make it easier to compose figures made of multiple animated plots, another thing people often want to do but is is awful with (I haven't really looked at how animated facetgrids might work yet, but I suspect that being able to animate a large list of blocks would be helpful too.) The logic is explained slightly more in #2355, and also looking at animatplot's docs might help you see why I think it's a good idea. We don't have to use animatplot, but if we didn't I would be suggesting reimplementing something with a similar class structure in xarray as the best approach anyway. The disadvantages of using animatplot are that it's an extra (optional) dependency, and it's not got good unit test coverage yet so someone should probably contribute tests to animatplot upstream before xarray officially relies on it. animatplot isn't particularly large, so if we wanted to reimplement something similar in xarray then that would be feasible. |
_labels = kwargs.pop('_labels', True) | ||
|
||
ax = get_axis(figsize, size, aspect, ax) | ||
xplt_val, yplt_val, hueplt, xlabel, ylabel, huelabel = \ |
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.
Does animate
work with pd.Interval
step plots?
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 have no idea. In theory then as long as xplt_val
and yplt_val
get set correctly then it should?
Have you got an example, issue, or point in the code somewhere I can look to see what a pd.Interval
step plot consists of?
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 the bit that tests it:
xarray/xarray/tests/test_plot.py
Line 460 in a84ea12
class TestPlotStep(PlotTestCase): |
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.
If it doesn't work; just raise an informative error...
@@ -0,0 +1,166 @@ | |||
""" | |||
Use this module directly: |
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 needs an edit.
I've done some more work on this, you can now: Plot multiple animated linesdat2d = air.isel(lat=[10, 15, 20])
anim = dat2d.plot(animate='time', hue='lat')
anim.save('line.gif', writer='imagemagick')
plt.show() Add static lines to an animated plot# draw animated line
anim = dat1d.plot(animate='time', label='evolving')
# draw static line
# plot will be drawn on to current axes unless ax argument given
dat1d.mean(dim='time').plot(color='g', label='average')
plt.legend(loc='upper right')
anim.save('average.gif', writer='imagemagick')
plt.show() |
There's a glitch with the legend in the first gif. Maybe the location needs to be pinned to something that is not 'best' |
This is looking great; let me know when you're ready for another review. I recommend saving facetgrid support for future PRs. I'm personally more excited about 2D animations and am happy to implement that once all the common infrastructure is in. With that bias in mind, I think the way to proceed would be: (this PR) → 2D animations → facetgrid support |
Just pinging @jbusecke, who recently announced https://github.com/jbusecke/xmovie. He might have some insights on this problem. |
This looks amazing! Which problem are you referring to specifically @rabernat? |
Also FYI I have a PR open that will enable xmovie to write movie files (by invoking ffmpeg 'under the hood'). |
Yeah, I'm not entirely sure why that happens yet, but I guess it means the legend position should be determined automatically for only the first frame, then fixed there for the other frames.
Agree. I think I might also leave animated step plot support for a future PR.
Help with that would be great! I also really want to get 2D animations working - I'm often trying to make animations like this: I anticipate that there might be some difficulties with cartopy integration for 2D animations, but I don't actually use cartopy in my work, so help with that would be especially appreciated.
Yeah we've been talking about the relationship between this and xmovie in jbusecke/xmovie#2. I think that movie output functionality can be tacked on later. |
This looks awesome; it would be really nice to have built-in xarray animation support!
Also wanted to point out while that's progressing along, hvplot supports 2D animations. https://hvplot.pyviz.org/
|
Just came across this PR while trying for the first time to create an animation of xarray data. Looks like it got quite far along but then sputtered. Did it get superseded by hvplot? |
Hi @spencerahill , sorry for the slow reply. This functionality isn't integrated yet, but hvplot I think probably provides the better approach. You will also want to see what philipjfr came up with over on #3709 , which would allow an xarray-like syntax but with the power of holoviews. The one remaining question for me with the holoviews approach is how easy it is to save a .gif or a video file. It seems like it might be possible already, but if you have a go then let me know how it goes! (This is something I very very much want to see working, but isn't my priority right now) |
@TomNicholas, this has been unintentionally closed when renaming |
This is an attempt at a proof-of-principle for making animated plots in the way I suggested in #2355. (Also relevant for #2030.)
This example code:
now produces this gif:

The units on the timeline are formatted incorrectly because this PR isn't merged yetI think it looks pretty good! It even animates the title properly. The actual animation creation only takes one line to do.
This currently only works for a plot with a single line, which is animated over a coordinate dimension.
It also required some minor modifications/bugfixes to animatplot, so it probably isn't reproducible right out of the box yet.If you want to try this out then use the develop branch of my forked version of animatplot.The reason I've put this up is because I wanted to
I feel like although it required only ~100 lines extra to do this then the logic is very fragmented and scattered through the
plot.line
andplot._infer_line_data
functions. In 2D this would get even more complicated, but I can't see a good way to abstract the case of animation out?(@t-makaro I expect you will be interested in this)
EDIT: To-Do list:
FacetGrids of multiple animated lines(will leave for a later PR)