Matplotlib animated histogram colormap/gradient - python

I am trying to animate a histogram using matplotlib and I want to show the different bars using a colormap, e.g:
I have this working when I clear the complete figure every frame and then redraw everything. But this is very slow, so I am trying out the example by matplotlib itself.
This works and is very fast, but unfortunately I have no idea on how to specify a colormap because it is using the patches.PathPatch object to draw the histogram now. I can only get it to work with the same single color for every individual bar.
How can I specify a gradient or colormap to achieve the desired result shown above?
Here is an example of a working animation with a single color which I am currently using.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.animation as animation
# Fixing random state for reproducibility
np.random.seed(19680801)
# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)
nverts = nrects * (1 + 3 + 1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5, 0] = left
verts[0::5, 1] = bottom
verts[1::5, 0] = left
verts[1::5, 1] = top
verts[2::5, 0] = right
verts[2::5, 1] = top
verts[3::5, 0] = right
verts[3::5, 1] = bottom
patch = None
def animate(i):
# simulate new data coming in
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
top = bottom + n
verts[1::5, 1] = top
verts[2::5, 1] = top
return [patch, ]
fig, ax = plt.subplots()
barpath = path.Path(verts, codes)
patch = patches.PathPatch(
barpath, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())
ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True)
plt.show()

I recommend u using BarContainer, you can change bar color individually. In your example, the path is single object, matplotlib seems not to support gradient color for a single patch (not sure though).
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
# histogram our data with numpy
data = np.random.randn(1000)
colors = plt.cm.coolwarm(np.linspace(0, 1, 100))
def animate(i):
data = np.random.randn(1000)
bc = ax.hist(data, 100)[2]
for i, e in enumerate(bc):
e.set_color(colors[i])
return bc
fig, ax = plt.subplots(1, 1, figsize=(7.2, 7.2))
ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True)

Related

How to plot an animated matrix in matplotlib

I need to do step by step some numerical calculation algorithms visually, as in the figure below: (gif)
Font
How can I do this animation with matplotlib? Is there any way to visually present these transitions? As transformation of matrices, sum, transposition, using a loop and it presenting the transitions etc.
My goal is not to use graphics but the same matrix representation. This is to facilitate the understanding of the algorithms.
Since matrices can be plotted easily with imshow, one could create such table with an imshow plot and adjust the data according to the current animation step.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.animation
#####################
# Array preparation
#####################
#input array
a = np.random.randint(50,150, size=(5,5))
# kernel
kernel = np.array([[ 0,-1, 0], [-1, 5,-1], [ 0,-1, 0]])
# visualization array (2 bigger in each direction)
va = np.zeros((a.shape[0]+2, a.shape[1]+2), dtype=int)
va[1:-1,1:-1] = a
#output array
res = np.zeros_like(a)
#colorarray
va_color = np.zeros((a.shape[0]+2, a.shape[1]+2))
va_color[1:-1,1:-1] = 0.5
#####################
# Create inital plot
#####################
fig = plt.figure(figsize=(8,4))
def add_axes_inches(fig, rect):
w,h = fig.get_size_inches()
return fig.add_axes([rect[0]/w, rect[1]/h, rect[2]/w, rect[3]/h])
axwidth = 3.
cellsize = axwidth/va.shape[1]
axheight = cellsize*va.shape[0]
ax_va = add_axes_inches(fig, [cellsize, cellsize, axwidth, axheight])
ax_kernel = add_axes_inches(fig, [cellsize*2+axwidth,
(2+res.shape[0])*cellsize-kernel.shape[0]*cellsize,
kernel.shape[1]*cellsize,
kernel.shape[0]*cellsize])
ax_res = add_axes_inches(fig, [cellsize*3+axwidth+kernel.shape[1]*cellsize,
2*cellsize,
res.shape[1]*cellsize,
res.shape[0]*cellsize])
ax_kernel.set_title("Kernel", size=12)
im_va = ax_va.imshow(va_color, vmin=0., vmax=1.3, cmap="Blues")
for i in range(va.shape[0]):
for j in range(va.shape[1]):
ax_va.text(j,i, va[i,j], va="center", ha="center")
ax_kernel.imshow(np.zeros_like(kernel), vmin=-1, vmax=1, cmap="Pastel1")
for i in range(kernel.shape[0]):
for j in range(kernel.shape[1]):
ax_kernel.text(j,i, kernel[i,j], va="center", ha="center")
im_res = ax_res.imshow(res, vmin=0, vmax=1.3, cmap="Greens")
res_texts = []
for i in range(res.shape[0]):
row = []
for j in range(res.shape[1]):
row.append(ax_res.text(j,i, "", va="center", ha="center"))
res_texts.append(row)
for ax in [ax_va, ax_kernel, ax_res]:
ax.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
ax.yaxis.set_major_locator(mticker.IndexLocator(1,0))
ax.xaxis.set_major_locator(mticker.IndexLocator(1,0))
ax.grid(color="k")
###############
# Animation
###############
def init():
for row in res_texts:
for text in row:
text.set_text("")
def animate(ij):
i,j=ij
o = kernel.shape[1]//2
# calculate result
res_ij = (kernel*va[1+i-o:1+i+o+1, 1+j-o:1+j+o+1]).sum()
res_texts[i][j].set_text(res_ij)
# make colors
c = va_color.copy()
c[1+i-o:1+i+o+1, 1+j-o:1+j+o+1] = 1.
im_va.set_array(c)
r = res.copy()
r[i,j] = 1
im_res.set_array(r)
i,j = np.indices(res.shape)
ani = matplotlib.animation.FuncAnimation(fig, animate, init_func=init,
frames=zip(i.flat, j.flat), interval=400)
ani.save("algo.gif", writer="imagemagick")
plt.show()
This example sets up the animation inline in a Jupyter notebook. I suppose there's probably also a way to export as a gif, but I haven't looked into that so far.
Anyway, first thing to do is set up the table. I borrowed heavily from Export a Pandas dataframe as a table image for the render_mpl_table code.
The (adapted) version for this problem is:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from IPython.display import HTML
import six
width = 8
data = pd.DataFrame([[0]*width,
[0, *np.random.randint(95,105,size=width-2), 0],
[0, *np.random.randint(95,105,size=width-2), 0],
[0, *np.random.randint(95,105,size=width-2), 0]])
def render_mpl_table(data, col_width=3.0, row_height=0.625, font_size=14,
row_color="w", edge_color="black", bbox=[0, 0, 1, 1],
ax=None, col_labels=data.columns,
highlight_color="mediumpurple",
highlights=[], **kwargs):
if ax is None:
size = (np.array(data.shape[::-1]) + np.array([0, 1])) *
np.array([col_width, row_height])
fig, ax = plt.subplots(figsize=size)
ax.axis('off')
mpl_table = ax.table(cellText=data.values, bbox=bbox, colLabels=col_labels,
**kwargs)
mpl_table.auto_set_font_size(False)
mpl_table.set_fontsize(font_size)
for k, cell in six.iteritems(mpl_table._cells):
cell.set_edgecolor(edge_color)
if k in highlights:
cell.set_facecolor(highlight_color)
elif data.iat[k] > 0:
cell.set_facecolor("lightblue")
else:
cell.set_facecolor(row_color)
return fig, ax, mpl_table
fig, ax, mpl_table = render_mpl_table(data, col_width=2.0, col_labels=None,
highlights=[(0,2),(0,3),(1,2),(1,3)])
In this case, the cells to highlight in a different color are given by an array of tuples that specify the row and column.
For the animation, we need to set up a function that draws the table with different highlights:
def update_table(i, *args, **kwargs):
r = i//(width-1)
c = i%(width-1)
highlights=[(r,c),(r,c+1),(r+1,c),(r+1,c+1)]
for k, cell in six.iteritems(mpl_table._cells):
cell.set_edgecolor("black")
if k in highlights:
cell.set_facecolor("mediumpurple")
elif data.iat[k] > 0:
cell.set_facecolor("lightblue")
else:
cell.set_facecolor("white")
return (mpl_table,)
This forcibly updates the colors for all cells in the table. The highlights array is computed based on the current frame. The width and height of the table are kind of hard-coded in this example, but that shouldn't be super hard to change based on the shape of your input data.
We create an animation based on the existing fig and update function:
a = animation.FuncAnimation(fig, update_table, (width-1)*3,
interval=750, blit=True)
And lastly we show it inline in our notebook:
HTML(a.to_jshtml())
I put this together in a notebook on github, see https://github.com/gurudave/so_examples/blob/master/mpl_animation.ipynb
Hope that's enough to get you going in the right direction!

ArtistAnimation of subplots with different framerates

Consider the following code which implements ArtistAnimation to animate two different subplots within the same figure object.
import numpy as np
import itertools
import matplotlib.pyplot as plt
import matplotlib.mlab as ml
import matplotlib.animation as animation
def f(x,y,a):
return ((x/a)**2+y**2)
avals = np.linspace(0.1,1,10)
xaxis = np.linspace(-2,2,9)
yaxis = np.linspace(-2,2,9)
xy = itertools.product(xaxis,yaxis)
xy = list(map(list,xy))
xy = np.array(xy)
x = xy[:,0]
y = xy[:,1]
fig, [ax1,ax2] = plt.subplots(2)
ims = []
for a in avals:
xi = np.linspace(min(x), max(x), len(x))
yi = np.linspace(min(y), max(y), len(y))
zi = ml.griddata(x, y, f(x, y, a), xi, yi, interp='linear') # turn it into grid data, this is what imshow takes
title = plt.text(35,-4,str(a), horizontalalignment = 'center')
im1 = ax1.imshow(zi, animated = True, vmin = 0, vmax = 400)
im2 = ax2.imshow(zi, animated=True, vmin=0, vmax=400)
ims.append([im1,im2, title])
ani = animation.ArtistAnimation(fig, ims, interval = 1000, blit = False)
plt.show()
In this case the number of items in im1 and im2 are the same, and the frame rate for each subplot is identical.
Now, imagine I have 2 lists with different numbers of items, and that I wish ArtistAnimate to go through the frames in the same total time. Initially I thought of manipulating the interval keyword in the ArtistAnimation call but this implies that you can set different intervals for different artists, which I don't think is possible.
Anyway, I think the basic idea is pretty clear len(im1) is not equal to len(im2), but the animation needs to go through them all in the same amount of time.
Is there any way to do this please? Thanks
EDIT
While I try out the answer provided below, I should add that I would rather use ArtistAnimation due to the structure of my data. If there are no alternatives I will revert to the solution below.
Yes that is possible, kinda, using Funcanimation and encapsulating your data inside func.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
arr1 = np.random.rand(300,3,4)
arr2 = np.random.rand(200,5,6)
fig, (ax1, ax2) = plt.subplots(1,2)
img1 = ax1.imshow(arr1[0])
img2 = ax2.imshow(arr2[0])
# set relative display rates
r1 = 2
r2 = 3
def animate(ii):
if ii % r1:
img1.set_data(arr1[ii/r1])
if ii % r2:
img2.set_data(arr2[ii/r2])
return img1, img2
ani = animation.FuncAnimation(fig, func=animate, frames=np.arange(0, 600))
plt.show()

Make a point move on the plot without clearing earlier plots in matplotlib [duplicate]

I tried to write a simple script which updates a scatter plot for every timestep t. I wanted to do it as simple as possible. But all it does is to open a window where I can see nothing. The window just freezes. It is maybe just an small error, but I can not find it.
The the data.dat has the format
x y
Timestep 1 1 2
3 1
Timestep 2 6 3
2 1
(the file contains just the numbers)
import numpy as np
import matplotlib.pyplot as plt
import time
# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
particles = []
for line in fp:
line = line.split()
if line:
line = [float(i) for i in line]
particles.append(line)
T = 100
numbParticles = 2
x, y = np.array([]), np.array([])
plt.ion()
plt.figure()
plt.scatter(x,y)
for t in range(T):
plt.clf()
for k in range(numbP):
x = np.append(x, particles[numbParticles*t+k][0])
y = np.append(y, particles[numbParticles*t+k][1])
plt.scatter(x,y)
plt.draw()
time.sleep(1)
x, y = np.array([]), np.array([])
The simplest, cleanest way to make an animation is to use the matplotlib.animation module.
Since a scatter plot returns a matplotlib.collections.PathCollection, the way to update it is to call its set_offsets method. You can pass it an array of shape (N, 2) or a list of N 2-tuples -- each 2-tuple being an (x,y) coordinate.
For example,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
T = 100
numbParticles = 2
particles = np.random.random((T,numbParticles)).tolist()
x, y = np.array([]), np.array([])
def init():
pathcol.set_offsets([[], []])
return [pathcol]
def update(i, pathcol, particles):
pathcol.set_offsets(particles[i])
return [pathcol]
fig = plt.figure()
xs, ys = zip(*particles)
xmin, xmax = min(xs), max(xs)
ymin, ymax = min(ys), max(ys)
ax = plt.axes(xlim=(xmin, xmax), ylim=(ymin, ymax))
pathcol = plt.scatter([], [], s=100)
anim = animation.FuncAnimation(
fig, update, init_func=init, fargs=(pathcol, particles), interval=1000, frames=T,
blit=True, repeat=True)
plt.show()
I finally found a solution. You can do it simply by using this script. I tried to keep it simple:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# Helps me to get the data from the file I want to plot
N = 0
# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
particles = []
for line in fp:
line = line.split()
particles.append(line)
# Create new Figure and an Axes which fills it.
fig = plt.figure(figsize=(7, 7))
ax = fig.add_axes([0, 0, 1, 1], frameon=True)
border = 100
ax.set_xlim(-border, border), ax.set_xticks([])
ax.set_ylim(-border, border), ax.set_yticks([])
# particle data
p = 18 # number of particles
myPa = np.zeros(p, dtype=[('position', float, 2)])
# Construct the scatter which we will update during animation
scat = ax.scatter(myPa['position'][:, 0], myPa['position'][:, 1])
def update(frame_number):
# New positions
myPa['position'][:] = particles[N*p:N*p+p]
# Update the scatter collection, with the new colors, sizes and positions.
scat.set_offsets(myPa['position'])
increment()
def increment():
global N
N = N+1
# Construct the animation, using the update function as the animation director.
animation = FuncAnimation(fig, update, interval=20)
plt.show()

Matplotlib funcanimation blit slow

I'm having issues with a slow animation in Matplotlib. I'm animating results from a simulation, which is easiest visualized with an array of rectangles that change color with time.
Following recommendations here, I'm using blitting to only draw the (small fraction) of rectangles that change in each frame. I also tried to implement this using FuncAnimation, but when using that with Blit=True, the script runs much slower.
I'm wondering if this is because I'm returning all of the rectangles to FuncAnimation, so it redraws all of them even if they haven't changed. Is there a way to pass different artists at each frame to FuncAnimation? I tried just passing a tuple of the ones that had changed (the commented out block in the "animate" function), but that led to seemingly random animation frames...
Use:
$ python2 [script].py blit
$ python2 [script].py anim
Thanks!
import sys
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import matplotlib.animation as manim
def animate_data(plot_type):
"""
Use:
python2 plot_anim.py [option]
option = anim OR blit
"""
# dimension parameters
Nx = 30
Ny = 20
numtimes = 100
size = 0.5
if plot_type == "blit":
# "interactive mode on"
plt.ion()
# Prepare to do initial plot
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.set_aspect('equal', 'box')
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
# An array in which to store the rectangle artists
rects = np.empty((Nx, Ny), dtype=object)
# Generate initial figure of all green rectangles
for (i,j),k in np.ndenumerate(rects):
color = 'green'
rects[i, j] = plt.Rectangle([i - size / 2, j - size / 2],
size, size, facecolor=color, edgecolor=color)
ax.add_patch(rects[i, j])
ax.autoscale_view()
# "Old" method using fig.canvas.blit()
if plot_type == "blit":
plt.show()
fig.canvas.draw()
# Step through time updating the rectangles
for tind in range(1, numtimes):
updated_array = update_colors(rects)
for (i, j), val in np.ndenumerate(updated_array):
if val:
ax.draw_artist(rects[i, j])
fig.canvas.blit(ax.bbox)
# New method using FuncAnimate
elif plot_type == "anim":
def animate(tind):
updated_array = update_colors(rects)
# # Just pass the updated artists to FuncAnimation
# toupdate = []
# for (i, j), val in np.ndenumerate(updated_array):
# if val:
# toupdate.append(rects[i, j])
# return tuple(toupdate)
return tuple(rects.reshape(-1))
ani = manim.FuncAnimation(fig, animate, frames=numtimes,
interval=10, blit=True, repeat=False)
plt.show()
return
# A function to randomly update a few rectangles
def update_colors(rects):
updated_array = np.zeros(rects.shape)
for (i, j), c in np.ndenumerate(rects):
rand_val = np.random.rand()
if rand_val < 0.003:
rects[i, j].set_facecolor('red')
rects[i, j].set_edgecolor('red')
updated_array[i, j] = 1
return updated_array
if __name__ == "__main__":
if len(sys.argv) > 1:
plot_type = sys.argv[1]
else:
plot_type = "blit"
animate_data(plot_type)
Update 600 rectangles every frame is very slow, cbar_blit mode in your code is faster because you only update the rectangles which's color is changed. You can use PatchCollection to speedup drawing, here is the code:
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import matplotlib.animation as manim
from matplotlib.collections import PatchCollection
Nx = 30
Ny = 20
numtimes = 100
size = 0.5
x, y = np.ogrid[-1:1:30j, -1:1:20j]
data = np.zeros((numtimes, Nx, Ny))
for i in range(numtimes):
data[i] = (x-i*0.02+1)**2 + y**2
colors = plt.cm.rainbow(data)
fig, ax = plt.subplots()
rects = []
for (i,j),c in np.ndenumerate(data[0]):
rect = plt.Rectangle([i - size / 2, j - size / 2],size, size)
rects.append(rect)
collection = PatchCollection(rects, animated=True)
ax.add_collection(collection)
ax.autoscale_view(True)
def animate(tind):
c = colors[tind].reshape(-1, 4)
collection.set_facecolors(c)
return (collection,)
ani = manim.FuncAnimation(fig, animate, frames=numtimes,
interval=10, blit=True, repeat=False)
plt.show()

Updating a matplotlib bar graph?

I have a bar graph which retrieves its y values from a dict. Instead of showing several graphs with all the different values and me having to close every single one, I need it to update values on the same graph. Is there a solution for this?
Here is an example of how you can animate a bar plot.
You call plt.bar only once, save the return value rects, and then call rect.set_height to modify the bar plot.
Calling fig.canvas.draw() updates the figure.
import matplotlib
matplotlib.use('TKAgg')
import matplotlib.pyplot as plt
import numpy as np
def animated_barplot():
# http://www.scipy.org/Cookbook/Matplotlib/Animations
mu, sigma = 100, 15
N = 4
x = mu + sigma*np.random.randn(N)
rects = plt.bar(range(N), x, align = 'center')
for i in range(50):
x = mu + sigma*np.random.randn(N)
for rect, h in zip(rects, x):
rect.set_height(h)
fig.canvas.draw()
fig = plt.figure()
win = fig.canvas.manager.window
win.after(100, animated_barplot)
plt.show()
I've simplified the above excellent solution to its essentials, with more details at my blogpost:
import numpy as np
import matplotlib.pyplot as plt
numBins = 100
numEvents = 100000
file = 'datafile_100bins_100000events.histogram'
histogramSeries = np.loadtext(file)
fig, ax = plt.subplots()
rects = ax.bar(range(numBins), np.ones(numBins)*40) # 40 is upper bound of y-axis
for i in range(numEvents):
for rect,h in zip(rects,histogramSeries[i,:]):
rect.set_height(h)
fig.canvas.draw()
plt.pause(0.001)

Categories

Resources