TL/DR: If you are having trouble with the ax.set_...
methods for animating your scatter plot, you can try to just clear the plot each frame (i.e., ax.clear()
) and re-plot things as desired. This is slower, but might be useful when you want to change a lot of things in a small animation.
Here is an example demonstrating this "clear" approach:
import itertools
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# set parameters
frames = 10
points = 20
np.random.seed(42)
# create data
data = np.random.rand(points, 2)
# set how the graph will change each frame
sizes = itertools.cycle([10, 50, 150])
colors = np.random.rand(frames, points)
colormaps = itertools.cycle(['Purples', 'Blues', 'Greens', 'Oranges', 'Reds'])
markers = itertools.cycle(['o', 'v', '^', 's', 'p'])
# init the figure
fig, ax = plt.subplots(figsize=(5,5))
def update(i):
# clear the axis each frame
ax.clear()
# replot things
ax.scatter(data[:, 0], data[:, 1],
s=next(sizes),
c=colors[i, :],
cmap=next(colormaps),
marker=next(markers))
# reformat things
ax.set_xlabel('world')
ax.set_ylabel('hello')
ani = animation.FuncAnimation(fig, update, frames=frames, interval=500)
ani.save('scatter.gif', writer='pillow')
The tutorials I have seen from matplotlib and other sources do not seem to use this approach, but I have seen others (as well as myself) suggest it on this site. I see some pros & cons, but I would appreciate anyone else's thoughts:
Pros
- You can avoid using the
set_...
methods for the scatter plot (i.e. .set_offsets()
, .set_sizes()
, ...), which have more obscure and less-detailed documentation (though the leading answer will get you very far here!). Plus, there are different methods for different plot types (e.g. you use set_data
for lines, but not for scatter points). By clearing the axis, you determine the plotted elements each frame like any other plot in matplotlib.
- Even more so, it is unclear if some properties are
set
-able, such as the marker type (as commented) or the colormap. I wouldn't know how to create the above plot using ax.set_...
, for example, because of the marker and colormap changes. But this is pretty basic with ax.scatter()
.
Cons
- It can be much slower; i.e. clearing and redrawing everything appears to be more expensive than the
set...
methods. So for large animations, this approach can be kind of painful.
- Clearing the axis also clears things like the axis labels, axis limits, other text, etc. So, those sorts of formatting things need to be included in
update
(else they will be gone). This can be annoying if you want some things to change, but others to stay the same.
Of course, the speed is a big con. Here is an example showing the difference. Given this data:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
np.random.seed(42)
frames = 40
x = np.arange(frames)
y = np.sin(x)
colors = itertools.cycle(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])
data = [(np.random.uniform(-1, 1, 10) + x[i],
np.random.uniform(-1, 1, 10) + y[i])
for i in range(frames)]
You can plot using the set...
method:
fig, ax = plt.subplots()
s = ax.scatter([], [])
ax.set_xlim(-2, frames+2)
ax.set_ylim(min(y) - 1, max(y) + 1)
def update(i):
s.set_offsets(np.column_stack([data[i][0], data[i][1]]))
s.set_facecolor(next(colors))
ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('set.gif', writer='pillow')
Or the "clear" method:
fig, ax = plt.subplots()
def update(i):
ax.clear()
ax.scatter(data[i][0], data[i][1], c=next(colors))
ax.set_xlim(-2, frames+2)
ax.set_ylim(min(y) - 1, max(y) + 1)
ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('clear.gif', writer='pillow')
To get this figure:
Using %%time
, we can see that clearing and replotting takes (more than) twice as long:
- for
set...
: Wall time: 1.33 s
- for clear:
Wall time: 2.73 s
Play with the frames
parameter to test this at different scales. For smaller animations (less frames/data), the time difference between the two methods is inconsequential (and for me, sometimes causes me to prefer the clearing method). But for larger cases, using set_...
can save significant time.