matplotlib 使用时间轴制作散点图动画

5t7ly7z5  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(107)

我使用this question制作了一个这样的动画散点图:

import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.collections import PathCollection

df = pd.DataFrame({"x": [1, 2, 6, 4, 5, 6], "y": [1, 4, 36, 16, 25, 36]})
plt.ion()
fig: plt.Figure = plt.figure
ax = fig.subplots()
path_collection: PathCollection = ax.scatter(df.loc[0:2, "x"], df.loc[0:2, "y"])
# Note: I don't use pandas built in DataFrame.plot.scatter function so I can get the PathCollection object to later change the scatterpoints.
fig.canvas.draw()
path_collection.set_offsets([[row.x, row.y] for index, row in df.loc[3:].iterrows()])
# Due to the format of offset (array-like (N,2)) this seems to be the best way to provide the data.

fig.canvas.draw()

字符串
这工作得很好,但我想在x轴上有时间,所以我尝试将上面的代码改为:

import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.collections import PathCollection

df = pd.DataFrame({'time': [pd.Timestamp('2021-02-04 00:00:01'),
                            pd.Timestamp('2021-02-04 00:00:02'),
                            pd.Timestamp('2021-02-04 00:00:10'),
                            pd.Timestamp('2021-02-04 00:00:05'),
                            pd.Timestamp('2021-02-04 00:00:06'),
                            pd.Timestamp('2021-02-04 00:00:08')],
                   'y': [5, 6, 10, 8, 9, 10]})
fig: plt.Figure = plt.figure()
ax = fig.subplots()
sc: PathCollection = ax.scatter(df.loc[0:2, "time"], df.loc[0:2, "y"])
fig.canvas.draw()    
sc.set_offsets([[row.time, row.y] for index, row in df.loc[3:].iterrows()])

fig.canvas.draw()


倒数第二行抛出了这个错误:TypeError: float() argument must be a string or a number, not 'Timestamp'。这似乎是由于PathCollection将它存储为一个numpy数组,而这个数组不能包含Timestamp。所以我想知道,有没有一个变通方法可以用时间轴来动画散点?
先谢了。

vyu0f0g1

vyu0f0g11#

对于任何有同样问题的人,我找到了一个可能远非理想的解决方案,但它确实完成了这项工作。事实证明,PathCollection将时间存储为np.float64,表示自1/1/0001以来的天数。由于使用set_offset似乎只能用于大小为(N,2)的类似数组的对象,我将pd.Timestamp重新格式化为从1/1/0001开始的天数,如下所示:

time = pd.Timestamp('2021-02-04 00:00:01')
sec_since_1970 = time.timestamp()           # pd.Timestamp.timestamp() gives seconds since epox.
days_since_1970 = sec_since_1970/60/60/24   # Seconds to days.
days_since_0001 = days_since_1970 + 719163  # 719163 = number of days from 1/1/0001 until 1/1/1970

字符串
在问题中实现这一点可以得到以下结果:

df = pd.DataFrame({'time': [pd.Timestamp('2021-02-04 00:00:01'),
                            pd.Timestamp('2021-02-04 00:00:02'),
                            pd.Timestamp('2021-02-04 00:00:10'),
                            pd.Timestamp('2021-02-04 00:00:05'),
                            pd.Timestamp('2021-02-04 00:00:06'),
                            pd.Timestamp('2021-02-04 00:00:08')],
                   'y': [5, 6, 10, 8, 9, 10]})
data = [[719163 + row.time.timestamp()/60/60/24, row.y] for index, row in df.loc[3:].iterrows()]
sc.set_offsets(data)
# Set axis limits
ax.set_ylim(bottom=df.y.loc[3:].min() -1, top=df.y.loc[3:].max()+1)
ax.set_xlim(left= (df.time.loc[3:].min().timestamp()-1)/60/60/24+719163,
            right=(df.time.loc[3:].max().timestamp()+1)/60/60/24+719163)
fig.canvas.draw()


所以这解决了问题,它可能不是最有效的方法,但对于小数据集,它工作得很好。

相关问题