How to make jupyter HTML-matplotlib animation with seaborn heatmap? - python

I trying to make HTML(anim.to_html5_video) animation work in jupyter with seaborn heatmap.
First, I get working working samples from documentation, and make "pure matplotlib" image map animated example, it worked, with small problem ("parasite output" in animation cell)
Then, I tried to make it work with seaborn.heatmap… but failed. Animation looks like "infinite mirror" — obviously something wrong with matplotlib axes/plot composition, but I can't get it.
Common initialization cell:
import pandas as pd
import seaborn as sns
import numpy as np
%matplotlib inline
#%matplotlib notebook # Tried both, not needed for animation.
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
Animation worked, but "unwanted static output image exists":
fig, ax = plt.subplots()
nx = 50
ny = 50
line2d, = ax.plot([], [], lw=2)
def init():
line2d.set_data([], [])
ax.imshow(np.zeros((nx, ny)))
return (line2d,)
def animate(i):
data = np.random.rand(nx, ny)
ax.set_title('i: ' + str(i))
ax.imshow(data)
return (line2d,)
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=10, interval=1000, blit=False)
HTML(anim.to_html5_video())
So, looks that all OK with my jupyter setup (packages, ffmpeg, etc).
But, I cannot get how to make it with seaborn.heatmap:
fig, ax = plt.subplots()
nx = 50
ny = 50
line2d, = ax.plot([], [], lw=2)
ax_global = ax
def init_heatmap():
line2d.set_data([], [])
sns.heatmap(np.zeros((nx, ny)), ax=ax_global)
return (line2d,)
def animate_heatmap(i):
data = np.random.rand(nx, ny)
sns.heatmap(data, ax=ax_global)
ax.set_title('Frame: ' + str(i))
return (line2d,)
anim = animation.FuncAnimation(fig, animate_heatmap, init_func=init_heatmap,
frames=10, interval=1000, blit=True)
HTML(anim.to_html5_video())
Both samples ready to test on github
Of course, I want to see animation with random map and "stable heat-axes"
but get this
https://vimeo.com/298786185/

You can toggle the "colorbar". From the Seaborn.heatmap documentation, you need to change sns.heatmap(data, ax=ax_global) to sns.heatmap(data, ax=ax_global, cbar=False) and also do the same inside the init_heatmap().

Related

Animate labels using FuncAnimation in Matplotlib

I am not able to make (animated) labels using FuncAnimation from matplotlib. Please find below a minimal code that I made. ax.annotate has no effect at all - the animation itself works though. What can I change to get animated labels/titles, which are different for each frame?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
fig.clear()
steps = 10
data = np.random.rand(20,20,10)
imagelist = [data[:,:,i] for i in range(steps) ]
im = plt.imshow(imagelist[0], cmap='Greys', origin='lower', animated=True)
plt.colorbar(shrink=1, aspect=30, label='Counts')
# does not work
ax.annotate("Frame: %d " % steps,(0.09,0.92),xycoords ='figure fraction')
def updatefig(j):
im.set_array(imagelist[j])
return [im]
ani = animation.FuncAnimation(fig, updatefig, frames=range(steps), interval=200, blit=True)
plt.show()
Two problems overall:
The annotation text never gets updated in updatefig()
The canvas gets cleared+blitted, which wipes out annotations
Five steps to resolve:
Remove fig.clear() to preserve annotations
Save the initial annotation's handle
Update the annotation's text in updatefig()
Include the annotation in the return of updatefig()
Set blit=False to preserve annotations
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
#1 do NOT call fig.clear()
steps = 10
data = np.random.rand(20, 20, steps)
im = plt.imshow(data[:, :, 0], cmap='Greys', origin='lower', animated=True)
plt.colorbar(shrink=1, aspect=30, label='Counts')
#2 annotate frame 0 and save handle
annot = ax.annotate('Frame: 0', (0.09, 0.92), xycoords='figure fraction')
def updatefig(j):
im.set_array(data[:, :, j])
#3 update annotation text
annot.set_text(f'Frame: {j}')
#4 include annotation when returning
return im, annot
#5 set blit=False
anim = animation.FuncAnimation(fig, updatefig, frames=steps, blit=False)

Plotting a live graph using matplotlib

I am trying a code to plot a live graph but i always land up with an empty plot. Here is my code :
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
import random
style.use('fivethirtyeight')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
y = random.randint(0,100) # generate random data
x = i # set x as iteration number
ax1.clear()
ax1.plot(x, y, 'ro')
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
I get warning but i am using plt.show() to show animation. Not sure what i am doing wrong :
UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. `anim`, that exists until you have outputted the Animation using `plt.show()` or `anim.save()`.
warnings.warn(

Matplotlib animation update title using ArtistAnimation

I am trying to use the ArtistAnimation to create an animation. And everything is working, except set_title isn't working. I don't understand why blit=False doesn't work.
Do I need to go to a FuncAnimation?
for time in np.arange(-0.5,2,0.01):
writer.UpdatePipeline(time=time)
df=pd.read_csv(outputPath + '0.csv', sep=',')
df['x'] = 1E6*df['x']
df['Efield'] = 1E-6*df['Efield']
line3, = ax3.plot(df['x'], df['Efield'])
line1A, = ax1.semilogy(df['x'], df['em_lin'])
line1B, = ax1.semilogy(df['x'], df['Arp_lin'])
line2A, = ax2.plot(df['x'], df['Current_em'])
line2B, = ax2.plot(df['x'], df['Current_Arp'])
ax1.set_title('$t = ' + str(round(time, n)))
ims.append([line1A, line1B, line2A, line2B, line3])
im_ani = animation.ArtistAnimation(fig, ims, interval=50, blit=False)
im_ani.save(outputPath + 'lines.gif', writer='imagemagick', fps=10, dpi=100)
plt.show()
Two problems. The immeadiate is that the title is not part of the list of artists to update, hence the animation cannot know that you want to update it.
The more profound problem is that there is only a single title per axes. Hence even if you include the title in the list of artists, it will always show the text that it has last been set to.
The solution would be not to use the axes' title to update, but other text elements, one per frame.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
a = np.random.rand(10,10)
fig, ax=plt.subplots()
container = []
for i in range(a.shape[1]):
line, = ax.plot(a[:,i])
title = ax.text(0.5,1.05,"Title {}".format(i),
size=plt.rcParams["axes.titlesize"],
ha="center", transform=ax.transAxes, )
container.append([line, title])
ani = animation.ArtistAnimation(fig, container, interval=200, blit=False)
plt.show()
For reference the same as FuncAnimation would look as follows, where the title can be set directly as usual.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
a = np.random.rand(10,10)
fig, ax=plt.subplots()
ax.axis([-0.5,9.5,0,1])
line, = ax.plot([],[])
def animate(i):
line.set_data(np.arange(len(a[:,i])),a[:,i])
ax.set_title("Title {}".format(i))
ani = animation.FuncAnimation(fig,animate, frames=a.shape[1], interval=200, blit=False)
plt.show()

How to make a Matplotlib animated violinplot?

I am trying to animate a violinplot, so I have started off with something I think should be very basic, but it is not working. I think the problem is that violinplot doesn't accept set_data, but I don't otherwise know how to pass the changing data to violinplot. For this example I would like a plot where the mean slowly shifts to higher values. If I am barking up the wrong tree, please advise on a code which does work to animate violinplot.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
data = np.random.rand(100)
def animate(i):
v.set_data(data+i) # update the data
return v
v = ax.violinplot([])
ax.set_ylim(0,200)
v_ani = animation.FuncAnimation(fig, animate, np.arange(1, 200),
interval=50, blit=True)
Indeed, there is no set_data method for the violinplot. The reason is probably, that there is a lot of calculations going on in the background when creating such a plot and it consists of a lot of different elements, which are hard to update.
The easiest option would be to simply redraw the violin plot and not use blitting.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
data = np.random.normal(loc=25, scale=20, size=100)
def animate(i, data):
ax.clear()
ax.set_xlim(0,2)
ax.set_ylim(0,200)
data[:20] = np.random.normal(loc=25+i, scale=20, size=20)
np.random.shuffle(data)
ax.violinplot(data)
animate(0)
v_ani = animation.FuncAnimation(fig, animate, np.arange(1, 200),
fargs=(data,), interval=50, blit=False)
plt.show()

How to update tick labels in animated matplotlib graph

How can I get the tick labels to update once changed in an animated graph?
Here is just a simple example of what I need. I realize I can set blit=False and have it work, however the project I am working on requires blit=True for performance reasons.
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
import time
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x, np.sin(x))
def animate(i):
line.set_ydata(np.sin(x + i/10.0))
if i > 30:
ax.tick_params(axis='y', colors='red') ### How do I get my graph to reflect this change?
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
interval=100, blit=True)
plt.show()
I got it working with the monkey patch suggested in this post.
Hope this works for wandering souls (like me) that spent A LOT of time trying to solve this problem.

Categories