I am trying to run this code below but it is not working properly. I've followed the documentation from matplotlib and wonder what is wrong with this simple code below. I am tryting to animate this into jupyter notebook with anaconda distro. My python version is 2.7.10.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
def init():
m = np.zeros(4800)
m[0] = 1.6
return m
def animate(i):
for a in range(1,4800):
m[a] = 1.6
m[a-1] = 0
return m
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
You need to create an actual plot. Just updating a NumPy array is not enough.
Here is an example that likely does what you intend. Since it is necessary to access the same objects at multiple places, a class seems better suited as it allows to access instance attributes via self:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
class MyAni(object):
def __init__(self, size=4800, peak=1.6):
self.size = size
self.peak = peak
self.fig = plt.figure()
self.x = np.arange(self.size)
self.y = np.zeros(self.size)
self.y[0] = self.peak
self.line, = self.fig.add_subplot(111).plot(self.x, self.y)
def animate(self, i):
self.y[i - 1] = 0
self.y[i] = self.peak
self.line.set_data(self.x, self.y)
return self.line,
def start(self):
self.anim = animation.FuncAnimation(self.fig, self.animate,
frames=self.size, interval=20, blit=False)
if __name__ == '__main__':
ani = MyAni()
ani.start()
plt.show()
Related
I'm trying to plot the results from each iteration of an algorithm (data points are obtained quite fast). I´ve already looked at matplotlib.animation and a bunch of solutions using other packages but none of them uses an OOP approach.
I came up with this code:
import matplotlib.animation as animation
import random
class LivePlot:
def __init__(self, title, x_label, y_label):
self.x = []
self.y = []
self.fig = plt.figure()
self.ax = self.fig.add_subplot(1, 1, 1)
# Create a blank line (will be updated in animate)
self.line, = self.ax.plot([], [])
# Add labels
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.title(title)
self.anim = animation.FuncAnimation(self.fig, self.animate,
init_func=self.__init,
blit=True)
def __init(self):
self.line.set_data([], [])
return self.line,
def animate(self, i):
# Update line with new Y values
self.line.set_data(self.x, self.y)
return self.line,
# Set up plot to call animate() function periodically
def plot(self, xs, ys):
self.x.append(xs)
self.y.append(ys)
plt.show()
if __name__ == '__main__':
pl = LivePlot(title='test', x_label='iter', y_label='score')
for x in range(100):
y = random.randint(1,4)
pl.plot(x, y)
It doesn´t work, the plot window disappear almost immediately without plotting any data.
I am getting data to a Raspberry Pi from some sensors.
Once the animation starts, I have not found a way to get it to stop the animation and then execute the rest of the code in the program.
I have tried quit() and animation.event_source.stop() to no avail. I read the documentation and it looks like the method animation.FuncAnimation() is some sort of loop that calls animate() and never ends in my case. Here are a few versions of my code below. Nothing changes between version below the commented out line.
from gpiozero import MCP3008
from timeit import default_timer
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
# This is specific to the ADC I am using for my sensor
ch2 = MCP3008(2)
vals = []
timer = []
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
#this is the fuction used as a parameter for the animation.FuncAnimation
def animate(i):
timer.append(default_timer())
vals.append(ch2.value)
ax1.clear()
ax1.plot(timer,vals)
#______________________________________
try:
ani = animation.FuncAnimation(fig, animate, interval = 50)
plt.show()
except KeyboardInterrupt:
plt.close("all")
#The plot created below is for saving the final set of collected data
plt.plot(timer,vals)
plt.xlabel("Time(s)")
plt.ylabel("V")
plt.savefig('/home/pi/programs/pics/plot.jpg')
plt.close('all')
quit()
The idea was that you would press control c, then the rest of the code would execute and the program would end, but the animation keeps running until I keyboard interrupt multiple times, and the rest of the code(under except) never runs. I have also tried...
from gpiozero import MCP3008
from timeit import default_timer
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
# This is specific to the ADC I am using for my sensor
ch2 = MCP3008(2)
vals = []
timer = []
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
#this is the fuction used as a parameter for the animation.FuncAnimation
def animate(i):
timer.append(default_timer())
vals.append(ch2.value)
ax1.clear()
ax1.plot(timer,vals)
#______________________________________
ani = animation.FuncAnimation(fig, animate, interval = 50)
plt.show()
commmand = input('Type q and press enter to quit')
if commmand == 'q':
plt.close("all")
#The plot created below is for saving the final set of collected data
plt.plot(timer,vals)
plt.xlabel("Time(s)")
plt.ylabel("V")
plt.savefig('/home/pi/programs/pics/plot.jpg')
plt.close('all')
quit()
I also tried putting print statements in various places after the plt.show after the line where ani is assigned, and the code never gets past that point.
Any tips?
The code after plt.show() will only execute, once the window that is shown is closed. At that point you do not have the figure available in pyplot to use plt.savefig any more. However, you may very well create a new plot, like you're doing already in the code and the second version of the code should run fine once you close the matplotlib window.
#from gpiozero import MCP3008 # not available here
from timeit import default_timer
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# emulate sensor
class MCP3008():
def __init__(self, r):
self.x = 0.5
self.r = r
def value(self):
self.x = self.r*self.x*(1.-self.x)
return self.x
ch2 = MCP3008(3.62)
vals = []
timer = []
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
#this is the fuction used as a parameter for the animation.FuncAnimation
def animate(i):
timer.append(default_timer())
vals.append(ch2.value())
ax1.clear()
ax1.plot(timer,vals, marker=".", ls="")
ani = animation.FuncAnimation(fig, animate, interval = 50)
plt.show()
plt.close("all")
#The plot created below is for saving the final set of collected data
plt.plot(timer,vals)
plt.xlabel("Time(s)")
plt.ylabel("V")
plt.savefig('plot.jpg')
plt.close('all')
If you want to keep the plot open and save the plot upon a key press, the following would be an option. It saves the actualy plot in the state when the q key is pressed. (Also, the axes is not cleared every iteration, but only the line data is updated, just to show that approach).
#from gpiozero import MCP3008 # not available here
from timeit import default_timer
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# emulate sensor
class MCP3008():
def __init__(self, r):
self.x = 0.5
self.r = r
def value(self):
self.x = self.r*self.x*(1.-self.x)
return self.x
ch2 = MCP3008(3.62)
vals = []
timer = []
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
line, = ax1.plot([],[], marker=".", ls="")
ax1.set_xlabel("Time(s)")
ax1.set_ylabel("V")
#this is the fuction used as a parameter for the animation.FuncAnimation
def animate(i):
timer.append(default_timer())
vals.append(ch2.value())
line.set_data(timer,vals)
ax1.relim()
ax1.autoscale_view()
ani = animation.FuncAnimation(fig, animate, interval = 50)
def press(event):
if event.key == 'q':
ani.event_source.stop()
fig.savefig("plot.png")
print("Plot saved")
cid = fig.canvas.mpl_connect('key_press_event', press)
plt.show()
I would like to plot data using multiprocessing and then creating animation with the plotted data. I mean something like this:
frames = []
def get_frames()
...
[index, frame] = mp_queue.get()
frames[index]=frame
def get_frames_process(queue, index, x_vals, y_vals):
frame = plt.scatter(x_vals[index], y_vals[index])
queue.put([index, frame])
def animate(frame):
frames.pop(0)
plt.plot(frame)
anim = animation.FuncAnimation(fig, animate, frames=frames)
Or also, there is a way of use FuncAnimation with multiprocessing?
From the FuncAnimation documentation:
frames can be a generator, an iterable, or a number of frames.
I suggest you write a generator function that uses multiprocessing to iterate through your frame calculations. Here's an example:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from multiprocessing import Pool
def calc_fib(n):
if n in (0, 1):
return 1
return calc_fib(n-1) + calc_fib(n-2)
class FibonacciAnimation(object):
def __init__(self, count):
self.count = count
self.line, = plt.plot([], [], 'r-')
self.pool = Pool(8)
def update(self, n):
self.line.set_data(self.xs, self.ys)
return self.line,
def frames(self):
for n in range(self.count):
self.xs = range(n)
self.ys = self.pool.map(calc_fib, self.xs)
yield
fig = plt.figure()
fib = FibonacciAnimation(30)
plt.xlim(0, fib.count)
plt.ylim(0, 1000000)
plt.title('Fibonacci Animation')
fib_ani = animation.FuncAnimation(fig, fib.update, fib.frames,
interval=50, blit=True)
plt.show()
I made the Fibonacci calculation deliberately inefficient so you can compare map to self.pool.map and see the effect of multiprocessing.
I have a simple python animation program that I downloaded. I am changing it around to understand animation calls a little better. This program makes a simple dot move in a circle on the screen. I placed the functions that change the dot's position and animation calls each into a class. That worked ok. Then I put the definition of the figure into a class. That works ok too. This is how it looks,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class FIG:
def __init__(self):
self.fig=plt.figure()
self.ax = self.fig.add_subplot(111)
self.line, = self.ax.plot([], [], 'bo', ms=10)
self.ax.set_ylim(-1, 1)
self.ax.set_xlim(-1, 1)
class sD:
def simData(self):
t_max = 100.0
dt = 0.001
x = 0.0
t = 0.0
while t < t_max:
x = np.sin(np.pi*t)
t = t + dt
y = np.cos(np.pi*t)
yield x, y
class sP:
def simPoints(self,simData):
x, t = simData[0], simData[1]
f1.line.set_data(t, x)
return f1.line #line2#, time_text
f1=FIG()
sd=sD()
sp=sP()
ani = animation.FuncAnimation(f1.fig, sp.simPoints, sd.simData, blit=False,
interval=10, repeat=True)
plt.show()
Now, I want to pass the figure instace as an argument through the animation call. But it does not work, this is the new code that does not work....
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class FIG:
def __init__(self):
self.fig=plt.figure()
self.ax = self.fig.add_subplot(111)
self.line, = self.ax.plot([], [], 'bo', ms=10)
self.ax.set_ylim(-1, 1)
self.ax.set_xlim(-1, 1)
class sD:
def simData(self):
t_max = 100.0
dt = 0.001
x = 0.0
t = 0.0
while t < t_max:
x = np.sin(np.pi*t)
t = t + dt
y = np.cos(np.pi*t)
yield x, y
class sP:
def simPoints(self,simData,figg):
x, t = simData[0], simData[1]
figg.line.set_data(t, x)
return figg.line #line2#, time_text
f1=FIG()
sd=sD()
sp=sP()
ani = animation.FuncAnimation(f1.fig, sp.simPoints, sd.simData, f1, blit=False,
interval=10, repeat=True)
plt.show()
The compiler tells me,
self._drawn_artists = self._init_func()
AttributeError: FIG instance has no __call__ method
Any comments are appreciated.
Thanks
I've been trying to save an animated scatterplot with matplotlib, and I would prefer that it didn't require totally different code for viewing as an animated figure and for saving a copy. The figure shows all the datapoints perfectly after the save completes.
This code is a modified version of Giggi's on Animating 3d scatterplot in matplotlib, with a fix for the colors from Yann's answer on Matplotlib 3D scatter color lost after redraw (because colors will be important on my video, so I want to make sure they work).
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.fig.canvas.mpl_connect('draw_event',self.forceUpdate)
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,frames=20)
def change_angle(self):
self.angle = (self.angle + 1)%360
def forceUpdate(self, event):
self.scat.changed()
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[:,1]) , np.ma.ravel(data[:,2]) )
plt.draw()
return self.scat,
def show(self):
plt.show()
if __name__ == '__main__':
a = AnimatedScatter()
a.ani.save("movie.avi", codec='avi')
a.show()
A perfectly valid .avi is generated by this, but it's blank for all of the four seconds except for the axes. The actual figure always shows exactly what I want to see. How can I populate the save function's plots the same way I populate a normally running animation, or is it possible in matplotlib?
EDIT: Using a scatter call in the update (without setting the bounds as in the initializer) causes the .avi to show the axes growing, showing that the data is being run each time, it's just not showing on the video itself.
I am using matplotlib 1.1.1rc with Python 2.7.3.
Remove blit=True from FuncAnimation and animated=True from scatter and it works. I suspect that there is something going wrong with the logic that makes sure only the artists that need to be updated are updated/redrawn between frames (rather than just re-drawing everything).
Below is exactly what I ran and I got the expected output movie:
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.fig.canvas.mpl_connect('draw_event',self.forceUpdate)
self.ax = self.fig.add_subplot(111,projection = '3d')
self.ani = animation.FuncAnimation(self.fig, self.update, interval=100,
init_func=self.setup_plot, frames=20)
def change_angle(self):
self.angle = (self.angle + 1)%360
def forceUpdate(self, event):
self.scat.changed()
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)
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)
self.scat._offsets3d = ( np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,1]) , np.ma.ravel(data[:,2]) )
return self.scat,
def show(self):
plt.show()
if __name__ == '__main__':
a = AnimatedScatter()
a.ani.save("movie.avi", codec='avi')
a.show()