关于python:如何更改matplotlib图的日期时间刻度标签频率?

How to change the datetime tick label frequency for matplotlib plots?

下面显示的是模拟数据图,其中包含我要修改的xtick。默认情况下,pd.df.plot选择大约间隔3个月的日期作为刻度。但是我想要的是每个月都在变动。做这个的最好方式是什么?那季节tick呢?先感谢您。

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
import matplotlib.dates as mdates

# convert date objects from pandas format to python datetime
index = pd.date_range(start ="2015-07-01", end ="2017-01-01", freq ="D")
index = [pd.to_datetime(date, format='%Y-%m-%d').date() for date in index]
data = np.random.randint(1,100, size=len(index))
df = pd.DataFrame(data=data,index=index, columns=['data'])
print (df.head())

ax = df.plot()
# set monthly locator
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
# set formatter
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
# set font and rotation for date tick labels
plt.gcf().autofmt_xdate()

plt.show()

对于季节标签,您必须自己构造它,然后使用plt.setp函数进行设置(对于02月份,设置标签winter,04-spring等):
plt.setp(new_labels, rotation=90, fontsize=9)

enter

1
2
3
4
5
6
            data
2015-07-01    26
2015-07-02    33
2015-07-03    46
2015-07-04    69
2015-07-05    17

我很难获得@Serenity答案,因为我直接与Matplotlib合作,而不是绘制Pandas数据集。因此,如果您是其中之一,我的回答可能会有所帮助。

用Matplotlib.plot()绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# Process dataset
bitcoin['Date'] = pd.to_datetime(bitcoin['Date'])
bitcoin['Open'] = pd.to_numeric(bitcoin['Open'])

# Plot
plt.figure()
plt.plot(bitcoin['Date'], bitcoin['Open'])
ax = plt.gca()
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
plt.gcf().autofmt_xdate() # Rotation
plt.show()

1
2
3
4
5
6
7
8
bitcoin[['Date', 'Open']].head()

    Date        Open
0   2017-09-05  4228.29
1   2017-09-04  4591.63
2   2017-09-03  4585.27
3   2017-09-02  4901.42
4   2017-09-01  4701.76

Bitcoin


此答案基于Serenity的答案以及ImportanceOfBeingErnest的答案。

自定义时间序列刻度标签的最佳方法是使用matplotlib.dates模块(mdates)中的刻度定位符和格式化程序。尽管值得注意的是,如果您希望基于与所绘制的时间序列相同的单位来设置刻度频率,则使用日期作为字符串来创建和格式化刻度标签可能会更加方便,例如在此问题的答案中pandas条形图。

如文档中所述,pandas使用matplotlib创建具有自己的自定义刻度格式的时间序列图:

pandas provides custom formatters for timeseries plots. These change the formatting of the axis labels for dates and times. By default, the custom formatters are applied only to plots created by pandas with DataFrame.plot() or Series.plot().

pandas时间序列图的刻度和标签当前默认为以下格式:

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np                   # v 1.19.2
import pandas as pd                  # v 1.1.3
import matplotlib.dates as mdates     # v 3.3.2

# Create random dataset stored as a pandas DataFrame with a DatetimeIndex
rng = np.random.default_rng(seed=1) # random number generator
date_day = pd.date_range(start='2015-07-01', end='2016-12-31', freq='D')
traffic = rng.lognormal(sigma=2, size=date_day.size)
df_day = pd.DataFrame(dict(traffic=traffic), index=date_day)

# Create pandas plot with default settings except for figure size
df_day.plot(figsize=(10,5));

pd_default_plot

为了能够使用mdates刻度定位器和格式化程序并覆盖默认的刻度格式,必须由matplotlib正确识别pandas的日期。问题在于,pandas和matplotlib具有不同的方法来计算用于在时间轴(默认为x轴)上定位刻度的日期数字。

在pandas中,时间是从1970-01-01 00:00:00(Unix纪元的起源)的零开始以纳秒为单位测量的,各个时间点被存储为pandas时间戳对象。但是在创建图的时间尺度时,pandas使用了另一个编号系统,该编号系统从相同的起点开始,然后在所选频率的每个周期内增加1(在本例中,频率以天为单位)。

自2020年7月发布3.3.0版以来,Matplotlib使用与pandas相同的默认来源,但日期始终以天为单位编号:

Matplotlib represents dates using floating point numbers specifying the number of days since a default epoch of 1970-01-01 UTC; for example, 1970-01-01, 06:00 is the floating point number 0.25.

使用pandas时,可以通过运行ax.get_xticks()ax = df.plot()来检查刻度所使用的数字。

您可能已经猜到了,这意味着当时间序列的频率以天为单位时,就不需要日期转换,如此处所示,其中带有简单的自定义刻度定位器和格式化程序:

1
2
3
4
5
6
7
8
ax = df_day.plot(figsize=(10,5))

# Create custom ticks using matplotlib date tick locator and formatter
loc = mdates.MonthLocator(interval=2)
ax.xaxis.set_major_locator(loc)
fmt = mdates.DateFormatter('%b\
%Y'
)
ax.xaxis.set_major_formatter(fmt)

pd_tsdays_customticks

这种特殊情况便于将其他pandas的默认设置保持为x轴限制和较小的x刻度。但这是一般规则的例外。

要能够将mdates刻度定位器和格式化程序与任何频率类型的时间序列的pandas图一起使用,您需要使用(已存在但尚无文档字符串且几乎未记录的文档)x_compat=True参数。以下示例说明了将其与重新采样到每月频率的相同数据集一起使用的情况。通常情况下,您只是想稍微调整默认的pandas格式,因此在以下示例中,将从头开始重新创建默认格式,以显示可用于调整它的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Resample time series to monthly frequency and plot it using date
# numbers that are compatible with mdates
df_month = df_day.resample('MS').sum()
ax = df_month.plot(figsize=(10,5), x_compat=True)

# Set major and minor date tick locators
maj_loc = mdates.MonthLocator(bymonth=np.arange(1,12,2))
ax.xaxis.set_major_locator(maj_loc)
min_loc = mdates.MonthLocator()
ax.xaxis.set_minor_locator(min_loc)

# Set major date tick formatter
zfmts = ['', '%b\
%Y'
, '%b', '%b-%d', '%H:%M', '%H:%M']
maj_fmt = mdates.ConciseDateFormatter(maj_loc, zero_formats=zfmts, show_offset=False)
ax.xaxis.set_major_formatter(maj_fmt)

ax.figure.autofmt_xdate(rotation=0, ha='center')
ax.set_xlim(df_month.index.min(), df_month.index.max());

pandas_tsmonths_customticks

文档:pd.date_range,日期格式代码,mdates.ConciseDateFormatterfig.autofmt_xdate