I have two data sets y1 = vol1 and y2 = vol2 for the same x range (0 to 5000 in steps of 10). I would like to use function animation in order to first animate y1 and after that animate y2 while the graph of y1 remains.
This is what I got from combing several examples (incl. this):
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
x = range(0, 5000, 10)
y1 = vol1
y2 = vol2
fig, ax = plt.subplots()
ax.set_xlim(0, 5000)
ax.set_ylim(0, 1000)
l1, = plt.plot([],[],'b-')
l2, = plt.plot([],[],'r-')
def init1():
return l1,
def init2():
return l2,
def animate1(i):
l1.set_data(x[:i],y1[:i])
return l1,
def animate2(i):
l2.set_data(x[:i-500],y2[:i-500])
return l2,
def gen1():
i = 0
while(i<500):
yield i
i += 1
def gen2():
j = 500
while(j<1000):
yield j
j += 1
ani1 = FuncAnimation(fig, animate1, gen1, interval=1, save_count=len(x),
init_func=init1, blit=True,
repeat=False)
ani2 = FuncAnimation(fig, animate2, gen2, interval=1, save_count=len(x),
init_func=init2, blit=True,
repeat=False)
# ani.save('ani.mp4')
plt.show()
My idea was to make two 'counters' gen1 andgen2 but since I have the same x values for both data sets, I tried to compensate that in the animate2 function. But this doesn't work..
Obviously, I'm quite new to python and I appreciate any help.
I would do just one animation, keeping track of the frame with respect to the line length:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation, FFMpegWriter
x = np.linspace(0, 2 * np.pi)
y1 = np.sin(x)
y2 = np.cos(x)
k = 0
fig, ax = plt.subplots()
ax.set_xlim(0, x.max())
ax.set_ylim(-1.5, 1.5)
l1, = plt.plot([],[],'b-')
l2, = plt.plot([],[],'r-')
def animate1(i):
global k
if k > 2 * len(x):
# reset if "repeat=True"
k = 0
if k <= len(x):
l1.set_data(x[:k],y1[:k])
else:
l2.set_data(x[:k - len(x)],y2[:k - len(x)])
k += 1
ani1 = FuncAnimation(fig, animate1, frames=2*len(x), interval=1, repeat=True)
writer = FFMpegWriter(fps=10)
ani1.save("test.mp4", writer=writer)
plt.show()
Related
I am a python beginner. This is my first time building a 3D scatterplot animation. In my code, I only want 6 points to display in a frame and remove points before the next frame comes. After writing the code, the problem came. The video is like a photo (see below) and shows every point.
Please help me, I don't know how to change my code.
CSV
import os
import csv
from csv import reader
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig = plt.figure()
ax = plt.subplot(projection='3d')
ax.set_xlim(0,100)
ax.set_ylim(0,100)
ax.set_zlim(0,100)
dataname = open('/content/id3.csv', 'r')
data = csv.DictReader(dataname)
xr = []
yr = []
zr = []
xr1 = []
yr1 = []
zr1 = []
for col in data:
xr.append(col['x'])
yr.append(col['y'])
zr.append(col['z'])
xr1.append(col['x1'])
yr1.append(col['y1'])
zr1.append(col['z1'])
x=[int(x) for x in xr]
y=[int(x) for x in yr]
z=[int(x) for x in zr]
x1=[int(x) for x in xr1]
y1=[int(x) for x in yr1]
z1=[int(x) for x in zr1]
def init():
ax.scatter(x, y, z)
ax.scatter(x1, y1, z1)
def run(data):
ax.clear()
ax.set_xlim(0,100)
ax.set_ylim(0,100)
ax.set_zlim(0,100)
ax.scatter(x,y,z)
ax.scatter(x1, y1, z1)
ani = animation.FuncAnimation(fig, run, frames=50, interval=30, init_func=init)
ani.save('DREAMLINERs.mp4', fps=10)
You have to edit run function ad use i parameter as a counter: in each frame i increases by 1, so you can use it in order to select which point to plot in each frame. If you want to plot 6 points, whose coordinates come from x, y, z arrays, you can use:
ax.scatter(x[i:i + 6], y[i:i + 6], z[i:i + 6])
Complete Code
import csv
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax = plt.subplot(projection='3d')
ax.set_xlim(0,100)
ax.set_ylim(0,100)
ax.set_zlim(0,100)
dataname = open('/content/id3.csv', 'r')
data = csv.DictReader(dataname)
xr = []
yr = []
zr = []
xr1 = []
yr1 = []
zr1 = []
for col in data:
xr.append(col['x'])
yr.append(col['y'])
zr.append(col['z'])
xr1.append(col['x1'])
yr1.append(col['y1'])
zr1.append(col['z1'])
x=[int(x) for x in xr]
y=[int(x) for x in yr]
z=[int(x) for x in zr]
x1=[int(x) for x in xr1]
y1=[int(x) for x in yr1]
z1=[int(x) for x in zr1]
def init():
ax.scatter(x, y, z)
ax.scatter(x1, y1, z1)
def run(i):
ax.clear()
ax.set_xlim(0,100)
ax.set_ylim(0,100)
ax.set_zlim(0,100)
ax.scatter(x[i:i + 6], y[i:i + 6], z[i:i + 6])
ax.scatter(x1[i:i + 6], y1[i:i + 6], z1[i:i + 6])
ani = animation.FuncAnimation(fig, run, frames=50, interval=30, init_func=init)
ani.save('DREAMLINERs.mp4', fps=10)
plt.show()
Animation
Notes
You can simplify the code by using pandas to read data from csv file:
import pandas as pd
data = pd.read_csv('data/id3.csv')
x = data['x']
y = data['y']
z = data['z']
x1 = data['x1']
y1 = data['y1']
z1 = data['z1']
I have just started learning python to plot realtime gragh. I have tried solutions provided on stackoverflow but none of them are working. Below is my code and it isn't woorking. Please help
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 512
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
def animate(i):
try:
while True:
x, y = pg.position()
f.append(x)
except KeyboardInterrupt:
print('')
# f.append(15)
if len(f)<binSize :
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="Noisy")
else:
plt.cla()
plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Noisy")
ani = FuncAnimation(plt.gcf(),animate,interval=1);
So I have updated the code and trying to draw two subplots but after sometime
Upper graph stopped clearing the canvas (Mouse X coordinates)
Lower graph stopped updating the plot (FFT)
When data grows beyond the binSize, notebook freezes and plots update really slowly
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
def animate(i):
x, y = pg.position()
f.append(x)
n = len(f)
if n<binSize :
plt.sca(axs[0])
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int')
# update the code third time
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
# axs[0].xlim(0,binSize) # this stopped the FFT graph to be plotted
# plt.cla()
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
# plt.xlim(t[0],t[-1])
# plt.legend()
# plt.sca(axs[1])
# plt.plot(freq[L],PSD[L],color='c',LineWidth=2,label="Mouse FFT")
# plt.xlim(0,300)
# plt.legend()
# plt.cla()
# plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Mouse")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
To make it faster you may reduce data like in other answer
f.pop(0)
I use also different method to update plot which works much faster on my computer.
I create empty plots at start
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
and later only update data in plots without clear() and plot() again
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
and
# replace data in plot
xdata = range(binSize)
ydata = f[-binSize:]
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
It needs only to run code which rescale plot
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
animate() has to also return new plots
# return plots
return p1, p2
And FuncAnimation() has to blit them
ani = FuncAnimation(..., blit=True)
EDIT:
Animation works much, much faster also because I run it normally python script.py, not in Jupuyter Notebook
EDIT:
when I run normally I found one problem which I could find solution: it doesn't update values/ticks on axes. Jupyter Notebook doesn't have this problem.
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 256
f = []
t = 0
dt = 1
fig, axs = plt.subplots(2, 1)
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
freq = np.arange(binSize)/(dt*binSize)
def animate(i):
x, y = pg.position()
n = len(f)
if n < binSize :
f.append(x)
# replace data in plot
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f, binSize)
PSD = fhat * np.conj(fhat) / binSize
# replace data in plot
#xdata = range(binSize)
ydata = f[-binSize:]
#p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
# return plots
return p1, p2
ani = FuncAnimation(plt.gcf(), animate, interval=dt, blit=True)
plt.show()
You should try this. Instead of clearing the plt clear axs[0] and so on. Also, instead of plotting on plt.plot, plot on axs[0].plot
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
plt.sca(axs[0])
plt.sca(axs[1])
def animate(i):
x, y = pg.position()
n = len(f)
if n<binSize :
f.append(x*100)
axs[0].clear()
axs[0].plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int') # index array of [1,2,3..... binsize/2] type int
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
plt.show()
I have a dataframe called benchmark_returns and strategy_returns. Both have the same timespan. I want to find a way to plot the datapoints in a nice animation style so that it shows all the points loading in gradually. I am aware that there is a matplotlib.animation.FuncAnimation(), however this typically is only used for a real-time updating of csv files etc but in my case I know all the data I want to use.
I have also tried using the crude plt.pause(0.01) method, however this drastically slows down as the number of points get plotted.
Here is my code so far
x = benchmark_returns.index
y = benchmark_returns['Crypto 30']
y2 = benchmark_returns['Dow Jones 30']
y3 = benchmark_returns['NASDAQ']
y4 = benchmark_returns['S&P 500']
fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
line2, = ax.plot(x, y2, color = 'b')
line3, = ax.plot(x, y3, color = 'r')
line4, = ax.plot(x, y4, color = 'g')
def update(num, x, y, y2, y3, y4, line):
line.set_data(x[:num], y[:num])
line2.set_data(x[:num], y2[:num])
line3.set_data(x[:num], y3[:num])
line4.set_data(x[:num], y4[:num])
return line, line2, line3, line4,
ani = animation.FuncAnimation(fig, update, fargs=[x, y, y2, y3, y4, line],
interval = 1, blit = True)
plt.show()
You could try matplotlib.animation.ArtistAnimation. It operates similar to FuncAnimation in that you can specify the frame interval, looping behavior, etc, but all the plotting is done at once, before the animation step. Here is an example
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib.animation import ArtistAnimation
n = 150
x = np.linspace(0, np.pi*4, n)
df = pd.DataFrame({'cos(x)' : np.cos(x),
'sin(x)' : np.sin(x),
'tan(x)' : np.tan(x),
'sin(cos(x))' : np.sin(np.cos(x))})
fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(10,10))
lines = []
artists = [[]]
for ax, col in zip(axs.flatten(), df.columns.values):
lines.append(ax.plot(df[col])[0])
artists.append(lines.copy())
anim = ArtistAnimation(fig, artists, interval=500, repeat_delay=1000)
The drawback here is that each artist is either drawn or not, i.e. you can't draw only part of a Line2D object without doing clipping. If this is not compatible with your use case then you can try using FuncAnimation with blit=True and chunking the data to be plotted each time as well as using set_data() instead of clearing and redrawing on every iteration. An example of this using the same data from above:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib.animation import FuncAnimation
n = 500
nf = 100
x = np.linspace(0, np.pi*4, n)
df = pd.DataFrame({'cos(x)' : np.cos(x),
'sin(x)' : np.sin(x),
'tan(x)' : np.tan(x),
'sin(cos(x))' : np.sin(np.cos(x))})
fig, axs = plt.subplots(2, 2, figsize=(5,5), dpi=50)
lines = []
for ax, col in zip(axs.flatten(), df.columns):
lines.append(ax.plot([], lw=0.5)[0])
ax.set_xlim(x[0] - x[-1]*0.05, x[-1]*1.05)
ax.set_ylim([min(df[col].values)*1.05, max(df[col].values)*1.05])
ax.tick_params(labelbottom=False, bottom=False, left=False, labelleft=False)
plt.subplots_adjust(hspace=0, wspace=0, left=0.02, right=0.98, bottom=0.02, top=0.98)
plt.margins(1, 1)
c = int(n / nf)
def animate(i):
if (i != nf - 1):
for line, col in zip(lines, df.columns):
line.set_data(x[:(i+1)*c], df[col].values[:(i+1)*c])
else:
for line, col in zip(lines, df.columns):
line.set_data(x, df[col].values)
return lines
anim = FuncAnimation(fig, animate, interval=2000/nf, frames=nf, blit=True)
Edit
In response to the comments, here is the implementation of a chunking scheme using the updated code in the question:
x = benchmark_returns.index
y = benchmark_returns['Crypto 30']
y2 = benchmark_returns['Dow Jones 30']
y3 = benchmark_returns['NASDAQ']
y4 = benchmark_returns['S&P 500']
line, = ax.plot(x, y, color='k')
line2, = ax.plot(x, y2, color = 'b')
line3, = ax.plot(x, y3, color = 'r')
line4, = ax.plot(x, y4, color = 'g')
n = len(x) # Total number of rows
c = 50 # Chunk size
def update(num):
end = num * c if num * c < n else n - 1
line.set_data(x[:end], y[:end])
line2.set_data(x[:end], y2[:end])
line3.set_data(x[:end], y3[:end])
line4.set_data(x[:end], y4[:end])
return line, line2, line3, line4,
ani = animation.FuncAnimation(fig, update, interval = c, blit = True)
plt.show()
or, more succinctly
cols = benchmark_returns.columns.values
# or, for only a subset of the columns
# cols = ['Crypto 30', 'Dow Jones 30', 'NASDAQ', 'S&P 500']
colors = ['k', 'b', 'r', 'g']
lines = []
for c, col in zip(cols, colors):
lines.append(ax.plot(benchmark_returns.index, benchmark_returns[col].values, c=c)[0])
n = len(benchmark_returns.index)
c = 50 # Chunk size
def update(num):
end = num * c if num * c < n else n - 1
for line, col in zip(lines, cols):
line.set_data(benchmark_returns.index, benchmark_returns[col].values[:end])
return lines
anim = animation.FuncAnimation(fig, update, interval = c, blit=True)
plt.show()
and if you need it to stop updating after a certain time simply set the frames argument and repeat=False in FuncAnimation().
You can just update the data into the line element like so:
fig = plt.figure()
ax = fig.add_subplot(111)
liner, = ax.plot()
plt.ion()
plt.show()
for i in range(len(benchmark_returns.values)):
liner.set_ydata(benchmark_returns['Crypto 30'][:i])
liner.set_xdata(benchmark_returns.index[:i])
plt.pause(0.01)
I'm trying to animate the function with matplotlib:
But for some reason, when I write it like that in the animate function:
l = 0.5
k = (2 * np.pi)/l
f = 4
w = 2 * np.pi * f
y = np.sin(k * x + w * i)
It doesn't animate, only when I write it with numbers it animates:
y = np.sin(15*x - 62 * i)
What is the reason? Why can't I use the other form?
Relevant code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# initialization function
def init():
line.set_data([], [])
return line,
# animation function
def animate(i):
x = np.linspace(0, 2, 1000)
l = 0.5
k = (2 * np.pi)/l
f = 4
w = 2 * np.pi * f
y = np.sin(k * x + w * i)
#y = np.sin(15*x - 62 * i)
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=300, interval=20, blit=True)
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
Thanks !!
The only problem is that your f is an integer and hence w is a multiple of 2 * np.pi. The function is essentially unchanged for integer i's. Change f to be non integer.
I have the following code :
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
p3.autoscale = True
ax = p3.Axes3D(fig)
ax.grid(True)
ax.set_xlim(-100, 100)
ax.set_ylim(-100, 100)
ax.set_zlim(-100, 100)
u = np.r_[0:2*np.pi:100j]
v = np.r_[0:np.pi:100j]
scale = 15
x = scale * np.outer(np.cos(u),np.sin(v))
y = scale * np.outer(np.sin(u),np.sin(v))
z = scale * np.outer(np.ones(np.size(u)),np.cos(v))
Line3DCollection_1 = ax.plot_wireframe(x,y,z, rstride=50, cstride=50)
Line3DCollection_2 = ax.plot_wireframe(x + 50,y,z, rstride=50, cstride=50)
# initialization function: plot the background of each frame
def init():
return Line3DCollection_1,
def animate(i):
print("frame :" + str(i))
x = 50 * np.sin(np.radians(i))
y = 50 * np.cos(np.radians(i))
path = plt.plot([x],[y],[0], 'bo')
return path
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=360, interval=0.1, blit=True)
plt.show()
This will produce 2 spheres and a path that I want one of the spheres to take, but I'm not sure how to include this in the animation, I can animate the path, but not the 'Line3DCollection_2' sphere.
Does anyone have any ideas?
Thanks.