I want to update the legends in using the ArtistAnimation from Matplotlib.
I try to adapt this solution : Matplotlib animation update title using ArtistAnimation to the legend but it didn't worked. I tried a bunch of others things too but nothing seems to work.
In this case, I got no error, the result is just not what's expected. The legend appears 1 time over 3, only for the green. And the result I want is that the legends change each time with the good color and label.
Here is a minimal example of what I've done :
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
a = np.random.rand(3,3)
fig, ax=plt.subplots()
container = []
colors = ["red","blue","green"]
labels = [0,1,2]
for i in range(a.shape[1]):
line, = ax.plot(a[:,i], color=colors[i])
title = ax.text(0.5,1.05,"Title {}".format(i),
size=plt.rcParams["axes.titlesize"],
ha="center", transform=ax.transAxes, )
legend = ax.legend(handles=[mpl.patches.Patch(color=colors[i], label=labels[i])])
container.append([line, title, legend])
ani = animation.ArtistAnimation(fig, container, interval=750, blit=False)
#ani.save('videos/test.gif')
plt.show()
Here is the result :
This work for me :
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
a = np.random.rand(3,3)
fig, ax=plt.subplots()
container = []
colors = ["red","blue","green"]
labels = [0,1,2]
for i in range(a.shape[1]):
line, = ax.plot(a[:,i], color=colors[i])
title = ax.text(0.5,1.05,"Title {}".format(i),
size=plt.rcParams["axes.titlesize"],
ha="center", transform=ax.transAxes, )
testList = [] # Do an array of the color you want to see
for j in range(len(colors)): # For the example I added all the colors but you can pick only the ones you want
testList.append(mpl.patches.Patch(color=colors[j], label=labels[j]))
legend = ax.legend(handles=testList) # Create the legend
ax.add_artist(legend) # Add manually the legend
container.append([line, title])
ani = animation.ArtistAnimation(fig, container, interval=750, blit=False)
#ani.save('videos/test.gif')
plt.show()
Source :
https://github.com/matplotlib/matplotlib/issues/12833
https://matplotlib.org/stable/tutorials/intermediate/legend_guide.html
Related
I am sorry for my poor English.
I have a matrix datas (10000 times 5000). It includes 10000 cases of data and the dimension of each data is 5000.
I want to make an animation to show each data one after another.
Following Code 1 works well.
(Code 1)
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ims = []
for i in range(10000):
im = plt.plot(masks[i,:])
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=10)
plt.show()
ani.save('output.mp4', writer="ffmpeg")
I want to add the time-varying title to know which data (data index) is shown at a certain time.
And I wrote the following Code 2.
(Code 2)
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ims = []
for i in range(10000):
im = plt.plot(masks[i,:])
tl = 'Data number:' + str(i+1) # ***added***
plt.title(tl) # ***added***
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=10)
plt.show()
ani.save('output.mp4', writer="ffmpeg")
However, I got an animation whose title is always 'Data number: 10000'.
How can I write the code to add the time-varying title?
I wrote plt.title(tl) before im = plt.plot(masks[i,:]) but nothing changed. Thank you for your help.
My environments are;
Python 3.6.9
matplitlib 3.3.3
We can imitate the figure title by annotating an axes object:
#test data generation
import numpy as np
np.random.seed(123)
masks = np.random.randn(10, 15)
#the animation routine starts here
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims = []
#iterating over the array
for i in range(masks.shape[0]):
#obtaining the Line2D object representing the line plot
im, = ax.plot(masks[i,:], color="blue")
#creating a centered annotation text above the graph
ann = ax.annotate(f"This is frame {i:.0f}.", (0.5, 1.03), xycoords="axes fraction", ha="center")
#collecting both objects for the animation
ims.append([im, ann])
ani = animation.ArtistAnimation(fig, ims, interval=300, repeat=False)
plt.show()
I make a boxplot with matplotlib:
import pandas as pd
import matplotlib.pyplot as plt
A = pd.DataFrame([54.183933149245775,98.14228839908178,97.56790596547185,81.28351460722497,116.36733517668105,93.64706288367272,107.68860349692736,109.65565349602194,88.58717530217115,54.87561132504807,137.89097514410435,116.90021701471281,121.41252555476005,102.68420408219474,107.32642696333856,
120.27307064490907,114.3674635060443,91.38936314166017,149.0476109186976,121.76625219213736,155.6027360469248,115.86331915425764,99.35036421024546,104.93804853361358,115.64286896238708,129.51583078514085,116.30239399660411,97.58582728510798,119.59975852978403,103.68594428632996], columns=['A'])
fig, ax = plt.subplots(1,1)
A.boxplot(grid=False, fontsize=12, notch=True,
flierprops = dict(markersize=10, markeredgecolor ='red', markerfacecolor='b'),
boxprops = dict(linewidth=2, color='red'))
fig.show()
The flier props will change the colors and marker size. However, for "boxprops", the linewidth can change but the color NEVER changes (here it stays blue). Does anybody know why? Also, where is the matplotlib documentation giving all the options for these properties?
You can do that by doing two things actually,
First, determine the return_type of your boxplot
Second, change the color of the boxes key like so:
Here, I will change the boxes into green
import pandas as pd
import matplotlib.pyplot as plt
A = pd.DataFrame([54.183933149245775,98.14228839908178,97.56790596547185,81.28351460722497,116.36733517668105,93.64706288367272,107.68860349692736,109.65565349602194,88.58717530217115,54.87561132504807,137.89097514410435,116.90021701471281,121.41252555476005,102.68420408219474,107.32642696333856,
120.27307064490907,114.3674635060443,91.38936314166017,149.0476109186976,121.76625219213736,155.6027360469248,115.86331915425764,99.35036421024546,104.93804853361358,115.64286896238708,129.51583078514085,116.30239399660411,97.58582728510798,119.59975852978403,103.68594428632996], columns=['A'])
fig, ax = plt.subplots(1,1)
bp = A.boxplot(grid=False, fontsize=12, notch=True,
flierprops = dict(markersize=10, markeredgecolor ='red', markerfacecolor='b'),
boxprops = dict(linewidth=2, color='red'),
return_type='dict') # add this argument
# set the color of the boxes to green
for item in bp['boxes']:
item.set_color('g')
plt.show()
And this will show the following graph:
I am a student and I am new to matplotlib animation.
I am trying to figure out how to animate zooming in towards the center of my 3d scatterplot, and I've included my code below. I am trying to get the zeroes to be at the middle of each axis so I am able to see the overall plot as a zoom in. I don't get an error whenever I run my code but when I run the animation the intervals change abruptly and don't seem to go in a certain pattern. Another thing I've noticed is that the zeroes are only sometimes in the middle of the axis, while the plot "glitches out".
Thank You.
import matplotlib.pylab as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
%matplotlib notebook
x = np.random.rand(100)*100
y = np.random.rand(100)*100
z = np.random.rand(100)*100
#setup figure
fig = plt.figure()
ax = fig.add_subplot(111, facecolor='LightCyan', projection = '3d')
#set up viewing window (in this case the 25 most recent values)
ax.set_xlim([-1, 1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
#sets up list of images for animation
plot = ax.scatter(x, y, z, color='b', marker= '*',)
def func(i):
x_lim = ax.set_xlim(-i,i)
y_lim = ax.set_ylim(-i, i)
z_lim = ax.set_zlim(-i, i)
return plot
ani = animation.FuncAnimation(fig, func, frames=100, interval=1000, blit=True)
I would like to draw animated subplots with ArtistAnimation. Unfortunately, I cannot figure out how to have an animated legend. I tried out different methods I found on StackOverflow. If I manage to get a legend it is not animated, but just the legends of all animation steps together.
My code looks like this:
import numpy as np
import pylab as pl
import matplotlib.animation as anim
fig, (ax1, ax2, ax3) = pl.subplots(1,3,figsize=(11,4))
ims = []
im1 = ['im11','im12','im13']
im2 = ['im21','im22','im23']
x = np.arange(0,2*np.pi,0.1)
n=50
for i in range(n):
for sp in (1,2,3):
pl.subplot(1,3,sp)
y1 = np.sin(sp*x + i*np.pi/n)
y2 = np.cos(sp*x + i*np.pi/n)
im1[sp-1], = pl.plot(x,y1)
im2[sp-1], = pl.plot(x,y2)
pl.xlim([0,2*np.pi])
pl.ylim([-1,1])
lab = 'i='+str(i)+', sp='+str(sp)
im1[sp-1].set_label([lab])
pl.legend(loc=2, prop={'size': 6}).draw_frame(False)
ims.append([ im1[0],im1[1],im1[2], im2[0],im2[1],im2[2] ])
ani = anim.ArtistAnimation(fig,ims,blit=True)
pl.show()
I thought this code would be equivalent to the method used here How to add legend/label in python animation but obviously I am missing something.
I also tried to set the labels as suggested in Add a legend for an animation (of Artists) in matplotlib but I do not really understand how to use it for my case. Like this
im2[sp-1].legend(handles='-', labels=[lab])
I get an AttributeError: 'Line2D' object has no attribute 'legend'.
[EDIT]: I did not state it clearly: I would like to have a legend for both lines in the plots.
I do not know what exactly the legend should look like, but I'd imaginge you simply want to let it display the current value of the one line from the current frame. You'd therefore better update the data of the line, instead of plotting 150 new plots.
import numpy as np
import pylab as plt
import matplotlib.animation as anim
fig, axes = plt.subplots(1,3,figsize=(8,3))
ims = []
im1 = [ax.plot([],[], label="label")[0] for ax in axes]
im2 = [ax.plot([],[], label="label")[0] for ax in axes]
x = np.arange(0,2*np.pi,0.1)
legs = [ax.legend(loc=2, prop={'size': 6}) for ax in axes]
for ax in axes:
ax.set_xlim([0,2*np.pi])
ax.set_ylim([-1,1])
plt.tight_layout()
n=50
def update(i):
for sp in range(3):
y1 = np.sin((sp+1)*x + (i)*np.pi/n)
y2 = np.cos((sp+1)*x + (i)*np.pi/n)
im1[sp].set_data(x,y1)
im2[sp].set_data(x,y2)
lab = 'i='+str(i)+', sp='+str(sp+1)
legs[sp].texts[0].set_text(lab)
legs[sp].texts[1].set_text(lab)
return im1 + im2 +legs
ani = anim.FuncAnimation(fig,update, frames=n,blit=True)
plt.show()
Although hours of browsing stackoverflow definitely improved my python animate code, I could not quite figure out one thing and therefore I'm turning towards the kind souls of the community in the hope that someone might be able to shred some light.
In a nutshell, I have a background image that is about 2000 x 1000 pixels let's say, I need to scatter some points over this image and animate the process and save whole thing as a video. I'm able to update the scatterplot as needed only with the exception that I can't remove the precedent scatterplot. So the output is not what I would really want. I would be glad if someone could have a glance at the code and see where the glitch is. I've used scat.remove() which seems to be doing nothing.
Thank you in advance folks.
import matplotlib.pyplot as plt
import pylab as pl
import numpy as np
from pylab import savefig
from matplotlib import animation
import matplotlib
######################################################
fig = plt.figure()
ax = plt.axes()
a = plt.imread('background.jpg')
im = plt.imshow(a)
#######################################################
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
scat = ax.scatter(0,0,color='white')
points = np.random.randint(5,size=10)
for j in points:
xy = randpair(j)
x = xy[0]
y = xy[1]
print x,y
if len(x) > 0 :
scat.remove()
scat = ax.scatter(x,y,color='r',s=18)
plt.xticks([])
plt.yticks([])
return scat,ax, # ax returns the text to be updated and scat returns the scatterplot.
anim = animation.FuncAnimation(fig, animate, 49,interval=300, blit=True)
writer = animation.writers['ffmpeg']
anim.save('film_3.mp4')
#plt.show()
In the code you already remove the last scatter before the loop is finished; so some scatter plots will be added and then removed immediately.
One can prevent this by collecting the scatters in a list, then removing the scatters in the list from the canvas using remove and cleaning the list.
Apart from that, returning the complete ax object is a little useless. So I would suggest to simply turn blitting off, since it will not matter for saving the animation.
Here is a complete code that worked for me:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig = plt.figure()
ax = plt.axes()
scats = []
a = np.random.rand(8,18)
im = ax.imshow(a, cmap="YlGn", vmin=0, vmax=3, extent=[0,2000,0,1000])
plt.xticks([])
plt.yticks([])
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
global scats
# first remove all old scatters
for scat in scats:
scat.remove()
scats=[]
# now draw new scatters
points = np.random.randint(5,size=10)
for j in points:
x, y = randpair(j)
if len(x) > 0 :
scats.append(ax.scatter(x,y,color='r',s=18))
anim = matplotlib.animation.FuncAnimation(fig, animate, 50,
interval=1000, blit=False)
writer = matplotlib.animation.FFMpegWriter(fps=15,
codec="h264",
extra_args=["-preset", "veryslow","-crf","0"])
anim.save(__file__+".mp4", writer=writer)
plt.show()