python matplotlib从函数更新散点图
- 2025-02-14 09:49:00
- admin 原创
- 39
问题描述:
我正在尝试自动更新散点图。我的 X 和 Y 值的来源是外部的,并且数据会在非预测的时间间隔(轮次)内自动推送到我的代码中。
我仅设法在整个过程结束时绘制所有数据,而我正尝试不断地将数据添加和绘制到画布中。
我得到的(在整个运行结束时)是这样的:
然而,我想要的是这个:
我的代码的简化版本:
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()
解决方案 1:
有几种方法可以为 matplotlib 图制作动画。下面让我们看两个使用散点图的最小示例。
(a)使用交互模式plt.ion()
要使动画发生,我们需要一个事件循环。获取事件循环的一种方法是使用plt.ion()
(“interactive on”)。然后需要先绘制图形,然后可以在循环中更新绘图。在循环中,我们需要绘制画布并引入一个短暂的暂停,以便窗口处理其他事件(如鼠标交互等)。如果没有这个暂停,窗口就会冻结。最后,我们调用plt.waitforbuttonpress()
让窗口在动画结束后保持打开状态。
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()
(b)使用FuncAnimation
上述大部分操作均可使用 实现自动化matplotlib.animation.FuncAnimation
。FuncAnimation 将负责循环和重绘,并animate()
在给定的时间间隔后不断调用函数(在本例中为 )。动画仅在plt.show()
调用后启动,从而自动在绘图窗口的事件循环中运行。
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()
解决方案 2:
据我了解,您希望以交互方式更新您的图。 如果是这样,您可以使用图而不是散点图,并像这样更新图的数据。
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()
如果这是您想要的行为,那么您只需要创建一个附加 sp 数据的函数,然后在该函数中获取您想要绘制的新点(使用 I/O 管理或您正在使用的任何通信过程)。希望这对您有所帮助。
解决方案 3:
这是在 Jupyter 笔记本中创建交互式绘图的一种方法
# 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)
这将迭代更新同一图。此处提供了详细说明https://pythonguides.com/matplotlib-update-plot-in-loop/
解决方案 4:
使用point.set_offsets(np.c_[xdata,ydata])
对我有用,因为当我更新到 matplotlib 3.9 时,ax.set_data 停止工作。
外观:
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()