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.
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'm writing a simple class to plot a sensor value in real-time; however, the animation does not run within the class.
I've tried to return the animation object to have an instance outside of the class but this does not work.
To my understanding, this is the same issue as raised in GitHub #1656
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from random import random
class Animate:
def __init__(self, sensor):
# Create figure for plotting
self.fig = plt.figure()
self.ax = self.fig.add_subplot(1, 1, 1)
self.xs = []
self.ys = []
self.ylabel = sensor
self.readings = 20
# This function is called periodically from FuncAnimation
def _update(self, i, xs, ys):
# Get sensor value
value = random()
# Add x and y to lists
self.xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
self.ys.append(value)
# Limit x and y lists to 20 items
self.xs = self.xs[-self.readings:]
self.ys = self.ys[-self.readings:]
# Draw x and y lists
self.ax.clear()
self.ax.plot(xs, ys)
# Format plot
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title(self.ylabel + ' over Time')
plt.ylabel(self.ylabel)
def start(self):
print('Starting')
# Set up plot to call animate() function periodically
self.anim = animation.FuncAnimation(self.fig, self._update, fargs=(self.xs, self.ys), interval=200)
plt.show();
rand = Animate('Torque')
rand.start();
your variables xs and ys are already named self.xs and self.ys, which are accessible in the class namespace; you do not need to pass them to self.update
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from random import random
class Animate:
def __init__(self, sensor):
# Create figure for plotting
self.fig = plt.figure()
self.ax = self.fig.add_subplot(1, 1, 1)
self.xs = []
self.ys = []
self.ylabel = sensor
self.readings = 20
# This function is called periodically from FuncAnimation
def _update(self, i):
# Read temperature (Celsius) from TMP102
temp_c = random()
# Add x and y to lists
self.xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
self.ys.append(temp_c)
# Limit x and y lists to 20 items
self.xs = self.xs[-self.readings:]
self.ys = self.ys[-self.readings:]
# Draw x and y lists
self.ax.clear()
self.ax.plot(self.xs, self.ys)
# Format plot
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title(self.ylabel + ' over Time')
plt.ylabel(self.ylabel)
def start(self):
print('Starting')
# Set up plot to call animate() function periodically
self.anim = animation.FuncAnimation(self.fig, self._update, interval=200)
plt.show()
rand = Animate('Torque')
rand.start()
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 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()
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