matplotlib artist animation : title or text not changing - python

I tried using matplotlib's ArtistAnimation. The text and titles of the figure are supposed to change in each frame, but they don't.
I have read tons of posts on similar problems, but I still don't understand what is the solution. The demos don't show updating titles as far as I could find.
If anybody out there knows, I would be grateful!
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig =plt.figure()
ims=[]
for iternum in range(4):
plt.title(iternum)
plt.text(iternum,iternum,iternum)
ims.append([plt.scatter(np.random.randint(0,10,5), np.random.randint(0,20,5),marker='+' )])
#plt.cla()
ani = animation.ArtistAnimation(fig, ims, interval=500, blit=False,
repeat_delay=2000)
plt.show()

To animate artists, you have to return a reference to each artists in your ims[] array, including the Text objects.
However it doesn't work for the title, I don't know why. Maybe somebody with a better understanding of the mechanisms involved will be able to enlighten us.
Nevertheless, the title is just a Text object, so we can produce the desired effect using:
fig = plt.figure()
ax = fig.add_subplot(111)
ims=[]
for iternum in range(4):
ttl = plt.text(0.5, 1.01, iternum, horizontalalignment='center', verticalalignment='bottom', transform=ax.transAxes)
txt = plt.text(iternum,iternum,iternum)
ims.append([plt.scatter(np.random.randint(0,10,5), np.random.randint(0,20,5),marker='+' ), ttl, txt])
#plt.cla()
ani = animation.ArtistAnimation(fig, ims, interval=500, blit=False,
repeat_delay=2000)

You need to supply the artists to animate as a list of sequences to the ArtistAnimation. In the code from the question you only supply the scatter, but not the text and title.
Unfortunately the title is also part of the axes and thus will not change even if supplied. So you may use a normal text instead.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims=[]
for iternum in range(4):
title = plt.text(0.5,1.01,iternum, ha="center",va="bottom",color=np.random.rand(3),
transform=ax.transAxes, fontsize="large")
text = ax.text(iternum,iternum,iternum)
scatter = ax.scatter(np.random.randint(0,10,5), np.random.randint(0,20,5),marker='+')
ims.append([text,scatter,title,])
ani = animation.ArtistAnimation(fig, ims, interval=500, blit=False,
repeat_delay=2000)
plt.show()
You may consider using FuncAnimation instead of ArtistAnimation. This would allow to change the title easily.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims=[]
text = ax.text(0,0,0)
scatter = ax.scatter(np.random.randint(0,10,5), np.random.randint(0,20,5),marker='+')
def update(iternum):
plt.title(iternum)
text.set_position((iternum, iternum))
text.set_text(str(iternum))
scatter.set_offsets(np.random.randint(0,10,(5,2)))
ani = animation.FuncAnimation(fig, update, frames=4, interval=500, blit=False,
repeat_delay=2000)
plt.show()

Related

Animating a kdeplot python

Im trying to annimate a kdeplot in python.
The idea is to evaluate actions over time.
The script below almost works as planned, but instead of updating the plot, it overlaps the plots and becomes very slow after a few runs.
So I can see that my problem might be that i dont really understand the animate func and have now becom very lost in trying to understand the problem.
So i hope that soembody can see the problem and help me.
import numpy as np
from matplotlib import animation
import matplotlib.pyplot as plt
import pandas as pd
import cmasher as cmr
from mplsoccer import VerticalPitch
df = pd.DataFrame(np.random.randint(0,100,size=(1000, 2)), columns=list('xy'))
#%%
pitch_dark = VerticalPitch(line_color='#cfcfcf', line_zorder=2, pitch_color='#122c3d')
fig, ax = pitch_dark.draw()
#kdeplot_dark = pitch_dark.kdeplot([], [], ax=ax, cmap=cmr.voltage, shade=True, levels=100)
def init():
kdeplot_dark = pitch_dark.kdeplot(0,0, ax=ax, cmap=cmr.voltage, shade=True, levels=100)
def animate(i):
x = df.x.iloc[i:i+20]
y = df.y.iloc[i:i+20]
kdeplot_dark = pitch_dark.kdeplot(x,y, ax=ax, cmap=cmr.voltage, shade=True, levels=100)
anim = animation.FuncAnimation(fig, animate, init_func=init)
plt.show()

How to add a colorbar to geopandas plot with matplotlib animation

I am trying to animate a geopandas plot unsing FuncAnimation. Basically eu_private_map is a geopandas.geodataframe.GeoDataFrame and among other fields, it contains several columns with the values for parameter x in different EU states across 10 consecutive years. Each column are the values of x for one year.
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(year):
ax1.clear()
my_map = eu_private_map.plot(ax=ax1, column=year, vmin=0, vmax=30, legend=False)
divider = make_axes_locatable(ax1)
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(my_map, cax=cax)
# the FuncAnimation function iterates through our animate function using the steps array
years = eu_private.columns.drop('country').tolist()
ani = FuncAnimation(fig, animate, years, interval=1000, blit=False)
HTML(ani.to_jshtml())
The colorbar is giving me a hard time. On the one hand, if I set legend = True, I do not like the fact that it does not have the same size (see figure) as the figure and it creates a weird effect (see figure). I tried this but I got an error: AttributeError: 'AxesSubplot' object has no attribute 'autoscale_None'. However I cannot spot the trouble in the code, any help to fix both issues at once? Thanks a lot
After trying many ways that failed to work for geopandas & animate, I ended up finding a solution to make it work being the main idea to separate the colorbar from the animate function to the init function of the plot.
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig = plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(1,1,1)
def animate(year):
ax1.clear()
eu_private_map.plot(color='gainsboro', ax=ax1, linewidth=0.6, edgecolor='1')
sm = eu_private_map.dropna().plot(ax=ax1, column=year, vmin=0, vmax=30, cmap = 'viridis',
legend=False, linewidth=0.6, edgecolor='1')
ax1.set_title(year, fontdict={'fontsize': '23'})
ax1.axis('off')
def init():
# Create colorbar as a legend
sm = plt.cm.ScalarMappable(cmap='viridis', norm=plt.Normalize(vmin=0, vmax=30))
# empty array for the data range
sm._A = []
# add the colorbar to the figure
divider = make_axes_locatable(ax1)
cax = divider.append_axes("right", size="5%", pad=0.5)
cbar = fig.colorbar(sm, cax=cax)
cbar.ax.tick_params(labelsize=14)
# the FuncAnimation function iterates through our animate function using the steps array
years = eu_private.columns.drop('country').tolist()[::-1]
ani = FuncAnimation(fig, animate, years, interval=1000, blit=False, init_func=init)
HTML(ani.to_jshtml())
Hei,
found a way to make a log scale color plot
norm=matplotlib.colors.LogNorm() does the trick:
A logarithmic colorbar in matplotlib scatter plot
and you can also add vmin and vmax like this:
norm=colors.LogNorm(vmin=vmin, vmax=vmax)
good to remember though it works only for vmin and vmax positive. E.g.
norm=colors.LogNorm(vmin=0.1, vmax=1000000)
For new answer-seekers who are stumbling on to this, the idea is to use init_func parameter for the FuncAnimation function. This was answered here.
the init_func will render the colorbar whereas the animate function will not render the colorbar.
Limitation: I am assuming the colorbar and its ticks do not change and this answer will keep the old colorbar intact while updating the chart axes.

Why does the output of animation.FuncAnimation have to be bound to a name? [duplicate]

I am trying to do an animation using the FuncAnimation module, but my code only produces one frame and then stops. It seems like it doesn't realize what it needs to update. Can you help me what went wrong?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
def animate(i):
PLOT.set_data(x[i], np.sin(x[i]))
print("test")
return PLOT,
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
def animate(i):
PLOT.set_data(x[:i], np.sin(x[:i]))
# print("test")
return PLOT,
ani = animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
You need to keep a reference to the animation object around, otherwise it gets garbage collected and it's timer goes away.
There is an open issue to attach a hard-ref to the animation to the underlying Figure object.
As written, your code well only plot a single point which won't be visible, I changed it a bit to draw up to current index

How to embed matplotlib funcAnimation object within PyQT GUI [duplicate]

I am trying to do an animation using the FuncAnimation module, but my code only produces one frame and then stops. It seems like it doesn't realize what it needs to update. Can you help me what went wrong?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
def animate(i):
PLOT.set_data(x[i], np.sin(x[i]))
print("test")
return PLOT,
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
def animate(i):
PLOT.set_data(x[:i], np.sin(x[:i]))
# print("test")
return PLOT,
ani = animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
You need to keep a reference to the animation object around, otherwise it gets garbage collected and it's timer goes away.
There is an open issue to attach a hard-ref to the animation to the underlying Figure object.
As written, your code well only plot a single point which won't be visible, I changed it a bit to draw up to current index

How can i make points of a python plot appear over time?

I would like to create a animation where my data points would gradually appear on my graph and freeze when all the data points have appeared. I've seen in done with correlations i'm just not too sure how to do it with just individual points themselves
This isn't something that will show anything particularly useful but i though it would look cool since i am trying to visualize some location data on a map
I know this isn't very clear so please as for clarifications, I'm not too sure how to phrase my problem very well.
Thanks
matplotlib.animation.FuncAnimation is the right tool for you. First create an empty graph, and then gradually add data points to it in the function. The following piece of code will illustrate it:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph, = plt.plot([], [], 'o')
def animate(i):
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, frames=10, interval=200)
plt.show()
The result (saved as gif file) is shown below:
EDIT: To make the animation look stopped when finished in matplotlib window, you need to make it infinite (omit frames parameter in FuncAnimation), and set the frame counter to the last number in your frame series:
def animate(i):
if i > 9:
i = 9
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, interval=200)
Or, which is better, you can set repeat parameter in FuncAnimation to False, as per answer to this question.
EDIT 2: To animate a scatter plot, you need a whole bunch of other methods. A piece of code is worth a thousand words:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
plt.show()

Categories

Resources