Reputation: 909
I am trying to automatically update a scatter plot. The source of my X and Y values is external, and the data is pushed automatically into my code in a non-predicted time intervals (rounds).
I have only managed to plot all the data when the whole process ended, whereas I am trying to constantly add and plot data into my canvas.
What I DO get (at the end of the whole run) is this:
Whereas, what I am after is this:
A simplified version of my code:
import matplotlib.pyplot as plt
def read_data():
#This function gets the values of xAxis and yAxis
xAxis = [some values] #these valuers change in each run
yAxis = [other values] #these valuers change in each run
plt.scatter(xAxis,yAxis, label = 'myPlot', color = 'k', s=50)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
Upvotes: 25
Views: 48798
Reputation: 11
Using point.set_offsets(np.c_[xdata,ydata])
worked for me because when I updated to matplotlib 3.9, ax.set_data stopped working.
How it looks:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def lerp(p0: np.array, p1: np.array,t = None,res=10):
# t = np.array(t)
if t == None:
t = np.linspace(0,1,res)
outX = (1-t)*p0[0]+t*p1[0]
outY = (1-t)*p0[1]+t*p1[1]
return outX,outY
def lerpAnimation(p0: np.array, p1: np.array,res = 10,speed=100):
plt.ioff()
xdata, ydata = [0],[0]
fig, ax = plt.subplots()
point = ax.scatter(xdata, ydata, lw=2)
ax.grid()
firstLerp = lerp(p0,p1)
ax.plot(firstLerp[0],firstLerp[1])
def run(t):
timedLerp = lerp(p0,p1,t)
xdata[0], ydata[0] = timedLerp[0],timedLerp[1]
# Here is where the values are changed
point.set_offsets(np.c_[xdata,ydata])
return point,
ani = animation.FuncAnimation(fig, run, (i for i in np.linspace(0,1,res)), interval=speed,
save_count=100,repeat = True)
plt.show()
Upvotes: 1
Reputation: 169
Here is one way of creating an interactive plot in Jupyter notebook
# Import Libraries
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, clear_output
# Create figure and subplot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Define and update plot
for i in range(20):
x = np.linspace(0, i, 100);
y = np.cos(x)
ax.set_xlim(0, i)
ax.cla()
ax.plot(x, y)
display(fig)
clear_output(wait = True)
plt.pause(0.1)
This will update the same plot iteratively. A detailed description is given here https://pythonguides.com/matplotlib-update-plot-in-loop/
Upvotes: 1
Reputation: 339120
There are several ways to animate a matplotlib plot. In the following let's look at two minimal examples using a scatter plot.
plt.ion()
For an animation to take place we need an event loop. One way of getting the event loop is to use plt.ion()
("interactive on"). One then needs to first draw the figure and can then update the plot in a loop. Inside the loop, we need to draw the canvas and introduce a little pause for the window to process other events (like the mouse interactions etc.). Without this pause the window would freeze. Finally we call plt.waitforbuttonpress()
to let the window stay open even after the animation has finished.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
plt.draw()
for i in range(1000):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
fig.canvas.draw_idle()
plt.pause(0.1)
plt.waitforbuttonpress()
FuncAnimation
Much of the above can be automated using matplotlib.animation.FuncAnimation
. The FuncAnimation will take care of the loop and the redrawing and will constantly call a function (in this case animate()
) after a given time interval. The animation will only start once plt.show()
is called, thereby automatically running in the plot window's event loop.
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
def animate(i):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=100, repeat=True)
plt.show()
Upvotes: 50
Reputation: 314
From what I understand, you want to update interactively your plot. If so, you can use plot instead of scatter plot and update the data of your plot like this.
import numpy
import matplotlib.pyplot as plt
fig = plt.figure()
axe = fig.add_subplot(111)
X,Y = [],[]
sp, = axe.plot([],[],label='toto',ms=10,color='k',marker='o',ls='')
fig.show()
for iter in range(5):
X.append(numpy.random.rand())
Y.append(numpy.random.rand())
sp.set_data(X,Y)
axe.set_xlim(min(X),max(X))
axe.set_ylim(min(Y),max(Y))
raw_input('...')
fig.canvas.draw()
If this is the behaviour your are looking for, you just need to create a function appending the data of sp, and get in that function the new points you want to plot (either with I/O management or whatever the communication process you're using). I hope it helps.
Upvotes: 8