简体   繁体   中英

Animating Matplotlib/Seaborn plots through Pandas?

I've been trying to animate a series of plots using matplotlib.animation to no avail. My data are currently stored in a Pandas dataframe and I want to iterate through a category (in this case, colors) and plot the data corresponding to each color as the following:

import pandas as pd
import seaborn as sns
import matplotlib.animation as animation

def update_2(i):
    plt.clf()
    fil_test = test[test['color'] == iterations[i]]
    sns.scatterplot(x = 'size',y = 'score',hue = 'shape',ci = None,
                              palette = 'Set1',data = fil_test)
    ax.set_title(r"Score vs. Size: {} Shapes".format(
        iterations[i]),fontsize = 20)
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5),prop={'size': 12})


test = pd.DataFrame({'color':["red", "blue", "red", 
"yellow",'red','blue','yellow','yellow','red'], 
        'shape': ["sphere", "sphere", "sphere", 
"cube",'cube','cube','cube','sphere','cube'], 
        'score':[1,7,3,8,5,8,6,2,9],
        'size':[2,8,4,7,9,8,3,2,1]})
iterations = test['color'].unique()
i = 0
fig2 = plt.figure(figsize = (8,8))
ax = plt.gca()
plt.axis()
ax.set_xlabel("size",fontsize = 16)
ax.set_ylabel("score",fontsize = 16)
ax.set_xlim(0,10)
ax.set_xlim(0,10)
ax.set_xticks(np.linspace(0,10,6))
ax.set_yticks(np.linspace(0,10,6))
ax.tick_params(axis='both', which='major', labelsize=15)

ani = animation.FuncAnimation(fig2,update_2,frames = len(iterations))
ani.save("test.mp4", dpi=200, fps=1)

However, 4 issues arise from this code:

  1. Even though I saved my animation to the ani variable, it doesn't seem to display the data associated with each different color.

  2. The title doesn't show up/update appropriately for each color.

  3. Calling on ax.legend produces the following error/Warning: No handles with labels found to put in legend.

  4. Trying to save the animation produces the following Error: MovieWriterRegistry' object is not an iterator

Could someone explain why these issues currently pop up and is there a better way to write/format my code for animated plots?

Have a look to this code:

import pandas as pd
import seaborn as sns
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np

test = pd.DataFrame({'color': ['red', 'blue', 'red', 'yellow', 'red', 'blue', 'yellow', 'yellow', 'red'],
                     'shape': ['sphere', 'sphere', 'sphere', 'cube', 'cube', 'cube', 'cube', 'sphere', 'cube'],
                     'score': [1, 7, 3, 8, 5, 8, 6, 2, 9],
                     'size': [2, 8, 4, 7, 9, 8, 3, 2, 1]})
iterations = test['color'].unique()

fig, ax = plt.subplots(figsize = (10, 8))
fig.subplots_adjust(top = 0.88, right = 0.85, bottom = 0.11, left = 0.12)

def update(i):
    ax.cla()
    fil_test = test[test['color'] == iterations[i]]
    fil_test = fil_test.sort_values(by = ['shape'])
    sns.scatterplot(x = 'size', y = 'score', hue = 'shape', ci = None, palette = 'Set1', data = fil_test)
    ax.set_title(f'Score vs. Size: {format(iterations[i]):>6} Shapes', fontsize = 20)
    ax.legend(loc = 'center left', bbox_to_anchor = (1, 0.5), prop = {'size': 12})
    ax.set_xlabel('size', fontsize = 16)
    ax.set_ylabel('score', fontsize = 16)
    ax.set_xlim(0, 10)
    ax.set_xlim(0, 10)
    ax.set_xticks(np.linspace(0, 10, 6))
    ax.set_yticks(np.linspace(0, 10, 6))
    ax.tick_params(axis = 'both', which = 'major', labelsize = 15)

ani = animation.FuncAnimation(fig, update, frames = len(iterations))
ani.save('test.mp4', dpi=200, fps=1)

plt.show()

I edited some things:

  1. as already explained by @Diziet Asahi , I replaced plt.clf() with ax.cla() in order to clean the axis at each frame
  2. moved plotting settings ( set_xlabel , set_xlim , set_xticks and so on) inside update function: in this way the figure is adjusted every cycle so it is fixed throughout the animation
  3. if you do not sort the filtered dataframe fil_test , the legend and color association will change with respect to the first value present in that dataframe. In order to avoid that, I added fil_test = fil_test.sort_values(by = ['shape']) : in this way the color-legend association for 'cube' and 'sphere' is fixed throughout the animation
  4. added fig.subplots_adjust(top = 0.88, right = 0.85, bottom = 0.11, left = 0.12) in order to make some space for the legend
  5. replaced r-string with f-string in set_title in order to fix the length of the title so as to improve its readability

Result:

在此处输入图像描述

Your issue is that you are deleting the ax object in your loop by calling plt.clf() . Instead, you should call plt.cla() which clear the content of the axes, but not the Axes themselves.

However, since you are clearing the axes, they go back to their original state, so you will probably want to reset the axes limit and formatting in the animate function as well

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM