Related
I want to create a simple animation to show my data changes.
create a 3 * 3 grid.
I have an array which is 20 * 9. The data is read into the animation line by line.
Color = [[0,0,0,0,0,0,0,0,0],
[100,0,0,0,100,0,0,0,0,0],
[80,0,80,0,80,100,0,0,0]
......]
I hope the list for the grid is read line by line and each line works for 100ms. If the number is more than 0, the color of the grid change into red. For example, in the first 100ms, all color is black, then, in the second 100ms, grid number 0 and grid number 4 change to red and last for 100ms. In the third 100ms, grid number 0,2,4,5 change to red and last for 100ms.
My current version of the code looks like this. I don't know how to draw like what I describe above.
%matplotlib notebook ## show in jupyter
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
patch = plt.Circle((5, -5), 0.75, fc='y')
def init():
patch.center = (5, 5)
ax.add_patch(patch)
return patch,
def animate(i):
x, y = patch.center
x = 5 + 3 * np.sin(np.radians(i))
y = 5 + 3 * np.cos(np.radians(i))
patch.center = (x, y)
return patch,
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=360,
interval=20,
blit=True)
plt.show()
I would appreciate it a lot if anyone could help me!!!
Cool question! I have an answer that works but it changes the structure of your Colors list. Here's the answer with an explanation below.
%matplotlib notebook
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
patch = plt.Circle((5, -5), 0.75, fc='y')
colors = [
[["black", None, None], ["black", None, "black"], [None, None, "red"]],
[["black", None, None], ["black", None, "red"], ["black", None, "red"]],
[["red", None, None], [None, None, None], [None, None, "red"]],
]
def get_coords(colors):
y = 0
x = 0
coords = []
for row in colors:
x = 0
for entry in row:
if entry:
coords.append([entry, x, y])
x += 3.33
y += 3.33
return coords
def get_grids(coord):
return [plt.Rectangle((x[1], x[2]), 3.33, 3.33, fc=x[0]) for x in coord]
coords = [get_coords(color) for color in colors]
grids = [get_grids(coord) for coord in coords]
def init():
patch.center = (5, 5)
ax.add_patch(patch)
return patch,
def animate(i):
patches = []
if (i % 100 == 0):
ax.patches = []
next_grid = grids.pop(0)
for rectangle in next_grid:
patches.append(ax.add_patch(rectangle))
x, y = patch.center
x = 5 + 3 * np.sin(np.radians(i))
y = 5 + 3 * np.cos(np.radians(i))
patch.center = (x, y)
patches.append(ax.add_patch(patch))
return patches
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=360,
interval=20,
blit=True)
plt.show()
The key idea is adding and removing plt.Rectangles to give the appearance of a grid. Because your graph is 10x10, these rectangles are squares of side length 10/3 =~ 3.33.
I think it's easier to use 20 * 3 * 3 instead of a 20 * 9 list for the colors. I use the following:
colors = [
[["black", None, None], ["black", None, "black"], [None, None, "red"]],
[["black", None, None], ["black", None, "red"], ["black", None, "red"]],
[["red", None, None], [None, None, None], [None, None, "red"]],
]
Each entry in this list, as yours, is a grid. Within these grids, however, are rows, each with a desired color entry. colors[0][0] == ["black", None, None] means at the first frame of the animation, the bottom left corner of the grid will be black and the rest of the bottom transparent. colors[0][1] == ["black", None, "black"] means the middle of the grid will have the left and right thirds black with the middle transparent.
The get_coords and get_grids functions are pretty hairy and clearly hard-coded to support a 3x3 grid with dimensions 10x10 - it'd be cool to parameterize that out down the line.
Only other important idea is that to change the animation every 100ms, we just check if i in the animate function is divisible by 100. If it is, we clear the existing patches (so we're not just adding rectangles ad nausem) and plot our new ones. When the grid list runs out, the grid will be transparent for the rest of the animation.
Hope this helps - happy plotting!
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
Color = [[0,0,0,0,0,0,0,0,0],
[100,0,0,0,100,0,0,0,0,0],
[80,0,80,0,80,100,0,0,0]]
fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)
fig.set_tight_layout(True)
ax = plt.axes(xlim=(0, 6), ylim=(0, 6))
patch = []
patch.append(plt.Rectangle(xy = (0,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (0,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (0,4),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,4),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,4),width = 2,height = 2,fill = True,color = 'k'))
def init():
for i in range(9):
patch[i].set_color('k')
ax.add_patch(patch[i])
return patch
def animate(i):
value = (np.array(Color[i]) == 0)
for j in range(9):
patch[j].set_color('k' if value[j] else 'r')
return patch
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=3,
interval=1000,
blit=True)
plt.show()
I'm trying to make a small animation of particles moving left or right but the figure just closes immediately after running. I tried to read some tips here in the forum but nothing helped.
Can't understand why the animation does not work for me.
I looked at some examples and I do not find the mistake I made.
Could you help me?
import numpy as np
import random
from scipy.spatial.distance import pdist, squareform
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import matplotlib.animation as animation
class ParticleBox:
"""
init_state is a 3D array of x,y coordinates + probability to go left or right
bounds is the size of the box: [xmin, xmax, ymin, ymax]
"""
def __init__(self,
init_state, bounds = [-9, 9, -2, 2],
size = 0.04, step_size=0.05):
self.init_state = np.asarray(init_state, dtype=float)
self.size = size
self.state = self.init_state.copy()
self.time_elapsed = 0
self.bounds = bounds
self.ycorbeg=range(len(self.state))
def step(self, dts):
pr=0.4
pl=0.4
ps=0.2
self.time_elapsed+=dts
for i in range(len(self.state)):
print c
c=random.random()
if c<pr:
print c
self.state[i,2]=ze
elif (pr<=c and c<pr+pl):
self.state[i,2]=-step_size
else:
self.state[i,0]=self.state[i,0]
self.state[:, 0] += dt * self.state[:, 2]
np.random.seed(0)
beginx=np.random.rand(1000)-0.5
lenbegin=len(beginx)
def beginy(lenbegin,maxy=250):
i=0
beginy=np.zeros(lenbegin)
while i<lenbegin:
for j in range(int(-maxy/2),int(maxy/2)):
beginy[i]=j/50.0
i+=1
if len(beginx)==len(beginy):
ze=np.zeros(len(beginy))
print zip(beginx,beginy,ze)[0]
return zip(beginx,beginy,ze)
else:
raise ValueError
init_state=beginy(lenbegin)
box = ParticleBox(init_state)
dts = 1. / 30
#------------------------------------------------------------
# set up figure and animation
fig = plt.figure()
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False,
xlim=(-3.2, 3.2), ylim=(-2.4, 2.4))
# particles holds the locations of the particles
particles, = ax.plot([], [], 'bo', ms=6)
# rect is the box edge
rect = plt.Rectangle(box.bounds[::2],
box.bounds[1] - box.bounds[0],
box.bounds[3] - box.bounds[2],
ec='none', lw=2, fc='none')
ax.add_patch(rect)
def init():
"""initialize animation"""
global box, rect
particles.set_data([], [])
rect.set_edgecolor('none')
return particles, rect
def animate(i):
"""perform animation step"""
global box, rect, dts, fig
box.step(dts)
ms = int(fig.dpi * 2 * box.size * fig.get_figwidth()
/ np.diff(ax.get_xbound())[0])
# update pieces of the animation
rect.set_edgecolor('k')
particles.set_data(box.state[:, 0], box.state[:, 1])
particles.set_markersize(ms)
return particles, rect
anim = animation.FuncAnimation(fig, animate, frames=600,
interval=10, blit=True, init_func=init)
plt.show()
I want to draw an animated chart using python3.5.4's matplotlib package, the examples of matplotlib official website works well on my local Python environment.
But those codes I wrote can not show me any chart, I can not figure out what's problem in those codes, so I come here to look for some help. Here are my codes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
class Plot(object):
def __init__(self, update_func, frames):
self.x_data, self.y_data = [], []
self.update_func = update_func
self.frames = frames
self.t = 0
def draw(self):
fig = plt.figure()
self.ax = plt.axes()
self.line, = self.ax.plot([1, 2, 3, 4], [1, 2, 3, 4], lw=2)
# Without invoke the FuncAnimation can display the chart.
self.ani_ref = FuncAnimation(fig, self._update, frames=self.frames, blit=True,
interval=20, init_func=self._animation_init)
plt.show()
def _animation_init(self):
self.line.set_data(self.x_data, self.y_data)
return self.line
def _update(self, i):
# modified the data from outside update function
self.x_data, self.y_data = self.update_func(self.x_data, self.y_data)
x_min, x_max = self.ax.get_xlim()
y_min, y_max = self.ax.get_ylim()
if np.max(self.x_data) >= x_max:
x_max = np.max(self.x_data) + 10
if np.min(self.x_data) <= x_min:
x_min = np.min(self.x_data) - 10
if np.max(self.y_data) >= y_max:
y_max = np.max(self.y_data) + 10
if np.min(self.y_data) <= y_min:
y_min = np.min(self.y_data) - 10
self.ax.set_xlim(x_min, x_max)
self.ax.set_ylim(y_min, y_max)
self.ax.figure.canvas.draw()
self.line.set_data(self.x_data, self.y_data)
return self.line
if __name__ == "__main__":
def update(x_data, y_data):
x, y = x_data[-1], np.sin(2 * np.pi * (x_data[-1] + 0.1))
x_data.append(x)
y_data.append(y)
return x_data, y_data
p = Plot(update_func=update, frames=100)
p.draw()
I have 3 dimensional data say (5,100,100). Now I would like to see them slice by slice upon hitting the down arrow button.
I adapted the code from an answer I gave to this question a couple of weeks ago to include keystrokes.
The left key will scroll backwards, the right key will scroll forwards.
import matplotlib.widgets
import matplotlib.patches
import mpl_toolkits.axes_grid1
class PageSlider(matplotlib.widgets.Slider):
def __init__(self, ax, label, numpages = 10, valinit=0, valfmt='%1d',
closedmin=True, closedmax=True,
dragging=True, **kwargs):
self.facecolor=kwargs.get('facecolor',"w")
self.activecolor = kwargs.pop('activecolor',"b")
self.fontsize = kwargs.pop('fontsize', 10)
self.numpages = numpages
self.fig = ax.figure
super(PageSlider, self).__init__(ax, label, 0, numpages,
valinit=valinit, valfmt=valfmt, **kwargs)
self.poly.set_visible(False)
self.vline.set_visible(False)
self.pageRects = []
for i in range(numpages):
facecolor = self.activecolor if i==valinit else self.facecolor
r = matplotlib.patches.Rectangle((float(i)/numpages, 0), 1./numpages, 1,
transform=ax.transAxes, facecolor=facecolor)
ax.add_artist(r)
self.pageRects.append(r)
ax.text(float(i)/numpages+0.5/numpages, 0.5, str(i+1),
ha="center", va="center", transform=ax.transAxes,
fontsize=self.fontsize)
self.valtext.set_visible(False)
divider = mpl_toolkits.axes_grid1.make_axes_locatable(ax)
bax = divider.append_axes("right", size="5%", pad=0.05)
fax = divider.append_axes("right", size="5%", pad=0.05)
self.button_back = matplotlib.widgets.Button(bax, label=ur'$\u25C0$',
color=self.facecolor, hovercolor=self.activecolor)
self.button_forward = matplotlib.widgets.Button(fax, label=ur'$\u25B6$',
color=self.facecolor, hovercolor=self.activecolor)
self.button_back.label.set_fontsize(self.fontsize)
self.button_forward.label.set_fontsize(self.fontsize)
self.button_back.on_clicked(self.backward)
self.button_forward.on_clicked(self.forward)
#connect keys:
self.fig.canvas.mpl_connect('key_press_event', self.keyevent)
def _update(self, event):
super(PageSlider, self)._update(event)
i = int(self.val)
if i >=self.valmax:
return
self._colorize(i)
def _colorize(self, i):
for j in range(self.numpages):
self.pageRects[j].set_facecolor(self.facecolor)
self.pageRects[i].set_facecolor(self.activecolor)
def forward(self, event):
current_i = int(self.val)
i = current_i+1
if (i < self.valmin) or (i >= self.valmax):
return
self.set_val(i)
self._colorize(i)
def backward(self, event):
current_i = int(self.val)
i = current_i-1
if (i < self.valmin) or (i >= self.valmax):
return
self.set_val(i)
self._colorize(i)
# define keyevent, left: backwards, right: forwards
def keyevent(self, event):
#print event.key
if event.key == 'right':
self.forward(event)
if event.key == 'left':
self.backward(event)
self.fig.canvas.draw()
if __name__ == "__main__":
import numpy as np
from matplotlib import pyplot as plt
num_pages = 5
data = np.random.rand(num_pages, 100, 100)
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.18)
im = ax.imshow(data[0, :, :], cmap='viridis', interpolation='nearest')
ax_slider = fig.add_axes([0.1, 0.05, 0.8, 0.04])
slider = PageSlider(ax_slider, 'Page', num_pages, activecolor="orange")
def update(val):
i = int(slider.val)
im.set_data(data[i, :,:])
slider.on_changed(update)
plt.show()
I'm trying to get a 3d animation of a scatterplot in matplotlib, based off the 2d scatterplot animation posted here and the 3d line plot posted here.
The problems arise from set_data and set_offsets not working in 3D, so you're supposed to use set_3d_properties to tack on the z information. Playing around with that it usually chokes, but with the code posted below it runs. However, the transparency increases enough that the points just fade away after a few frames. What am I doing wrong here? I want the points to jump around within the bounds of the box for a while. Even adjusting the step size to something very small doesn't slow down the transparency.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
FLOOR = -10
CEILING = 10
class AnimatedScatter(object):
def __init__(self, numpoints=5):
self.numpoints = numpoints
self.stream = self.data_stream()
self.angle = 0
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.ani = animation.FuncAnimation(self.fig, self.update, interval=100,
init_func=self.setup_plot, blit=True)
def change_angle(self):
self.angle = (self.angle + 1)%360
def setup_plot(self):
x, y, z = next(self.stream)
c = ['b', 'r', 'g', 'y', 'm']
self.scat = self.ax.scatter(x, y, z,c=c, s=200, animated=True)
self.ax.set_xlim3d(FLOOR, CEILING)
self.ax.set_ylim3d(FLOOR, CEILING)
self.ax.set_zlim3d(FLOOR, CEILING)
return self.scat,
def data_stream(self):
data = np.zeros((3, self.numpoints))
xyz = data[:3, :]
while True:
xyz += 2 * (np.random.random((3, self.numpoints)) - 0.5)
yield data
def update(self, i):
data = next(self.stream)
data = np.transpose(data)
self.scat.set_offsets(data[:,:2])
#self.scat.set_3d_properties(data)
self.scat.set_3d_properties(data[:,2:],'z')
self.change_angle()
self.ax.view_init(30,self.angle)
plt.draw()
return self.scat,
def show(self):
plt.show()
if __name__ == '__main__':
a = AnimatedScatter()
a.show()
Found the solution finally, here is how to update points w/o touching colors:
from mpl_toolkits.mplot3d.art3d import juggle_axes
scat._offsets3d = juggle_axes(xs, ys, zs, 'z')
this is internally done by set_3d_properties along with re-initializing colors
I've found this, and more generic, solution:
You shold add np.ma.ravel( x_data ) ... before inserting your data in the collection.
But the scatter plot don't seems to be intended for animations; it's too slow.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
FLOOR = -10
CEILING = 10
class AnimatedScatter(object):
def __init__(self, numpoints=5):
self.numpoints = numpoints
self.stream = self.data_stream()
self.angle = 0
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.ani = animation.FuncAnimation(self.fig, self.update, interval=100,
init_func=self.setup_plot, blit=True)
def change_angle(self):
self.angle = (self.angle + 1)%360
def setup_plot(self):
X = next(self.stream)
c = ['b', 'r', 'g', 'y', 'm']
self.scat = self.ax.scatter(X[:,0], X[:,1], X[:,2] , c=c, s=200, animated=True)
self.ax.set_xlim3d(FLOOR, CEILING)
self.ax.set_ylim3d(FLOOR, CEILING)
self.ax.set_zlim3d(FLOOR, CEILING)
return self.scat,
def data_stream(self):
data = np.zeros(( self.numpoints , 3 ))
xyz = data[:,:3]
while True:
xyz += 2 * (np.random.random(( self.numpoints,3)) - 0.5)
yield data
def update(self, i):
data = next(self.stream)
data = np.transpose(data)
self.scat._offsets3d = ( np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,0]) )
self.change_angle()
self.ax.view_init(30,self.angle)
plt.draw()
return self.scat,
def show(self):
plt.show()
if __name__ == '__main__':
a = AnimatedScatter()
a.show()