使用Matplotlib与类别轴进行交互式绘图

时间:2018-10-07 15:39:15

标签: python matplotlib

我有以下代码片段来说明问题:

import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import time
import random

# Mock categories
categories = ["cat1", "cat2", "cat3", "cat4"]

counter = 0

# Plot
x_data = []
y_data = []

plt.ion()
fig = plt.figure()

subplt = fig.add_subplot(312)
subplt_line, = subplt.plot(x_data, y_data, 'b.')

while True:

    time.sleep(0.1)  # simulate some delay that occurs in the actual application

    x_data.append(counter)
    subplt_line.set_xdata(x_data)

    counter += 1

    # y_data.append(random.randrange(1, 15))  # This works fine (except the scaling)
    y_data.append(random.choice(categories))  # This will end in an exception
    subplt_line.set_ydata(y_data)

    # Update the plot
    fig.canvas.draw()
    fig.canvas.flush_events()

它将以这样的异常结束:

Traceback (most recent call last):
  File "test.py", line 36, in <module>
    fig.canvas.draw()
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/backends/backend_tkagg.py", line 12, in draw
    super(FigureCanvasTkAgg, self).draw()
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 437, in draw
    self.figure.draw(self.renderer)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/figure.py", line 1493, in draw
    renderer, self, artists, self.suppressComposite)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
    a.draw(renderer)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/axes/_base.py", line 2635, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
    a.draw(renderer)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/lines.py", line 738, in draw
    self.recache()
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/lines.py", line 656, in recache
    yconv = self.convert_yunits(self._yorig)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/artist.py", line 200, in convert_yunits
    return ax.yaxis.convert_units(y)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/axis.py", line 1526, in convert_units
    ret = self.converter.convert(x, self.units, self)
  File "/anaconda3/envs/xxx/lib/python2.7/site-packages/matplotlib/category.py", line 65, in convert
    unit.update(values)
AttributeError: 'NoneType' object has no attribute 'update'

我似乎是分类y数据和交互性的结合导致了这一点。使用数值时,它可以很好地工作;使用非交互式功能时,即使是分类轴也可以很好地工作。

另一个问题是y轴的自动缩放。通过set_y_data()点添加的新值似乎触发了这一点。

该图将可视化对无限数据流所做的分析,并且像仪表板一样使用-因此该图应随循环的每次迭代更新。

1 个答案:

答案 0 :(得分:2)

我无法使用while循环运行您的代码,但是无论如何,我建议还是使用FuncAnimation创建自更新图形(SO和在线示例很多)。

我相信您的问题在于Line2D对象的初始化。当您传递任何空的y数组时,matplotlib似乎假设您将使用数字值而非分类值。将字符串初始化为y值似乎可以解决问题。您必须调整代码,以使创建的第一点对您的数据有意义,但这仅是一个小麻烦。

对于轴的缩放,matplotlib将每个类别添加到一个新的整数值,因此您只需要计算数据中有多少类别即可知道轴的范围。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
import random

# Mock categories
categories = ["cat1", "cat2", "cat3", "cat4"]

counter = 0

# Plot
x_data = [0]
y_data = ['cat1']

fig = plt.figure()

subplt = fig.add_subplot(312)
subplt_line, = subplt.plot(x_data, y_data, 'b.-')
debug_text = fig.text(0, 1, "TEXT", va='top')  # for debugging

def init():
    subplt_line.set_data([0],['cat1'])

def animate(num, ax):
    new_x, new_y = num, random.choice(categories)
    debug_text.set_text('{:d} {:s}'.format(num, new_y))
    x, y = subplt_line.get_data()
    x = np.append(x, new_x)
    y = np.append(y, new_y)
    subplt_line.set_data(x,y)
    ax.set_xlim(min(x),max(x))
    ax.set_ylim(0,len(np.unique(y))-1)
    return subplt_line,debug_text

ani = animation.FuncAnimation(fig, animate, fargs=[subplt], init_func=init, frames=20, blit=False, repeat=False)
plt.show()

enter image description here