Speeding up an animation in matplotlib - python

I am trying to figure out how to speed up this animation. I want the whole thing to finish at 30 seconds.
I've tried adjusting the interval between frames, the save count, inside FuncAnimation, but it doesn't seem to work. Is there anyway to just set the total duration and have matplotlib squeeze everything into that time limit?
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation #1
n = 500
x = np.random.randn(n)
%matplotlib notebook
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000)+7
x4 = np.random.uniform(14,20, 10000)
def update(curr):
# check if animation is at the last frame, and if so, stop the animation a
if curr == n:
a.event_source.stop()
plt.cla()
plt.hist(x1[:curr], normed=True, bins=20, alpha=0.5)
plt.hist(x2[:curr], normed=True, bins=20, alpha=0.5)
plt.hist(x3[:curr], normed=True, bins=20, alpha=0.5)
plt.hist(x4[:curr], normed=True, bins=20, alpha=0.5)
plt.axis([-7,21,0,0.6])
plt.text(x1.mean()-1.5, 0.5, 'Normal')
plt.text(x2.mean()-1.5, 0.5, 'Gamma')
plt.text(x3.mean()-1.5, 0.5, 'Exponential')
plt.text(x4.mean()-1.5, 0.5, 'Uniform')
plt.annotate('n = {}'.format(curr), [3,27])
fig = plt.figure()
fig = plt.figure(figsize=(9,3))
a = animation.FuncAnimation(fig, update, interval=10, blit=True, save_count=500)
The final product looks like this:

I have been given a suggested answer:
...
def update(curr)
x = 100 #x as speed multiplier
curr = curr*x
if curr >= n:
a.event_source.stop()
...
The logic is basically to speed up the rate at which FuncAnimate plots each graph by taking a larger subsection of the array of values.

Related

Need help on animating a 2-D trajectory using FuncAnimation

I have an array x_trj that has shape (50,3), and I want to plot a 2-D trajectory using the 1st and the 2nd columns of this array (x & y coordinates respectively). This trajectory will be on top of a circle. Here is my code so far:
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-5, 5))
line, = ax.plot([], [], lw=2)
# Plot circle
theta = np.linspace(0, 2*np.pi, 100)
plt.plot(r*np.cos(theta), r*np.sin(theta), linewidth=5)
ax = plt.gca()
def animate(n):
# Plot resulting trajecotry of car
for n in range(x_trj.shape[0]):
line.set_xdata(x_trj[n,0])
line.set_ydata(x_trj[n,1])
return line,
anim = FuncAnimation(fig, animate,frames=200, interval=20)
However, the animation turns out to be a stationary figure. I checked out the Matplotlib animation example on the documentation page, but I still can't figure out what my animate(n) function should look like in this case. Can someone give me some hints?
The code below makes the following changes:
added some test data
in animate:
remove the for loop
only copy the part of the trajectory until the given n
in the call to FuncAnimation:
`frames should be equal to the given number of points (200 frames and 50 points doesn't work well)
interval= set to a larger number, as 20 milliseconds make things too fast for only 50 frames
added plt.show() (depending on the environment where the code is run, plt.show() will trigger the animation to start)
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
# create some random test data
x_trj = np.random.randn(50, 3).cumsum(axis=0)
x_trj -= x_trj.min(axis=0, keepdims=True)
x_trj /= x_trj.max(axis=0, keepdims=True)
x_trj = x_trj * 8 - 4
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-5, 5))
line, = ax.plot([], [], lw=2)
# Plot circle
theta = np.linspace(0, 2 * np.pi, 100)
r = 4
ax.plot(r * np.cos(theta), r * np.sin(theta), linewidth=5)
def animate(n):
line.set_xdata(x_trj[:n, 0])
line.set_ydata(x_trj[:n, 1])
return line,
anim = FuncAnimation(fig, animate, frames=x_trj.shape[0], interval=200)
# anim.save('test_trajectory_animation.gif')
plt.show()

How to show sliding windows of a numpy array with matplotlib FuncAnimation

I am developing a simple algorithm for the detection of peaks in a signal. To troubleshoot my algorithm (and to showcase it), I would like to observe the signal and the detected peaks all along the signal duration (i.e. 20 minutes at 100Hz = 20000 time-points).
I thought that the best way to do it would be to create an animated plot with matplotlib.animation.FuncAnimation that would continuously show the signal sliding by 1 time-points and its superimposed peaks within a time windows of 5 seconds (i.e. 500 time-points). The signal is stored in a 1D numpy.ndarray while the peaks information are stored in a 2D numpy.ndarray containing the x and y coordinates of the peaks.
This is a "still frame" of how the plot would look like.
Now the problem is that I cannot wrap my head around the way of doing this with FuncAnimation.
If my understanding is correct I need three main pieces: the init_func parameter, a function that create the empty frame upon which the plot is drawn, the func parameter, that is the function that actually create the plot for each frame, and the parameter frames which is defined in the help as Source of data to pass func and each frame of the animation.
Looking at examples of plots with FuncAnimation, I can only find use-cases in which the data to plot are create on the go, like here, or here, where the data to plot are created on the basis of the frame.
What I do not understand is how to implement this with data that are already there, but that are sliced on the basis of the frame. I would thus need the frame to work as a sort of sliding window, in which the first window goes from 0 to 499, the second from 1to 500 and so on until the end of the time-points in the ndarray, and an associated func that will select the points to plot on the basis of those frames. I do not know how to implement this.
I add the code to create a realistic signal, to simply detect the peaks and to plot the 'static' version of the plot I would like to animate:
import neurokit2 as nk
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
#create realistic data
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05,\
random_state = 1)
#scale data
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
#find peaks
peak = find_peaks(scaled_arr.squeeze(), height = .66,\
distance = 60, prominence = .5)
#plot
plt.plot(scaled_arr[0:500])
plt.scatter(peak[0][peak[0] < 500],\
peak[1]['peak_heights'][peak[0] < 500],\
color = 'red')
I've created an animation using the data you presented; I've extracted the data in 500 increments for 5000 data and updated the graph. To make it easy to extract the data, I have created an index of 500 rows, where id[0] is the start row, id1 is the end row, and the number of frames is 10. This code works, but the initial settings and dataset did not work in the scatter plot, so I have drawn the scatter plot directly in the loop process.
import neurokit2 as nk
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.signal import find_peaks
import numpy as np
#create realistic data
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05, random_state = 1)
#scale data
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
#find peaks
peak = find_peaks(scaled_arr.squeeze(), height = .66, distance = 60, prominence = .5)
ymin, ymax = min(scaled_arr), max(scaled_arr)
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot([],[], lw=2)
scat = ax.scatter([], [], s=20, facecolor='red')
idx = [(s,e) for s,e in zip(np.arange(0,len(scaled_arr), 1), np.arange(499,len(scaled_arr)+1, 1))]
def init():
line.set_data([], [])
return line,
def animate(i):
id = idx[i]
#print(id[0], id[1])
line.set_data(np.arange(id[0], id[1]), scaled_arr[id[0]:id[1]])
x = peak[0][(peak[0] > id[0]) & (peak[0] < id[1])]
y = peak[1]['peak_heights'][(peak[0] > id[0]) & (peak[0] < id[1])]
#scat.set_offsets(x, y)
ax.scatter(x, y, s=20, c='red')
ax.set_xlim(id[0], id[1])
ax.set_ylim(ymin, ymax)
return line,scat
anim = FuncAnimation(fig, animate, init_func=init, frames=50, interval=50, blit=True)
plt.show()
Probably not exactly what you want, but hope it can help,
import neurokit2 as nk
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from sklearn.preprocessing import MinMaxScaler
import numpy as np
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
xs = xs[i]
ys = ys[i]
# Draw x and y lists
ax.clear()
ax.plot(xs, ys)
if __name__=="__main__":
data = nk.ecg_simulate(duration = 50, sampling_rate = 100, noise = 0.05, random_state = 1)
scaler = MinMaxScaler()
scaled_arr = scaler.fit_transform(data.reshape(-1,1))
ys = scaled_arr.flatten()
ys = [ys[0:50*i] for i in range(1, int(len(ys)/50)+1)]
xs = [np.arange(0, len(ii)) for ii in ys ]
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=500)
ani.save('test.gif')

The animation function must return a sequence of Artist objects

I am trying to test a Matplotlib animation example on my Pycharm which is listed as following:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
n = 1000
def update(curr):
if curr == n:
a.event_source.stop()
subplot1.cla()
subplot2.cla()
subplot3.cla()
subplot4.cla()
# subplots re-set to standard positions
x1 = np.random.normal(0, 1, n)
x2 = np.random.gamma(2, 1, n)
x3 = np.random.exponential(2, n)
x4 = np.random.uniform(0, 6, n)
# increment the number of bins in every 10th frame
# bins = 20 + curr // 10
bins = 10 + curr
# drawing the subplots
subplot1.hist(x1, bins=bins, alpha=0.5, color='red')
subplot2.hist(x2, bins=bins, alpha=0.5, color='green')
subplot3.hist(x3, bins=bins, alpha=0.5, color='blue')
subplot4.hist(x4, bins=bins, alpha=0.5, color='darkorange')
# set all ticks to null
subplot1.set_xticks([])
subplot2.set_xticks([])
subplot3.set_xticks([])
subplot4.set_xticks([])
subplot1.set_yticks([])
subplot2.set_yticks([])
subplot3.set_yticks([])
subplot4.set_yticks([])
# name the subplots
subplot1.set_title('Normal')
subplot2.set_title('Gamma')
subplot3.set_title('Exponential')
subplot4.set_title('Uniform')
# the title will change to reflect the number of bins
fig.suptitle('No of bins: {}'.format(bins))
# no redundant space left for saving into mp4
plt.tight_layout()
# Set up formatting for the movie files
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, metadata=dict(artist='Kuba Siekierzynski', title='Distributions'), bitrate=1800)
fig, ([subplot1, subplot2], [subplot3, subplot4]) = plt.subplots(2, 2)
a = animation.FuncAnimation(fig, update, interval=100, save_count=500, blit=True, frames=100)
# will only work with ffmpeg installed!!!
a.save('distributions.mp4', writer=writer)
plt.show()
the terminal shows the error:
raise RuntimeError('The animation function must return a '
RuntimeError: The animation function must return a sequence of Artist objects.
I have try to several time but cannot figure it out
As explained here for the case of init_func, since you are setting the parameter blit = True in the FuncAnimation definition, your update function has to return the plotting object. As an alternative, you can set blit = False; in that case, you get this animation:

Animating a line plot over time in Python

Time series data is data over time. I am trying to animate a line plot of time series data in python. In my code below this translates to plotting xtraj as they and trange as the x. The plot does not seem to be working though.
I have found similar questions on Stack overflow but none of the solutions provided here seem to work. Some similar questions are matplotlib animated line plot stays empty, Matplotlib FuncAnimation not animating line plot and a tutorial referencing the help file Animations with Matplotlib.
I begin by creating the data with the first part and simulating it with the second. I tried renaming the data that would be used as y-values and x-values in order to make it easier to read.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
dt = 0.01
tfinal = 5.0
x0 = 0
sqrtdt = np.sqrt(dt)
n = int(tfinal/dt)
xtraj = np.zeros(n+1, float)
trange = np.linspace(start=0,stop=tfinal ,num=n+1)
xtraj[0] = x0
for i in range(n):
xtraj[i+1] = xtraj[i] + np.random.normal()
x = trange
y = xtraj
# animation line plot example
fig = plt.figure(4)
ax = plt.axes(xlim=(-5, 5), ylim=(0, 5))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(x[:i], y[:i])
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=len(x)+1,interval=200, blit=False)
plt.show()
Any help would be highly appreciated. I am new to working in Python and particularly trying to animate plots. So I must apologize if this question is trivial.
Summary
So to summarize my question how does one animate time series in Python, iterating over the time steps (x-values).
Check this code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
dt = 0.01
tfinal = 1
x0 = 0
sqrtdt = np.sqrt(dt)
n = int(tfinal/dt)
xtraj = np.zeros(n+1, float)
trange = np.linspace(start=0,stop=tfinal ,num=n+1)
xtraj[0] = x0
for i in range(n):
xtraj[i+1] = xtraj[i] + np.random.normal()
x = trange
y = xtraj
# animation line plot example
fig, ax = plt.subplots(1, 1, figsize = (6, 6))
def animate(i):
ax.cla() # clear the previous image
ax.plot(x[:i], y[:i]) # plot the line
ax.set_xlim([x0, tfinal]) # fix the x axis
ax.set_ylim([1.1*np.min(y), 1.1*np.max(y)]) # fix the y axis
anim = animation.FuncAnimation(fig, animate, frames = len(x) + 1, interval = 1, blit = False)
plt.show()
The code above reproduces this animation:

Animation using animation.FuncAnimation from matplotlib playing slower than expected

From an earlier question, it transpired that a piece of code was leading to different animations on my PC as it was to another commenter. I have since re-written the code to make it a little simpler, as was suggested:
from numpy import sin, cos
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# create a time array from 0..100 sampled at 0.05 second steps
dt = 0.025
t = np.arange(0.0, 20, dt)
length = len(t)
def listmaker(n):
return [0]*n
th1 = listmaker(length)
th2 = listmaker(length)
#dummy data
for i in range(0,length):
th1[i] = 0.01*i
th2[i] = 0.05*i
x1 = sin(th1)
y1 = -cos(th1)
x2 = sin(th2) + x1
y2 = -cos(th2) + y1
fig = plt.figure()
ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2))
ax.grid()
line, = ax.plot([], [], 'o-', lw=2)
time_template = 'time = %.1fs'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
def init():
line.set_data([], [])
time_text.set_text('')
return line, time_text
def animate(i):
thisx = [0, x1[i], x2[i]]
thisy = [0, y1[i], y2[i]]
line.set_data(thisx, thisy)
time_text.set_text(time_template % (i*dt))
return line, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, length),
interval=25, blit=True, init_func=init)
# ani.save('double_pendulum.mp4', fps=15)
plt.show()
The issue, as shown in the other thread, is that since the interval (note that the interval argument is in milliseconds, hence the factor of 1000 difference) in the FuncAnimation is the same as the time step dt, the animation should run at "real time" i.e. the time tracker at the top left of the figure should run at the same speed as a normal clock. While this seemed to be the case for the other commenter, it was not the case on my own PC. I am hoping someone else is also able to reproduce the issue, so I can be pointed in the right direction.
I have no idea what is relevant, but I am running this code on Python 3.7, Idle 3.6.6 on a Windows machine.

Categories

Resources