我尝试使用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()
这会产生以下动画:
但是,只要我尝试将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
答案 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)