0

I have a df consisting of two different frames for each observation, denoted by the ending character in the variable

     name x1 y1 x2 y2
0    bob  3  2  1  4
1    amy  2  1  4  3
2    pam  6  3  3  1
3    joe  4  2  6  5

I am wondering how to create an animation consisting of two frames ([x1,y1],[x2,y2]). I have seen resources on how to create animations with line and bar charts, but I couldn't find much info on scatterplot animations.

The response to this question seems a bit complicated for my application.

Things I have tried:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig = plt.figure(figsize=(5,5))
scatter=ax.scatter(df["x1"], df["y1"])

def animate():
    scatter.set_data(df[['x2','y2'])

Is my dataframe set up correctly for this? I would also like to annotate these points, but I know how to do that with the adjustText package, so that isn't a problem here, right? I'm assuming I don't have to set the annotations like I have to set the data.

Any help is appreciated. Thanks!

bismo
  • 1,257
  • 1
  • 16
  • 36

1 Answers1

1

The answer in the linked question mentions that in order to change the data of a scatter plot you do

scatter.set_offsets(array)

A few other things to notice from there, or from reading the docs/other resources, is that the animate function requires an argument, which is the current frame you're on. You're also supposed to return as a tuple the artists you want to animate. So at minimum it should look like the following:

def animate(i):
    scatter.set_offsets(<the respective (4, 2) array for ith frame>)
    return scatter,

If you want to include annotations in your animation, you also have to return those artists. In that case, I suggest putting everything in a tuple and accessing them by index. Here is a simple example for your two frames + the annotation of each point's respective name:

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame(
     [['bob', 3, 2, 1, 4], ['amy', 2, 1, 4, 3], ['pam', 6, 3, 3, 1], ['joe', 4, 2, 6, 5]],
     columns=['name', 'x1', 'y1', 'x2', 'y2'])

def animate(i):
     columns = df.columns[1:]
     data = df[columns[2*i:2*i+2]].to_numpy()
     # You can also do `scatter.set_offsets()` and `zip(annnotations, data)`
     artists[0].set_offsets(data)
     for ann, (x, y) in zip(artists[1:], data):
          ann.set_x(x)
          ann.set_y(y)
     return artists,

fig = plt.figure(figsize=(5,5))
scatter = plt.scatter([], [])   # whatever, it'll change every frame
annotations = tuple(plt.annotate(df['name'].iloc[i], (0, 0)) for i in range(len(df)))
artists = (scatter,) + annotations
# Setting them manually here; all points in all frames should be visible
plt.xlim(0, 7)
plt.ylim(0, 7)
anim = FuncAnimation(fig, animate, frames=2)
plt.show()
Reti43
  • 9,656
  • 3
  • 28
  • 44
  • Awesome! Is there a way to do a sliding transition, rather than just cutting to the second set of data points? That is, can there be a smoother transition between the two frames somehow? @Reti43 – bismo May 19 '21 at 16:25
  • 1
    @bismo Not that I know of with matplotlib's FuncAnimation, but it's worth asking a new question about it. If no options exist for creating smooth transitions between frames, I would simple add a lot more frames in the between and interpolate the points' movement. After all, you have a start and end point, so figuring out their line of motion and speed shouldn't be hard. – Reti43 May 19 '21 at 22:15