Pandas 时间序列图设置 x 轴主刻度和次刻度以及标签
- 2025-02-13 08:36:00
- admin 原创
- 50
问题描述:
我希望能够为从 Pandas 时间序列对象绘制的时间序列图设置主 xticks 和次 xticks 及其标签。
Pandas 0.9 的“新功能”页面显示:
“您可以使用 to_pydatetime 或为 Timestamp 类型注册一个转换器”
但我不知道如何做到这一点,以便我可以使用 matplotlibax.xaxis.set_major_locator
和ax.xaxis.set_major_formatter
(和次要)命令。
如果我在不转换熊猫时间的情况下使用它们,x 轴刻度和标签最终会出错。
通过使用 'xticks' 参数,我可以将主刻度传递给 pandas' .plot
,然后设置主刻度标签。我无法弄清楚如何使用这种方法来设置小刻度(我可以在 pandas' 设置的默认小刻度上设置标签.plot
)。
这是我的测试代码:
图表 xaxis 上有奇怪的日期
import pandas as pd
import matplotlib.dates as mdates
import numpy as np
dateIndex = pd.date_range(start='2011-05-01', end='2011-07-01', freq='D')
testSeries = pd.Series(data=np.random.randn(len(dateIndex)), index=dateIndex)
ax = plt.figure(figsize=(7,4), dpi=300).add_subplot(111)
testSeries.plot(ax=ax, style='v-', label='first line')
# using MatPlotLib date time locators and formatters doesn't work with new
# pandas datetime index
ax.xaxis.set_minor_locator(mdates.WeekdayLocator())
ax.xaxis.set_minor_formatter(mdates.DateFormatter('%d
%a'))
ax.xaxis.grid(True, which="minor")
ax.xaxis.grid(False, which="major")
ax.xaxis.set_major_formatter(mdates.DateFormatter('
%b%Y'))
plt.show()
带有正确日期的图表(无小刻度)
# set the major xticks and labels through pandas
ax2 = plt.figure(figsize=(7,4), dpi=300).add_subplot(111)
xticks = pd.date_range(start='2011-05-01', end='2011-07-01', freq='W-Tue')
testSeries.plot(ax=ax2, style='-v', label='second line', xticks=xticks.to_pydatetime())
ax2.set_xticklabels([x.strftime('%a
%d
%h
%Y') for x in xticks]);
# remove the minor xtick labels set by pandas.plot
ax2.set_xticklabels([], minor=True)
# turn the minor ticks created by pandas.plot off
plt.show()
更新:通过使用循环构建主要的 xtick 标签,我已经能够更接近我想要的布局:
# only show month for first label in month
month = dStart.month - 1
xticklabels = []
for x in xticks:
if month != x.month :
xticklabels.append(x.strftime('%d
%a
%h'))
month = x.month
else:
xticklabels.append(x.strftime('%d
%a'))
但是,这有点像使用 x 轴ax.annotate
:可行但并不理想。
绘制熊猫时间序列数据时,如何设置主刻度和次刻度?
解决方案 1:
pandas
和都matplotlib.dates
用于matplotlib.units
定位蜱虫。
但是,尽管matplotlib.dates
有方便的方法来手动设置刻度,但到目前为止,pandas 似乎仍专注于自动格式化(您可以查看pandas 中日期转换和格式化的代码)。
因此目前看来使用它更为合理matplotlib.dates
(正如@BrenBarn 在他的评论中提到的)。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates
idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)
fig, ax = plt.subplots()
ax.plot_date(idx.to_pydatetime(), s, 'v-')
ax.xaxis.set_minor_locator(dates.WeekdayLocator(byweekday=(1),
interval=1))
ax.xaxis.set_minor_formatter(dates.DateFormatter('%d
%a'))
ax.xaxis.grid(True, which="minor")
ax.yaxis.grid()
ax.xaxis.set_major_locator(dates.MonthLocator())
ax.xaxis.set_major_formatter(dates.DateFormatter('
%b
%Y'))
plt.tight_layout()
plt.show()
(我的语言环境是德语,因此星期二 [Tue] 变成了 Dienstag [Di])
解决方案 2:
要关闭 Pandas Datetime 刻度调整,必须添加参数x_compat=True
例子:
ds.plot(x_compat=True)
请参阅 Pandas 文档中的更多示例:抑制刻度分辨率调整
解决方案 3:
在matplotlib中plot()
,默认的时间序列单位是1天,而在pandas中plot()
,1个单位等于时间序列的频率,所以如果频率是1天,那么1个单位就是1天;如果是1小时,那么1个单位就是1小时等等。这使得plot()
matplotlib和pandas在处理时间序列数据时的调用有所不同。
如果时间序列的频率是1天,那么matplotlib.dates.WeekdayLocator
等matplotlib.dates.MonthLocator
可以“定位”刻度位置1,因为1天是pandas使用1天作为基本单位来制作xtick位置plot()
(与matplotlib的默认值一致)。
由于 pandas 的plot()
调用返回一个 Axes 对象,因此可以使用 来修改该 Axes 对象的刻度标签matplotlib.dates
。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
idx = pd.date_range('2011-05-01', '2011-07-01', freq='D')
s1 = pd.Series(np.random.randn(len(idx)), index=idx)
ax = s1.plot(style='v-')
ax.xaxis.set(
minor_locator=mdates.WeekdayLocator(), # make minor ticks on each Tuesday
minor_formatter=mdates.DateFormatter('%d
%a'), # format minor ticks
major_locator=mdates.MonthLocator(), # make major ticks on first day of each month
major_formatter=mdates.DateFormatter('
%b
%Y') # format major ticks
);
但是,如果频率不是 1 天,而是 1 周,那么matplotlib.dates
将无法定位位置,因为如前所述,pandasplot()
将单位设置为与时间序列频率(1 周)相同,这会“混淆” matplotlib.dates
。因此,如果我们尝试使用用于设置刻度标签的相同代码s1
来设置刻度标签s2
,那么我们将得到非常错误的刻度标签。
要“解决”该问题,一种方法是通过传递来删除 pandas 的自动刻度分辨率调整x_compat=True
。然后可以使用 matplotlib 的分辨率设置主/次刻度标签;换句话说,可以按照与上述相同的方式进行设置。
idx = pd.date_range('2011-05-01', '2011-07-01', freq='W')
s2 = pd.Series(np.random.randn(len(idx)), index=idx)
ax = s2.plot(style='v-', x_compat=True, rot=0)
ax.xaxis.set(
minor_locator=mdates.WeekdayLocator(), # make minor ticks on each Tuesday
minor_formatter=mdates.DateFormatter('%d'), # format minor ticks
major_locator=mdates.MonthLocator(), # make major ticks on first day of each month
major_formatter=mdates.DateFormatter('
%b
%Y') # format major ticks
);
解决该问题的另一种方法是改用 matplotlib (如@bmuplot()
所建议)。由于单位在 matplotlib 中是固定的,因此我们可以像上面一样设置刻度标签而不会出现问题。
plt.plot(s2.index, s2, 'v-') # use matplotlib instead
plt.gca().xaxis.set(
minor_locator=mdates.WeekdayLocator(byweekday=0), # make minor ticks on each Monday
minor_formatter=mdates.DateFormatter('%d'), # format minor ticks
major_locator=mdates.MonthLocator(), # make major ticks on first day of each month
major_formatter=mdates.DateFormatter('
%b
%Y') # format major ticks
);
1 matplotlib.dates.num2timedelta(1) == datetime.timedelta(days=1)
为真。