动画不适用于datetime X值,但它可以与整数一起使用

时间:2018-03-20 18:20:47

标签: python datetime animation matplotlib

我尝试使用matplotlib制作动画情节。当我使用X值的整数时,它可以正常工作:

#!/usr/bin/env python
import os
import random
import numpy as np
from datetime import datetime as dt, timedelta
from collections import deque

import matplotlib.pyplot as plt  # $ pip install matplotlib
import matplotlib.animation as animation

%matplotlib notebook

npoints = 30
x = deque([0], maxlen=npoints)
y = deque([0], maxlen=npoints)
fig, ax = plt.subplots()
[line] = ax.plot(x, y)

def get_data():
    t = random.randint(-100, 100)
    return t * np.sin(t**2)

def data_gen():
    while True:
        yield get_data()

def update(dy):
    x.append(x[-1] + 1)
    y.append(dy)
    line.set_data(x, y)
    ax.relim()
    ax.autoscale_view(True, True, True)
    return line, ax

plt.rcParams['animation.convert_path'] = 'c:/bin/convert.exe'
ani = animation.FuncAnimation(fig, update, data_gen, interval=500, blit=True)
#ani.save(os.path.join('C:/','temp','test.gif'), writer='imagemagick', fps=30)
plt.show()

这会产生以下动画:

enter image description here

但是,只要我尝试将datetime值用作x值,该图就为空:

npoints = 30
x = deque([dt.now()], maxlen=npoints)   # NOTE: `dt.now()`
y = deque([0], maxlen=npoints)
fig, ax = plt.subplots()
[line] = ax.plot(x, y)

def get_data():
    t = random.randint(-100, 100)
    return t * np.sin(t**2)

def data_gen():
    while True:
        yield get_data()

def update(dy):
    x.append(dt.now())                  # NOTE: `dt.now()`
    y.append(dy)
    line.set_data(x, y)
    ax.relim()
    ax.autoscale_view(True, True, True)
    return line, ax

plt.rcParams['animation.convert_path'] = 'c:/bin/convert.exe'
ani = animation.FuncAnimation(fig, update, data_gen, interval=1000, blit=True)
#ani.save(os.path.join('C:/','temp','test.gif'), writer='imagemagick', fps=30)
plt.show()

我做错了什么?

PS我使用matplotlib版本:2.1.2

2 个答案:

答案 0 :(得分:2)

问题中的代码在matplotlib 2.2.0的Jupyter笔记本(%matplotlib notebook)中运行正常。但是在脚本运行时使用以下任何后端都会失败:Qt4Agg,Qt4Cairo,TkAgg,TkCairo。

因此我怀疑@ M.F.上面的评论确实是真的,并且date2num转换是必要的。

这是下面的代码所做的,除了摆脱blitting,这在轴本身也必须被绘制的情况下是没有用的。

import random
import numpy as np
from datetime import datetime as dt, timedelta
from collections import deque
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.animation as animation

npoints = 30
x = deque([mdates.date2num(dt.now())], maxlen=npoints)   # NOTE: `dt.now()`
y = deque([0], maxlen=npoints)
fig, ax = plt.subplots()
[line] = ax.plot_date(x, y, ls="-", marker="")

def get_data():
    t = random.randint(-100, 100)
    return t * np.sin(t**2)

def data_gen():
    while True:
        yield get_data()


def update(dy):
    x.append(mdates.date2num(dt.now()))                  # NOTE: `dt.now()`
    y.append(dy)
    line.set_data(x, y)
    ax.relim()
    ax.autoscale_view(True, True, True)    

ani = animation.FuncAnimation(fig, update, data_gen, interval=1000)
#ani.save("anidates.gif", writer='imagemagick', fps=30)
plt.show()

答案 1 :(得分:1)

使用pandas,您可以注册转换器(通过调用register_matplotlib_converters())告诉matplotlib在调用datetime.datetime时如何处理line.set_data个对象,这样您就不必调用{{ 1}}关于每个值你自己:

date2num

使用matplotlib 2.2.0版进行测试,后端为TkAgg,Qt4Agg,Qt5Agg,GTK3Agg和GTK3Cairo。

import datetime as DT import collections import random import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation import matplotlib.dates as mdates import pandas.plotting as pdplt pdplt.register_matplotlib_converters() npoints = 30 x = collections.deque([DT.datetime.now()], maxlen=npoints) y = collections.deque([0], maxlen=npoints) fig, ax = plt.subplots() [line] = ax.plot(x, y) # Not necessary, but offers more control over the format xfmt = mdates.DateFormatter('%H:%M:%S') ax.xaxis.set_major_formatter(xfmt) def get_data(): t = random.randint(-100, 100) return t * np.sin(t**2) def data_gen(): while True: yield get_data() def update(dy): x.append(DT.datetime.now()) y.append(dy) line.set_data(list(x), y) ax.relim() ax.autoscale_view() # Not necessary, but it rotates the labels, making them more readable fig.autofmt_xdate() return [line] ani = animation.FuncAnimation(fig, update, data_gen, interval=1000, blit=False) plt.show() 维护着它使用的转换器注册表 到convert non "numlike" values到可绘制的值。

matplotlib.units

In [91]: import matplotlib.units as munits In [92]: munits.registry Out[92]: {numpy.str_: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc99e8>, numpy.bytes_: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc9a58>, str: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc9a20>, bytes: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc99b0>} 等高级绘图函数会自动处理plt.plot,但像datetime这样的低级方法则不会。因此,如果我们想要制作使用line.set_data个对象的动画,并且我们不希望在每个值上手动调用datetime,那么我们可以改为register a converter

如果我们安装了date2num,那么我们可以使用pandas代替从头开始编写转换器,它会教matplotlib来处理(除此之外) list pandas.plotting.register_matplotlib_converters个对象。

datetime.datetime

不幸的是,DatetimeConverter它不处理In [96]: import pandas.plotting as pdplt In [97]: pdplt.register_matplotlib_converters() In [98]: munits.registry Out[98]: {datetime.datetime: <pandas.plotting._converter.DatetimeConverter at 0x7f1d400145f8>, numpy.str_: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc99e8>, numpy.bytes_: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc9a58>, pandas._libs.tslibs.timestamps.Timestamp: <pandas.plotting._converter.DatetimeConverter at 0x7f1d40014668>, str: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc9a20>, numpy.datetime64: <pandas.plotting._converter.DatetimeConverter at 0x7f1d400142b0>, datetime.date: <pandas.plotting._converter.DatetimeConverter at 0x7f1d40014748>, datetime.time: <pandas.plotting._converter.TimeConverter at 0x7f1d40014240>, bytes: <matplotlib.category.StrCategoryConverter at 0x7f1d65cc99b0>, pandas._libs.tslibs.period.Period: <pandas.plotting._converter.PeriodConverter at 0x7f1d40014710>} 个对象的 deque

要绕过这个小障碍,请致电

datetime.datetime

而不是

line.set_data(list(x), y)