Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions animatplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .timeline import Timeline
from .animations.animation import Animation
from . import blocks, util
from . import xanim
Empty file added animatplot/examples/__init__.py
Empty file.
Empty file.
21 changes: 21 additions & 0 deletions animatplot/examples/xanim/imshow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
from animatplot.xanim import animated_plot


# Create data set
x = np.linspace(-2, 2, 41)
y = np.linspace(-2, 2, 41)
t = np.linspace(0, 2*np.pi, 30)
X, Y, T = np.meshgrid(x, y, t)
data = np.sin(X*X+Y*Y-T)
da = xr.DataArray(data, coords=[('horizontal position', x),
('vertical position', y),
('time', t)])

# Create animated 2D plot
anim, block, timeline = animated_plot(da)
anim.controls()
anim.save_gif('../xarray_imshow')
plt.show()
Binary file added animatplot/examples/xarray_imshow.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions animatplot/xanim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import animatplot as amp
import xarray as xr


def animated_plot(da, anim_over='__guess__', x='x', y='y', plot_type='imshow', fps=10):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core of xarray is still a numpy array. I sort of think that anim_over should be t_axis. That said, the xarray docs keep using the term "coods", so maybe t_coord? I think I prefer t_axis for api consistency. OTOH, t_axis is usually an integer....

Either way, the default value should be None, and you can check

if anim_over is None:
...

Also, could you add the numpydoc style parameter to the docstring? Looks like:

Parameters
------------
parameter_name : type
    description
…
Returns
--------
type1
    description
type2
   description

You can read about numpydoc here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sort of think that anim_over should be t_axis.

That would defeat a lot of the purpose of using xarray. The point of xarray is that the dimensions of the data are labelled in a human-readable manner, and you can interact with the data without specifying seemingly-arbitrary numbers (as in axis=0).

the xarray docs keep using the term "coords"

The xarray docs use the term coord because that is one of the fundamental objects in xarray, and it's not the same thing as a dimension (which is not the same thing as an axis!).

To be honest when I first started using xarray I was like "is all this extra jargon really necessary?", but having used it for a while it makes way more sense.

the default value should be None

Yep you're right.

I think I prefer t_axis for api consistency

I didn't use the word "axis" because that implies an integer numpy axis, which is what xarray allows you move past. I didn't use the letter "t" because t_axis only represents time in the sense that a gif evolves over real time. In general then t_axis could refer to something which isn't time at all. For example, I could have a DataArray of temperature T(x,y,z,t) in the atmosphere for each day in a month. If I want to plot a gif of the variation of temperature with height ona particular day then it would be much clearer to be able to write animate(da, anim_over='z') or even animate(da, anim_over='height') then to write animate(da, t_axis='z') (or worse, animate(da, t_axis=3). I chose "anim_over" because it's literally the dimension of the data over which the gif is to be animated. What do you think?

numpydoc

Yep, I am more than happy to conform to animatplot's commenting and documenting style (and write some unit tests), I just wanted to get the general idea of the layout worked out first.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, anim_over it is.

"""
Function to plot an animated plot from an xarray DataArray.

The intention is that this would form the basis of a xr.DataArray.plot.animate() method.
"""

if not isinstance(da, xr.DataArray):
raise TypeError('First argument must be an xarray DataArray object')

timeline, evolving_coord = _timeline_from_coords(da.coords, anim_over, fps)
t_axis = da.dims.index(evolving_coord)

if len(da.dims) is not 3:
raise NotImplementedError('Currently only plots 2D dataarrays')

@t-makaro t-makaro Aug 8, 2018

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this say "only plots 3D dataarrays"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it currently only plots 2D DataArrays which evolve over a 3rd dimension... Not sure what the clearest way to write that is.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Only animates 3D arrays"?


if plot_type is 'imshow':
# TODO ideally the blocks method should call the xarray plotting method da.plot.imshow()
print(da.values.shape)
print(t_axis)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the print statements please

block = amp.blocks.Imshow(da.values, t_axis=t_axis)
else:
# TODO deal with other kinds of plot (lines, quiver...) here
raise NotImplementedError('Currently only plots using imshow')

# TODO if we can't call the xarray plotting method directly then add titles, axes labels etc here
anim = amp.Animation([block])

return anim, block, timeline

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the way you return things here. It bodes well for future functions like this. But, I sort of think it should be a list of blocks (because future convenience functions could return multiple blocks). OTOH, advanced tuple unpacking

anim, *blocks, timeline = ...

is a thing. OTOH,

anim, [block1, block2], timeline = ...

is also possible. That's a tough decision. Keep with this and I'll make a decision befor 0.3 release.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally like the

anim, [block1, block2], timeline = ...

variant more, but this is something that would be easy to change anyway.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy to change, but annoying to change after release. Ya, make it return a list of blocks. It will be easier to explain in the docs too.



def _timeline_from_coords(coords, anim_over, fps):
"""Create the animatplot Timeline object from the information in the coordinates attribute of the DataArray."""

# Determine coordinate over which plot should be animated
if anim_over is '__guess__':
# Attempt to automatically determine coordinate over which to animate plot
guesses = {'t', 'T', 'time', 'Time'}
matches = list(guesses & set(coords))
if len(matches) is 1:
evolving_coord = matches[0]
else:
raise ValueError('Could not automatically determine coordinate to animate over - '
'multiple possibilities found: ' + str(matches))
elif anim_over in coords:
evolving_coord = anim_over
else:
raise ValueError('Could not determine coordinate to animate over')

print(evolving_coord)
t = coords[evolving_coord].values

# TODO Attempt to determine units from metadata in dataarray attributes, according to CF conventions

timeline = amp.Timeline(t, units=None, fps=fps)

return timeline, evolving_coord