我有一个M x N 2D数组:第i行代表在时间i的N个点的值。
我希望以图形的形式可视化点[数组的1行],其中值会在一小段时间后更新。因此,图表一次显示1行,然后将值更新为下一行,依此类推。
我想在jupyter笔记本中这样做。寻找参考代码。
我尝试过但没有成功:
答案 0 :(得分:41)
这是另一种可能更简单的解决方案:
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()
fig.show()
fig.canvas.draw()
for i in range(0,100):
ax.clear()
ax.plot(matrix[i,:])
fig.canvas.draw()
答案 1 :(得分:6)
答案 2 :(得分:3)
我一直在为一个线程正在抽取数据并且我们希望Jupyter笔记本在不阻塞任何内容的情况下不断更新图形的情况下寻找一个好的答案。在浏览了大约十二个相关答案之后,下面是一些发现:
警告
如果想要实时图表,请不要使用下面的魔术。如果笔记本使用以下内容,则无法进行图形更新:
%load_ext autoreload
%autoreload 2
在导入matplotlib之前,您需要先在笔记本上做到以下几点:
%matplotlib notebook
方法1:使用FuncAnimation
这具有一个缺点,即使您的数据尚未更新,也会进行图形更新。下面的示例显示了另一个线程通过FuncAnimation
更新数据时Jupyter Notebook更新图形的数据。
%matplotlib notebook
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
from threading import Thread
import time
class LiveGraph:
def __init__(self):
self.x_data, self.y_data = [], []
self.figure = plt.figure()
self.line, = plt.plot(self.x_data, self.y_data)
self.animation = FuncAnimation(self.figure, self.update, interval=1000)
self.th = Thread(target=self.thread_f, daemon=True)
self.th.start()
def update(self, frame):
self.line.set_data(self.x_data, self.y_data)
self.figure.gca().relim()
self.figure.gca().autoscale_view()
return self.line,
def show(self):
plt.show()
def thread_f(self):
x = 0
while True:
self.x_data.append(x)
x += 1
self.y_data.append(randrange(0, 100))
time.sleep(1)
g = LiveGraph()
g.show()
方法2:直接更新
第二种方法是在数据从另一个线程到达时更新图形。这是有风险的,因为matplotlib不是线程安全的,但是只要只有一个线程在进行更新,它就可以正常工作。
%matplotlib notebook
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
from threading import Thread
import time
class LiveGraph:
def __init__(self):
self.x_data, self.y_data = [], []
self.figure = plt.figure()
self.line, = plt.plot(self.x_data, self.y_data)
self.th = Thread(target=self.thread_f, daemon=True)
self.th.start()
def update_graph(self):
self.line.set_data(self.x_data, self.y_data)
self.figure.gca().relim()
self.figure.gca().autoscale_view()
def show(self):
plt.show()
def thread_f(self):
x = 0
while True:
self.x_data.append(x)
x += 1
self.y_data.append(randrange(0, 100))
self.update_graph()
time.sleep(1)
from live_graph import LiveGraph
g = LiveGraph()
g.show()
答案 3 :(得分:1)
除了@ 0aslam0之外,我还使用了来自here的代码。我刚刚更改了动画功能,以便每次都能获得下一行。它绘制了所有N个点的动画演化(M步)。
from IPython.display import HTML
import numpy as np
from matplotlib import animation
N = 5
M = 100
points_evo_array = np.random.rand(M,N)
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, M), ylim=(0, np.max(points_evo_array)))
lines = []
lines = [ax.plot([], [])[0] for _ in range(N)]
def init():
for line in lines:
line.set_data([], [])
return lines
def animate(i):
for j,line in enumerate(lines):
line.set_data(range(i), [points_evo_array[:i,j]])
return lines
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate,np.arange(1, M), init_func=init, interval=10, blit=True)
HTML(anim.to_html5_video())
希望它会有用
答案 4 :(得分:1)
通过对@Shital Shah解决方案的适度修改,我创建了一个更通用的框架,该框架可以简单地应用于各种情况:
import matplotlib
from matplotlib import pyplot as plt
class LiveLine:
def __init__(self, graph, fmt=''):
# LiveGraph object
self.graph = graph
# instant line
self.line, = self.graph.ax.plot([], [], fmt)
# holder of new lines
self.lines = []
def update(self, x_data, y_data):
# update the instant line
self.line.set_data(x_data, y_data)
self.graph.update_graph()
def addtive_plot(self, x_data, y_data, fmt=''):
# add new line in the same figure
line, = self.graph.ax.plot(x_data, y_data, fmt)
# store line in lines holder
self.lines.append(line)
# update figure
self.graph.update_graph()
# return line index
return self.lines.index(line)
def update_indexed_line(self, index, x_data, y_data):
# use index to update that line
self.lines[index].set_data(x_data, y_data)
self.graph.update_graph()
class LiveGraph:
def __init__(self, backend='nbAgg', figure_arg={}, window_title=None,
suptitle_arg={'t':None}, ax_label={'x':'', 'y':''}, ax_title=None):
# save current backend for later restore
self.origin_backend = matplotlib.get_backend()
# check if current backend meets target backend
if self.origin_backend != backend:
print("original backend:", self.origin_backend)
# matplotlib.use('nbAgg',warn=False, force=True)
plt.switch_backend(backend)
print("switch to backend:", matplotlib.get_backend())
# set figure
self.figure = plt.figure(**figure_arg)
self.figure.canvas.set_window_title(window_title)
self.figure.suptitle(**suptitle_arg)
# set axis
self.ax = self.figure.add_subplot(111)
self.ax.set_xlabel(ax_label['x'])
self.ax.set_ylabel(ax_label['y'])
self.ax.set_title(ax_title)
# holder of lines
self.lines = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def close(self):
# check if current beckend meets original backend, if not, restore it
if matplotlib.get_backend() != self.origin_backend:
# matplotlib.use(self.origin_backend,warn=False, force=True)
plt.switch_backend(self.origin_backend)
print("restore to backend:", matplotlib.get_backend())
def add_line(self, fmt=''):
line = LiveLine(graph=self, fmt=fmt)
self.lines.append(line)
return line
def update_graph(self):
self.figure.gca().relim()
self.figure.gca().autoscale_view()
self.figure.canvas.draw()
使用上述2类,您可以简单地重现@Graham S的示例:
import numpy as np
m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)
with LiveGraph(backend='nbAgg') as h:
line1 = h.add_line()
for i in range(0,100):
line1.update(range(len(matrix[i,:])), matrix[i,:])
请注意,默认后端为nbAgg
,您可以传递其他后端,例如qt5Agg
。完成后,它将还原到您的原始后端。
和@Tom Hale的示例:
with LiveGraph(figure_arg={'num':'DORMANT2'}, window_title='Canvas active title',
suptitle_arg={'t':'Figure title','fontsize':20},
ax_label={'x':'x label', 'y':''}, ax_title='Axes title') as g:
with LiveGraph() as h:
line1 = g.add_line()
line2 = h.add_line('rx')
end = 40
for i in range(end):
line1.addtive_plot(range(i,end), (i,)*(end-i))
line2.update(range(i,end), range(i,end))
此外,您可以在@Tom Hale的示例的加法图中更新特定行:
import numpy as np
with LiveGraph(figure_arg={'num':'DORMANT3'}, window_title='Canvas active title',
suptitle_arg={'t':'Figure title','fontsize':20},
ax_label={'x':'x label', 'y':''}, ax_title='Axes title') as g:
line1 = g.add_line()
end = 40
for i in range(end):
line_index = line1.addtive_plot(range(i,end), (i,)*(end-i))
for i in range(100):
j = int(20*(1+np.cos(i)))
# update line of index line_index
line1.update_indexed_line(line_index, range(j,end), (line_index,)*(end-j))
请注意,第二个for循环仅用于更新索引为line_index
的特定行。您可以将该索引更改为另一行的索引。
就我而言,我在机器学习训练循环中使用它来逐步更新学习曲线。
import numpy as np
import time
# create a LiveGraph object
g = LiveGraph()
# add 2 lines
line1 = g.add_line()
line2 = g.add_line()
# create 2 list to receive training result
list1 = []
list2 = []
# training loop
for i in range(100):
# just training
time.sleep(0.1)
# get training result
list1.append(np.random.normal())
list2.append(np.random.normal())
# update learning curve
line1.update(np.arange(len(list1)), list1)
line2.update(np.arange(len(list2)), list2)
# don't forget to close
g.close()
答案 5 :(得分:0)
这是一个处理实时绘图/记录数据(joystick)的库,虽然我不确定它是否与jupyter一起使用。您可以使用通常的pip install joystick
安装它。
很难在没有关于数据的更多细节的情况下制作出有效的解决方案。这是一个选项:
import joystick as jk
import numpy as np
class test(jk.Joystick):
# initialize the infinite loop decorator
_infinite_loop = jk.deco_infinite_loop()
def _init(self, *args, **kwargs):
"""
Function called at initialization, see the docs
"""
# INIT DATA HERE
self.shape = (10, 4) # M, N
self.data = np.random.random(self.shape)
self.xaxis = range(self.shape[1])
############
# create a graph frame
self.mygraph = self.add_frame(
jk.Graph(name="TheName", size=(500, 500), pos=(50, 50),
fmt="go-", xnpts=self.shape[1], freq_up=5, bgcol="w",
xylim=(0, self.shape[1]-1, None, None)))
@_infinite_loop(wait_time=0.5)
def _generate_fake_data(self): # function looped every 0.5 second
"""
Loop starting with the simulation start, getting data and
pushing it to the graph every 0.5 seconds
"""
# NEW (RANDOM) DATA
new_data = np.random.random(self.shape[1])
# concatenate data
self.data = np.vstack((self.data, new_data))
# push new data to the graph
self.mygraph.set_xydata(self.xaxis, self.data[-1])
t = test()
t.start()
t.stop()
t.exit()
此代码将创建一个每秒自动更新5次的图形(freq_up = 5),同时每0.5秒(wait_time = 0.5)(随机)生成新数据并将其推送到图形以供显示。
如果您不想让Y轴摆动,请输入t.mygraph.xylim = (0, t.shape[1]-1, 0, 1)
。
答案 6 :(得分:0)
我对此进行了探索,并产生了以下内容,这些内容大部分是自我记录的:
import matplotlib.pyplot as plt
%matplotlib notebook
print('This text appears above the figures')
fig1 = plt.figure(num='DORMANT')
print('This text appears betweeen the figures')
fig2 = plt.figure()
print('This text appears below the figures')
fig1.canvas.set_window_title('Canvas active title')
fig1.suptitle('Figure title', fontsize=20)
# Create plots inside the figures
ax1 = fig1.add_subplot(111)
ax1.set_xlabel('x label')
ax2 = fig2.add_subplot(111)
# Loop to update figures
end = 40
for i in range(end):
ax2.cla() # Clear only 2nd figure's axes, figure 1 is ADDITIVE
ax1.set_title('Axes title') # Reset as removed by cla()
ax1.plot(range(i,end), (i,)*(end-i))
ax2.plot(range(i,end), range(i,end), 'rx')
fig1.canvas.draw()
fig2.canvas.draw()
答案 7 :(得分:-6)