matplotlib/pandas 中日期的内部表示不一致

Inconsistent internal representation of dates in matplotlib/pandas

import pandas as pd

index = pd.to_datetime(['2016-05-01', '2016-11-01', '2017-05-02'])
data = pd.DataFrame({'a': [1, 2, 3],
                     'b': [4, 5, 6]}, index=index)
ax = data.plot()
print(ax.get_xlim())

# Out: (736066.7, 736469.3)

现在,如果我们更改最后日期。

index = pd.to_datetime(['2016-05-01', '2016-11-01', '2017-05-01'])
data = pd.DataFrame({'a': [1, 2, 3],
                     'b': [4, 5, 6]}, index=index)
ax = data.plot()
print(ax.get_xlim())

# Out: (184.8, 189.2)

第一个例子似乎与matplotlib docs一致:

Matplotlib represents dates using floating point numbers specifying the number of days since 0001-01-01 UTC, plus 1

为什么第二个例子 return 看起来完全不同?我正在使用 pandas 版本 0.22.0 和 matplotlib 版本 2.2.2.

在第二个示例中,如果您查看图表,matplotlib 不会给出日期,而是给出季度值:

本例中的日期正好是六个月,因此相隔两个季度,这大概就是您看到此行为的原因。虽然我无法在文档中找到它,但 xlim 在这种情况下给出的数字与自 Unix 纪元(1970 年 1 月 1 日)以来的季度数一致。

Pandas使用不同的单位来表示坐标轴上的日期和时间,这取决于dates/times使用的范围。这意味着正在使用不同的定位器。

第一种情况,

print(ax.xaxis.get_major_locator())
# Out: pandas.plotting._converter.PandasAutoDateLocator

第二种情况

print(ax.xaxis.get_major_locator())
# pandas.plotting._converter.TimeSeries_DateLocator

您可以使用 x_compat 参数强制 pandas 始终使用 PandasAutoDateLocator

df.plot(x_compat=True)

这将确保始终获得相同的日期时间定义,与 matplotlib.dates 约定一致。

缺点是这会删除不错的季度更新

并将其替换为标准滴答

另一方面,它将允许使用非常可定制的 matplotlib.dates 代码和格式化程序。例如获取季度 ticks/labels

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as mticker
import pandas as pd

index = pd.to_datetime(['2016-05-01', '2016-11-01', '2017-05-01'])
data = pd.DataFrame({'a': [1, 2, 3],
                     'b': [4, 5, 6]}, index=index)
ax = data.plot(x_compat=True)

# Quarterly ticks
ax.xaxis.set_major_locator(mdates.MonthLocator((1,4,7,10)))

# Formatting:
def func(x,pos):
    q = (mdates.num2date(x).month-1)//3+1
    tx = "Q{}".format(q)
    if q == 1:
        tx += "\n{}".format(mdates.num2date(x).year)
    return tx
ax.xaxis.set_major_formatter(mticker.FuncFormatter(func))
plt.setp(ax.get_xticklabels(), rotation=0, ha="center")

plt.show()