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

Posted

技术标签:

【中文标题】如何更改 matplotlib 图的日期时间刻度标签频率?【英文标题】:How to change the datetime tick label frequency for matplotlib plots? 【发布时间】:2018-01-24 01:17:54 【问题描述】:

下面显示了一个模拟数据图,其中包含我要修改的 xticks。默认情况下,pd.df.plot 选择相隔大约 3 个月的日期作为刻度。但我想要的是每个月都是一个滴答声。做这个的最好方式是什么?季节性蜱虫呢?先感谢您。

【问题讨论】:

【参考方案1】:

首先,您必须将 pandas 日期对象转换为 python 日期对象。由于 matplotlib 内部日期转换功能,需要此转换。然后使用matplotlib.dates 中的函数设置所需的格式化程序和刻度位置,如下所示:

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).

df 的负责人:

            data
2015-07-01    26
2015-07-02    33
2015-07-03    46
2015-07-04    69
2015-07-05    17

【讨论】:

【参考方案2】:

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

使用 Matplotlib.plot() 绘图

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()
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

【讨论】:

【参考方案3】:

此答案基于the one by Serenity 以及this one by ImportanceOfBeingErnest。

自定义时间序列刻度标签的最佳方法是使用来自 matplotlib.dates 模块 (mdates) 的刻度定位器和格式化程序。尽管值得注意的是,如果您想要基于与您正在绘制的时间序列相同的单位的刻度频率,使用日期作为字符串创建刻度标签并设置格式可能更方便,就像this question concerning pandas bar plots 的答案一样.

如in the documentation 所述,pandas 使用 matplotlib 为时间序列创建带有自己的 custom tick formatters 的绘图:

pandas 为时间序列图提供自定义格式化程序。这些更改日期和时间轴标签的格式。默认情况下,自定义格式化程序仅适用于 pandas 使用 DataFrame.plot() 或 Series.plot() 创建的图。

pandas 时间序列图的刻度和标签目前默认格式如下:

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));

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

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

Matplotlib 使用与 pandas since version 3.3.0 released in July 2020 相同的默认来源,但日期始终为 numbered in terms of days:

Matplotlib 使用浮点数表示日期,指定自默认纪元 1970-01-01 UTC 以来的天数;例如,1970-01-01, 06:00 是浮点数 0.25。

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

正如您可能已经猜到的那样,这意味着当时间序列的频率以天为单位时,不需要进行日期转换,如此处所示,使用简单的自定义刻度定位器和格式化程序:

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\n%Y')
ax.xaxis.set_major_formatter(fmt)

这种特殊情况可以方便地为其他 pandas 保留 x 轴限制和次要 x 刻度的默认设置。但这是一般规则的例外。

为了能够将 mdates 刻度定位器和格式化程序与任何类型频率的时间序列的 pandas 图一起使用,您需要使用 (long-existing 和 absent-from-the-docstring 和 barely-documented) x_compat=True 参数。以下示例说明了它与按月重采样的相同数据集的使用情况。通常情况下,您可能只是想稍微调整一下默认的 pandas 格式,因此在下面的示例中,从头开始重新创建默认格式以显示可以使用哪些方法来调整它:

# 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\n%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());


文档:pd.date_range、date format codes、mdates.ConciseDateFormatterfig.autofmt_xdate

【讨论】:

以上是关于如何更改 matplotlib 图的日期时间刻度标签频率?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 matplotlib 中轴对象刻度的字体大小 [重复]

如何在 matplotlib 中更改图例字体名称

如何更改 Matplotlib 中颜色条刻度标签的字体大小?

删除 Matplotlib,Python 中某些子图的 y 轴刻度标签

如何更改熊猫时间序列图的 x 刻度密度?

matplotlib 中的日期刻度和旋转