How to dynamically display time data stream on matplotlib - python

I'm using matplotlib to display data saved on a csv file periodically,
now the data is plotted well but the time axis is hardly moving, in fact
the script is trying to show all the data stored on that file, I want to see only latest data and be able to scrol horizontaly to see older data
this is a part of the script :
style.use('grayscale')
fig = plt.figure()
ax0= fig.add_subplot(511)
def animate(i):
graph_data = open('filelocation','r').read()
lines = graph_data.split('\n')
xs = []
ys = []
for line in lines :
if len(line)>1:
time0 , quantity0 = line.split (',')
xs.append(dt.datetime.strptime(time0,'%H:%M:%S.%f'))
ys.append(quantity0)
ax0.clear()
ax0.plot(xs,ys)
xs = matplotlib.dates.date2num(xs)
hfmt = matplotlib.dates.DateFormatter('%H:%M:%S')
ax0.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
ax0.set_ylabel('risk')
ax0.xaxis.set_major_formatter(hfmt)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.ticklabel_format(style='sci',axis ='y' , scilimits = (0,0))
plt.show()
plt.clear()

From that video, it looks like you want something like the following - I can still see a scrolling window in the video you posted so I'm still a little confused as to what you want. This uses fig.canvas.draw but there are other options using the matplotlib animation module (you didn't specify that it had to be using that module).
import numpy as np, time
from matplotlib import pyplot as plt
import matplotlib
matplotlib.interactive(True)
rest_time = 0.001
data_stream = np.random.randn(200)
# Define the width of the viewing window (xaxis limits),
# and the number of points to be displayed until a scrolling window starts
window_width = 40
n = 60
fig, ax = plt.subplots()
plotted_data, = ax.plot([], [], 'bo-', lw = 1.5)
x = []
y = []
ax.set_xlim([0, window_width])
ax.set_ylim([-4, 4])
for t, d in enumerate(data_stream):
x.append(t)
y.append(d)
plotted_data.set_xdata(x)
plotted_data.set_ydata(y)
if t > window_width:
ax.set_xlim([0, t])
if len(x) > n:
ax.set_xlim([t-n, t])
time.sleep(rest_time)
fig.canvas.draw()

Related

How can I animate a matplotlib plot from within for loop

I would like to update my matplotlibplot with values calculated in each iteration of a for loop. The idea is that I can see in real time which values are calculated and watch the progress iteration by iteration as my script is running. I do not want to first iterate through the loop, store the values and then perform the plot.
Some sample code is here:
from itertools import count
import random
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
def animate(i, x_vals, y_vals):
plt.cla()
plt.plot(x_vals, y_vals)
if __name__ == "__main__":
x_vals = []
y_vals = []
fig = plt.figure()
index = count()
for i in range(10):
print(i)
x_vals.append(next(index))
y_vals.append(random.randint(0, 10))
ani = FuncAnimation(fig, animate, fargs=(x_vals, y_vals))
plt.show()
Most of the examples I have seen online, deal with the case where everything for the animation is global variables, which I would like to avoid. When I use a debugger to step through my code line by line, the figure does appear and it is animated. When I just run the script without the debugger, the figure displays but nothing is plot and I can see that my loop doesn't progress past the first iteration, first waiting for the figure window to be closed and then continuing.
You should never be using a loop when animating in matplotlib.
The animate function gets called automatically based on your interval.
Something like this should work
def animate(i, x=[], y=[]):
plt.cla()
x.append(i)
y.append(random.randint(0, 10))
plt.plot(x, y)
if __name__ == "__main__":
fig = plt.figure()
ani = FuncAnimation(fig, animate, interval=700)
plt.show()
trying to elaborate on #dumbpotato21 answer, here my attempt:
import random
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
def data():
cnt = 0
x = []
y = []
for i in range(1,10):
# x = []
# y = []
x.append(cnt*i)
y.append(random.randint(0, 10))
cnt += 1
yield x, y, cnt
input('any key to exit !!!')
quit()
def init_animate():
pass
def animate( data, *fargs) :
print('data : ', data, '\n data type : ', type(data), ' cnt : ', data[2])
plt.cla()
x = [i*k for i in data[0]]
y = [i*p for i in data[1]]
plt.plot(x,y)
if __name__ == "__main__":
fig = plt.figure()
k = 3
p = 5
ani = FuncAnimation(fig, animate, init_func=init_animate, frames=data, interval=700, fargs = [k,p])
plt.show()
There are a number of alternatives which might come in handy in different situations. Here is one that I have used:
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
x = np.linspace(0, 30, 51)
y = np.linspace(0, 30, 51)
xx, yy = np.meshgrid(x, y)
# plt.style.use("ggplot")
plt.ion()
fig, ax = plt.subplots()
fig.canvas.draw()
for n in range(50):
# compute data for new plot
zz = np.random.randint(low=-10, high=10, size=np.shape(xx))
# erase previous plot
ax.clear()
# create plot
im = ax.imshow(zz, vmin=-10, vmax=10, cmap='RdBu', origin='lower')
# Re-render the figure and give the GUI event loop the chance to update itself
# Instead of the two lines one can use "plt.pause(0.001)" which, however gives a
# decepracted warning.
# See https://github.com/matplotlib/matplotlib/issues/7759/ for an explanation.
fig.canvas.flush_events()
sleep(0.1)
# make sure that the last plot is kept
plt.ioff()
plt.show()
Additionally, the set_data(...) method of a line plot or imshow object is useful if only the data changes and you don't want to re-drw the whole figure (as this is very time consuming).

Real time live graphs in Jupyter Notebook

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()

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:

Old lines won't delete when animating multiple lines with matplotlib

I am trying to animate multiple lines from a set of data using matplotlib. When each frame comes around, the animation does not delete the old lines.
I followed the format given in this SO post: Matplotlib multiple animate multiple lines
Their lines do not delete either when I run their code. Since the code I am using requires an input file, I'll simply put the code from the other SO thread below. If I can figure out a solution to the problem in their code, I should be able to apply a solution to my code.
import matplotlib.pyplot as plt
from matplotlib import animation
from numpy import random
fig = plt.figure()
ax1 = plt.axes(xlim=(-108, -104), ylim=(31,34))
line, = ax1.plot([], [], lw=2)
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plotlays, plotcols = [2], ["black","red"]
lines = []
for index in range(2):
lobj = ax1.plot([],[],lw=2,color=plotcols[index])[0]
lines.append(lobj)
def init():
for line in lines:
line.set_data([],[])
return lines
x1,y1 = [],[]
x2,y2 = [],[]
# fake data
frame_num = 100
gps_data = [-104 - (4 * random.rand(2, frame_num)), 31 + (3 * random.rand(2, frame_num))]
def animate(i):
x = gps_data[0][0, i]
y = gps_data[1][0, i]
x1.append(x)
y1.append(y)
x = gps_data[0][1,i]
y = gps_data[1][1,i]
x2.append(x)
y2.append(y)
xlist = [x1, x2]
ylist = [y1, y2]
#for index in range(0,1):
for lnum,line in enumerate(lines):
line.set_data(xlist[lnum], ylist[lnum]) # set data for each line separately.
return lines
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=frame_num, interval=10, blit=True)
plt.show()
I would like for there to be 2 lines only displayed on each frame. Instead the lines stack with each frame until the entire plot is filled.

Make GIFs/Animation of pictures and plots in the same file

I am here looking for some advice. I have a data file, basically containing Time, Displacement & Force items I recorded from a mechanical experiment. During the experiment, I recorded a picture of it every 2 seconds.
What I would like to make, is an animation (*.avi, *.gif, ...) of the evolution of the Force vs. Displacement plot and the corresponding pictures above (or below) the plot.
To do so, I first made a script, greatly inspired by another post on the forum, to generate an animation of the plots only. Then another one to open a plot above and a picture below. However, I did not manage to mix these two codes.
Below are the two scripts if needed for more understanding.
Script 1: Generation an animated plot
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pytex as p
import os
#1- Data reading
for filename in os.listdir("."):
if (filename[0]=="#"):
f = open(filename,"r+",encoding="utf8")
d = f.readlines()
f.seek(0)
for i in range(len(d)):
if i == 21:
f.write(d[i])
if i > 23:
f.write(d[i])
f.truncate()
f.close()
os.rename(filename,filename[1:])
#2- Data for plot
def simData():
for filename in os.listdir("."):
data = p.loadDataMatrix(filename)
tt = data["Time (sec)"]
x_raw = data["Position (microns)"]
y_raw = data["Load (N)"]
x = []
y = []
t = 0.
for i in range(0,len(x_raw),3):
x = x + [x_raw[i] - x_raw[0]]
y = y + [y_raw[i]]
t = tt[i]
yield x, y, t
def simPoints(simData):
xx, yy, t = simData[0], simData[1], simData[2]
time_text.set_text(time_template%(t))
line.set_data(xx, yy)
return line, time_text
#3- Plotting the figure
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim(0, 450)
ax.set_ylim(0, 65)
delay = 1
line, = ax.plot([], [], 'b-', linewidth = 0.6)
time_template = 'Time = %.01f s'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
ani = animation.FuncAnimation(fig, simPoints, simData, interval=delay, repeat=False, save_count = 50000)
ani.save('animation.mp4', fps=100, dpi=100)
Script 2: Merging a plot and an image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as im
fig, (chart, picture) = plt.subplots(nrows=2, figsize=(10,8))
# First, the chart
chart.plot(np.random.random(100))
# Second, an image
image=im.imread("../test_no_8/IMG_004850.tiff")
picture.imshow(image, aspect='auto')
picture.axis('off')
plt.show()
fig.savefig('temp.tiff', dpi=100)
Thanks a lot for any help of yours, and please pardon my English if it ever seems unclear.

Categories

Resources