I am trying to construct a live graph in python to plot random numbers in a graph with a fixed plot window
The width of the plot window will be 20 samples. For the 21st sample, the 1st sample will dissappear from the right side. Here is my code. Cant figure out why is it not plotting.
import random
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
#creating a subplot
ax1 = fig.add_subplot(1,1,1)
xs = []
ys = []
iter = 0
def animate(i,xs,ys,iter):
while True:
iter = iter+1
xs.append(iter)
ys.append(round(random.uniform(-120,20),2))
#I want only 20 data points on the plot i.e the plot window will be only showing 20 samples at a time
x = xs[-20:]
y = ys[-20:]
ax1.clear()
ax1.plot(x, y)
ax1.set_ylim([-120,20])
plt.xlabel('Value')
plt.ylabel('Time')
plt.title('Live Graph')
time.sleep(1)
ani = animation.FuncAnimation(fig, animate, fargs = (xs,ys,iter), interval=1000)
plt.show()
Check out the documentation for FuncAnimation.
The second parameter is a function that is repeatedly called for each consecutive frame, updating the figure with each call. Your function animate is an infinite loop, so the execution thread never returns back to FuncAnimation. Try something like this instead as a starting point:
import random
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
xs = []
ys = []
line, = ax1.plot(xs, ys)
plt.xlabel('Value')
plt.ylabel('Time')
plt.title('Live Graph')
def animate(frame, xs, ys):
xs.append(frame)
ys.append(round(random.uniform(-120,20),2))
x = xs[-20:]
y = ys[-20:]
line.set_xdata(x)
line.set_ydata(y)
ax1.set_xlim(min(x)-1, max(x)+1)
ax1.set_ylim(min(y)-1, max(y)+1)
ax1.set_xticks(list(range(min(x), max(x)+1)))
return line
ani = animation.FuncAnimation(fig, animate, fargs = (xs,ys), interval=100)
plt.show()
Related
I am trying to create a crude animation, where each additional data point is plotted on the same graph. The problem is that the loop is generating a new graph for each data point.
Here's the code:
x = []
y = []
for i in range(3):
x.append(random.randint(0,10))
y.append(random.randint(0,10))
plt.scatter(x,y)
plt.pause(0.1)
This resulted in 3 separate plots stacked vertically. I would like all data points to update on the same graph, creating an animation. Thanks!
I've made an adaptation of your code (in particular, in each iteration I plot only another scatter point, because plotting each time ALL the points soon becomes unbearably slow).
If you will execute this file, as I invite you to do, $ python3 slow.py, it will print 0 50 100 150 200 and, initially fast, then slower and slower, it will produce a progressive display of the data points, all in the same Axes.
I have to confess that I don't understand your problem description because it's so different from what I've seen.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import random
def point():
return (random.randint(0,10), random.randint(0,10))
plt.xlim((-1, 11))
plt.ylim((-1, 11))
random.seed(20221229)
N = 200
cmap = plt.get_cmap('plasma')
plt.colorbar(ScalarMappable(cmap=cmap)).set_ticks((0,1), labels=("1",str(N)))
for i in range(N):
if not(i%50) : print(i)
plt.scatter(*point(), color=cmap(i/N), ec='black', s=80)
plt.pause(0.001)
print(N)
plt.show()
To update matplotlib graph you should use the module animation like Matplotlib is not very compatible with threads.
Here is an example adding a new point every 2 seconds :
import matplotlib.pyplot as pl
from matplotlib.animation import FuncAnimation
import random
datas = [0, 50]
fig = pl.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x=datas, y=datas, marker = '+', c='red')
def update(frame):
global datas
ax.clear()
ax.scatter(x=datas, y=datas, marker = '+', c='red')
datas.append(random.randint(0,50))
animation = FuncAnimation(fig, update, interval=2000, repeat = True)
pl.show()
Interval (in milliseconds) in FuncAnimation function plays the role of the pause function you are looking for.
James, I think plt.scatter can't make an animation. All the code will be executed and then a chart with 3 points will be the result.
To avoid the generation of multiple figures you can use plt.subplots.
fig, ax = plt.subplots()
for i in range(3):
ax.scatter(x=random.randint(0,10),y= random.randint(0,10))
If you want to create some animated figure use matplotlib.animation.FuncAnimation , as in the answer of the following topic. How can i make points of a python plot appear over time?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
HTML(ani.to_jshtml())
Mark the option loop, and click in the minus or plus sing to slow or speed up the animation
Alternative approach, using FuncAnimation
from matplotlib.pyplot import Normalize, get_cmap, subplots
from matplotlib.cm import ScalarMappable
from matplotlib.animation import FuncAnimation, PillowWriter
from numpy.random import rand, seed
def update(xyn):
x, y, n = xyn
ax.scatter(x, y, color=cmap(n/N), ec='grey')
def start():
ax.set_xlim((-0.1, 1.1)) ; ax.set_ylim((-0.1, 1.1))
cb = fig.colorbar(ScalarMappable(cmap=cmap, norm=Normalize(0, 1)))
cb.set_ticks((0, 1), labels=('First', 'Last'))
def points(n):
seed(20230102)
def _():
for n_ in range(n):
yield rand(), rand(), n_
return _
fig, ax = subplots()
cmap = get_cmap('Greys')
N = 80
FuncAnimation(fig, update, points(N), start, repeat=False).save(
'scatter.gif', writer=PillowWriter())
I am trying to dynamically update a matplotlib from a .txt file that periodically updates.
For this, I used the following tutorial.
https://pythonprogramming.net/python-matplotlib-live-updating-graphs/
The .txt file looks like such
1,2
2,3
3,6
4,9
5,4
6,7
7,7
8,4
9,3
10,10
The code looks like such:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
pullData = open("sampleText.txt","r").read()
dataArray = pullData.split('\n')
xar = []
yar = []
for eachLine in dataArray:
if len(eachLine)>1:
x,y = eachLine.split(',')
xar.append(int(x))
yar.append(int(y))
ax1.clear()
ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
This output a figure with these points plotted.
When I update with a new line, such as 11,15, and save there is no updated figure.
How can I make this update to the current figure as a new line is added to the .txt file?
I have tried some of the solutions to these questions asked on stackoverflow without success:
live updating with matplotlib
What is the currently correct way to dynamically update plots in Jupyter/iPython?
I created the code with the understanding that the intent of the question was to draw a graph based on the row-by-row data by retrieving the values from an updated, localized text file. The main points that I modified are the initial settings and updating the values in the animation function.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from matplotlib.animation import PillowWriter
#from IPython.display import HTML
pullData = open("sampleText.txt","r").read()
dataArray = pullData.split('\n')
frm = len(dataArray)
fig = plt.figure()
ax1 = plt.axes(xlim=(0, size), ylim=(0, size))
line, = ax1.plot([],[], 'r-', lw=3)
xar = []
yar = []
def animate(i):
if i < size:
x,y = dataArray[i].split(',')
xar.append(int(x))
yar.append(int(y))
line.set_data(xar, yar)
ax1.set_ylim(0, max(yar)+3)
return line
ani = animation.FuncAnimation(fig, animate, frames=frm, interval=200, repeat=False)
ani.save('plot_ani_test.gif', writer='pillow')
# jupyter lab
# plt.close()
# HTML(ani.to_html5_video())
I've got some code similar to this
from matplotlib import animation
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
def animate(i):
x0,y0 = np.random.random(size=(2,))*4-2
x = np.random.normal(loc=x0, size=(1000,))
y = np.random.normal(loc=y0, size=(1000,))
for layer in prevlayers:
layer.remove()
prevlayers[:] = []
hexlayer = ax.hexbin(x,y, gridsize=10, alpha=0.5)
# the following line is needed in my code
hexlayer.remove()
prevlayers.append(hexlayer)
return hexlayer,
prevlayers = []
ani = animation.FuncAnimation(fig, animate, frames=12)
ani.save('teste.gif', writer='PillowWriter')
I'm trying to show only one frame at a time, but the code that I have written uses two ax.hexbin() calls and I have to remove one of them in order to show the correct graph. Is there a way to show one hexbin layer at a time using FuncAnimation?
You only need ax.clear() for each frame
from matplotlib import animation
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
def animate(i):
ax.clear()
x0,y0 = np.random.random(size=(2,))*4-2
x = np.random.normal(loc=x0, size=(1000,))
y = np.random.normal(loc=y0, size=(1000,))
hexlayer = ax.hexbin(x,y, gridsize=10, alpha=0.5)
return ax
ani = animation.FuncAnimation(fig, animate, frames=12)
ani.save('teste.gif', writer='PillowWriter')
this code produces
I want to liveplot some data acquired by instruments.
The following works (random data as an example):
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
from itertools import count
import time
%matplotlib qt
fields, volts = [], []
index = count()
def animate(i):
global fields, volts
fields.append(next(index))
volts.append(random.randint(0, 5))
plt.cla()
plt.plot(fields, volts)
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111)
ani = FuncAnimation(fig, animate, 1000)
plt.show()
... but I need to have the data update outside of FuncAnimation, in it own loop.
I believed it could work, but the following code just opens the window and doesnt update it until I stop the program:
fields, volts = [], []
index = count()
def animate(i):
global fields, volts
plt.cla()
plt.plot(fields, volts)
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111)
ani = FuncAnimation(fig, animate, 1000)
plt.show()
while (True):
time.sleep(1)
fields.append(next(index))
volts.append(random.randint(0, 5))
Try this, FuncAnimation calls your animate periodically so you could shift your append statements into that loop and also control the interval using the interval keyword argument.
fields, volts = [], []
index =iter(range(20))
from numpy import random
def animate(i):
global fields, volts
fields.append(len(volts))
volts.append(random.randint(0,5))
plt.cla()
plt.plot(fields, volts, c='c')
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111)
ani = FuncAnimation(fig, animate, frames=20, interval=1000)
plt.show()
so I am following a tutorial of plotting values that change in the regular intervals(live data)... I am using matplotlib and using FuncAnimation... I have imported all the necessary modules but no answer... here is what i have done and the output
x = []
y = []
c = count()
def anim(i):
x.append(next(c))
y.append(random.randint(0,10))
plt.cla()
plt.plot(x,y,'or',markersize=10)
ani = FuncAnimation(plt.gcf(),anim,interval=5000)
the output:
<Figure size 432x288 with 0 Axes>
This section explains the basic structure of the animation.
Configure the objects that will draw the graph. (Line 5)
Set the Y value you want to animate with the animation function (def anim()).
Then update the value of the graph object set in #1
Set the number of times to draw, the interval between draws, the inability to repeat, etc. with Funcanimation().
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
im, = ax.plot([], [], 'or', markersize=10)
x = np.arange(10)
ax.set_xlim(0, 9)
ax.set_ylim(0, 1)
def anim(i):
y = np.random.rand(10)
im.set_data(x, y)
anim = FuncAnimation(fig, anim, frames=50, interval=200, repeat=False, blit=False)
fig.show()