Basically, I'm creating a graph with Matplotlib displaying live data every second. The program works just fine (quite smooth - no errors found) as it's initiated. However, after a certain time, the program starts lagging, and finally not responding (my assumption may be by loading too much data?). I'm trying to achieve the ability to handle massive data for my program. What I've done was removed the grid and hide X-axis from the chart, also used thread to run the function separately. It feels like helping just a bit, not much, still experiencing crashes after leaving it on for a while.
If anyone has any suggestions on how could I improve the program to run smoothly please do advise. I've highly appreciated it in advance.
from __future__ import annotations
from concurrent.futures import thread
from itertools import count
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import mplcursors
from mpl_interactions import zoom_factory
import threading
plt.style.use('fivethirtyeight')
x_vals = []
y_vals = []
index = count()
ax = plt.gca()
def animate(i):
data = pd.read_csv('data.csv')
x = data['x_value']
y = data['total_1']
line = plt.plot(x, y)
plt.setp(line,linewidth=0.5, color='r')
mplcursors.cursor(hover=False)
threads = []
for i in range(1):
thread = threading.Thread(target=animate, args=(i,))
thread.start()
threads.append(thread)
disconnect_zoom = zoom_factory(ax)
plt.title('Live Data')
plt.rcParams['font.size'] = 8
ax.get_xaxis().set_visible(False)
ax.grid(False)
ani = FuncAnimation(plt.gcf(), animate, interval=1000)
plt.show()
Here's the example of data.csv (time and value) every second new data will be accumulated
x_value,total_1
02:22:30-08/16/22,-0.049
02:24:00-08/16/22,0.079
02:24:02-08/16/22,0.081
02:24:03-08/16/22,0.083
02:24:04-08/16/22,0.084
02:24:05-08/16/22,0.073
02:24:06-08/16/22,0.073
02:24:07-08/16/22,0.073
02:24:08-08/16/22,0.073
02:24:09-08/16/22,0.083
This is not a good strategy because you are accumulating data on the plot through plt.plot().
The best way to go about this is, before calling plt.plot():
get the axes:
ax = plt.gca()
get the lines in the axes:
lines = ax.get_lines()
remove the lines:
lines[0].remove()
add the new line:
line = plt.plot(x,y)
This would prevent the graph from including too many lines (also, maybe this is not the best strategy, depending on how big data.csv gets).
Also, the thread is not doing anything really.
EDIT
I have tried to modify your code as little as possible. But I have removed the thread inclusion, which didn't feel like a sensible choice.
Try the code below and let me know:
from __future__ import annotations
from concurrent.futures import thread
from itertools import count
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import threading
import mplcursors
plt.style.use('fivethirtyeight')
x_vals = []
y_vals = []
index = count()
ax = plt.gca()
def animate(i):
data = pd.read_csv('data.csv')
x = data['x_value']
y = data['total_1']
# removal of previous lines
ax = plt.gca()
lines = ax.get_lines()
if len(lines) > 0:
lines[0].remove()
line = plt.plot(x, y)
plt.setp(line,linewidth=0.5, color='r')
mplcursors.cursor(hover=False)
i = 1
animate(i)
plt.title('Live Data')
plt.rcParams['font.size'] = 8
ax.get_xaxis().set_visible(False)
ax.grid(False)
ani = FuncAnimation(plt.gcf(), animate, interval=1000)
plt.show()
EDIT 2
I have modified the code to take advantage of the set_data method on the line primitive. This should speed things up a bit.
from __future__ import annotations
from concurrent.futures import thread
from itertools import count
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#import threading
import mplcursors
plt.style.use('fivethirtyeight')
x_vals = []
y_vals = []
index = count()
ax = plt.gca()
def animate(i):
data = pd.read_csv('data.csv')
x = data['x_value']
y = data['total_1']
# removal of previous lines
ax = plt.gca()
lines = ax.get_lines()
if len(lines) == 0:
plt.plot(x,y,c="r")
else:
lines[0].set_data(x,y)
mplcursors.cursor(hover=False)
i = 1
animate(i)
plt.title('Live Data')
plt.rcParams['font.size'] = 8
ax.get_xaxis().set_visible(False)
ax.grid(False)
ani = FuncAnimation(plt.gcf(), animate, interval=1000)
plt.show()
Related
I have created the script below for real-time data plotting. But the plot which shows up is empty. Could you tell me what's the case?
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from itertools import count
import random
style.use('fivethirtyeight')
fig = plt.figure()
axl = fig.add_subplot(1,1,1)
x_vals = []
y_vals = []
index = count()
def animate(i):
x_vals.append(next(index))
y_vals.append(random.randint(0, 5))
axl.clear()
axl.plot(x_vals,y_vals)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
I'm using python 3.8, matlibplot 3.1.2 and PyCharm as IDE.
I've noticed that every solution to plotting continuously updating data (I've found) with a continuously increasing length has one huge setback - If the data isn't there immediately, the matplotlib window freezes (says not responding). Take this for example:
from matplotlib import pyplot as plt
from matplotlib import animation
from random import randint
from time import sleep
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
line, = ax.plot([])
x = []
y = []
def animate(i):
x.append(i)
y.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate,
frames=200, interval=20, blit=True)
plt.show()
This code works fine without the data acquisition for loop in the animate function, but with it there, the graph window freezes. Take this as well:
plt.ion()
x = []
for i in range(1000):
x.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
plt.plot(x)
plt.pause(0.001)
Also freezes. (Thank god for that, because using this method it's borderline impossible to close as the graph keeps popping up in front of everything. I do not recommend removing the sleep)
This too:
plt.ion()
x = []
for i in range(1000):
x.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
plt.plot(x)
plt.draw()
plt.pause(0.001)
plt.clf()
Also this: (copied from https://stackoverflow.com/a/4098938/9546874)
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
for i in range(100000000):
# Do calculations to attain next data point
pass
fig.canvas.draw()
fig.canvas.flush_events()
This is a huge problem, as it's naive to think all the data will come at consistent intervals. I just want a graph that updates when data comes, and doesn't implode in the downtime. Keep in mind the interval between data could change, it could be 2 seconds, or 5 minutes.
EDIT:
After further testing, the FuncAnimation one can be used, but it's very hacky, and is still a bit broken. If you increase the interval to above the expected time of animate, it will work, but every time you pan or zoom the graph, all the data disappears until the next update. So once you have a view, you can't touch it.
Edit:
Changed sleep to a for loop for clarity
Updated Answer:
The problem is that data aquisition or generation and the matplotlib window run on the same thread so that the former is blocking the latter. To overcome this move the data aquisition into a seperate process as shown in this example. Instead of processes and pipes you can also use threads and queues.
See this example with sleep usage, it's working well:
=^..^=
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x)
def animate(i):
y_data = 0
for j in range(10):
y_data = np.random.uniform(-1, j, 1)
line.set_ydata(y_data)
plt.pause(1)
return line,
ani = animation.FuncAnimation(
fig, animate, interval=2, blit=True, save_count=50)
plt.ylim(-2, 11)
plt.show()
I'm trying to animate a few simple subplots with imshow but there's apparently an issue.
This is a small demo of my problem:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
fig,axs=plt.subplots(2,5,figsize=(10,4))
imgs=[]
for row in axs:
for col in row:
col.set_xlim(4.5,-.5)
col.set_ylim(4.5,-.5)
col.set_xticks([])
col.set_yticks([])
#line A
imgs.append(col.imshow([[]],animated=1))
#freezes the animation([[]] is just a placeholder)
#line B
#imgs.append(col.imshow(np.arange(25).reshape((5,5)),animated=1))
#animation does work if the above line is used instead
def func(frm):
for i in range(10):
imgs[i].set_array(np.arange(25).reshape(5,5)*np.log10(frm+1))
return imgs
anim=animation.FuncAnimation(fig,func,10,interval=100)
plt.show()
If I use line A, the animation freezes as if func is not executed (while actually it is), raising no errors. If line B is used instead, the animation works. Am I missing something about imshow animating?
Animation from multiple imshow can be created by ArtistAnimation object using multiple axes generated by subplots command.
from numpy import random
from matplotlib import animation
import matplotlib.pyplot as plt
img_lst_1 = [random.random((368,1232)) for i in range(10)] # Test data
img_lst_2 = [random.random((368,1232)) for i in range(10)] # Test data
fig, (ax1, ax2) = plt.subplots(2,1)
frames = [] # store generated images
for i in range(len(img_lst_1)):
img1 = ax1.imshow(img_lst_1[i], animated=True)
img2 = ax2.imshow(img_lst_2[i], cmap='gray', animated=True)
frames.append([img1, img2])
ani = animation.ArtistAnimation(fig, frames, interval=500, blit=True,
repeat_delay=1000)
ani.save('movie_example.mp4')
Code output:
I have written the following code with the help of online search. My intention here is to get a real time graph with time on x axis and some randomly generated value on y axis
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
xar = []
yar = []
x,y = time.time(), np.random.rand()
xar.append(x)
yar.append(y)
ax1.clear()
ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
With the above code I just see the range of y axis changing continuously and the graph will not appear in the figure.
The problem is that you never update xvar and yvar. You can do that by moving the definitions of the lists outside the definition of animate.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
xar = []
yar = []
def animate(i):
x,y = time.time(), np.random.rand()
xar.append(x)
yar.append(y)
ax1.clear()
ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
I'm trying to plot a 3D trajectory of a vehicle that comes from a CSV file, plotting is easy, I want to make the animation, actually a "replay" of the movements. I based my code from this example (http://matplotlib.org/examples/animation/simple_3danim.html) and then just modify it to only plot one line and to read the data from a CSV file being read by pandas, the code looks like this:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
import pandas as pd
def update_lines(num, data, line):
# NOTE: there is no .set_data() for 3 dim data...
x = data['x'].values[num]
y = data['y'].values[num]
z = data['z'].values[num]
line[0].set_data(x,y)
line[0].set_3d_properties(z)
print z
return line
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Reading the data from a CSV file using pandas
data = pd.read_csv('data.csv',sep=',',header=0)
# Creating fifty line objects.
# NOTE: Can't pass empty arrays into 3d version of plot()
x = np.array([0])
y = np.array([0])
z = np.array([0])
line = ax.plot(x, y, z)
# Setting the axes properties
ax.set_xlim3d([0.0, 3.0])
ax.set_xlabel('X')
ax.set_ylim3d([0.0, 3.0])
ax.set_ylabel('Y')
ax.set_zlim3d([0.0, 2.0])
ax.set_zlabel('Z')
ax.set_title('3D Test')
# Creating the Animation object
line_ani = animation.FuncAnimation(fig, update_lines, len(data), fargs=(data, line),
interval=10, blit=False)
plt.show()
I print the z just to see if the data is being iterated correctly, but all I get is a white plot like this:
Plot showing absolutely nothing.
at least, there are two issues with your code:
the way of how data is build
length of frames per second
here is the modified working example, please take a look how data variable
was arranged:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
import pandas as pd
from sys import exit
def update_lines(num, data, line):
# NOTE: there is no .set_data() for 3 dim data...
line.set_data(data[0:2, :num])
line.set_3d_properties(data[2, :num])
return line
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Reading the data from a CSV file using pandas
repo = pd.read_csv('data.csv',sep=',',header=0)
data = np.array((repo['x'].values, repo['y'].values, repo['z'].values))
print data.shape[1]
#exit()
# Creating fifty line objects.
# NOTE: Can't pass empty arrays into 3d version of plot()
line = ax.plot(data[0, 0:1], data[1, 0:1], data[2, 0:1])[0]
# Setting the axes properties
ax.set_xlim3d([-2.0, 2.0])
ax.set_xlabel('X')
ax.set_ylim3d([-2.0, 2.0])
ax.set_ylabel('Y')
ax.set_zlim3d([-2.0, 2.0])
ax.set_zlabel('Z')
ax.set_title('3D Test')
# Creating the Animation object
line_ani = animation.FuncAnimation(fig, update_lines, data.shape[1], fargs=(data, line), interval=50, blit=False)
plt.show()
you can watch the beauty of that flight just being tracked