首页 > 解决方案 > Matplotlib - 在保持正确 x 值的同时跳过 xticks

问题描述

我试图从两个熊猫数据框中绘制两个独立的东西,但 x 轴给出了一些问题。使用 matplotlib.ticker 跳过 x-ticks 时,不会跳过日期。结果是 x 轴值与绘制的不匹配。

例如,当 x-ticks 设置为以 2 为底时,您会看到日期增加了 1。

2的基础

但是当基数设置为 4 时,图形具有相同的间距,您可以在此处看到:

4的基础

对于第二张图片,目标是每个刻度增加 4 天,因此它应该是 22、26、30 等。

这是我正在使用的代码:

ax = plot2[['Date','change value']].plot(x='Date',color='red',alpha=1,linewidth=1.5)
plt.ylabel('Total Change')
plot_df[['Date','share change daily']].plot(x='Date',secondary_y=True,kind='bar',ax=ax,alpha=0.4,color='black',figsize=(6,2),label='Daily Change')
plt.ylabel('Daily Change')
ax.legend(['Total Change (L)','Daily Change'])
plt.xticks(plot_df.index,plot_df['Date'].values)

myLocator = mticker.MultipleLocator(base=4)
ax.xaxis.set_major_locator(myLocator)

任何帮助表示赞赏!谢谢 :)

标签: pythonpandasmatplotlib

解决方案


首先,我建议您将日期设置为数据框的索引。这让 pandas 在您创建折线图时自动很好地格式化日期标签,并让您可以方便地使用该strftime方法创建自定义格式。

第二点与此示例相关,因为在线图上绘制条形图会阻止您获取熊猫线图日期标签,因为 x 轴单位从 0 开始切换为整数单位(请注意,情况也是如此当您将日期用作字符串而不是datetime对象时,即timestamppandas 中的对象)。您可以通过ax.get_xticks()在创建线图(使用 DatetimeIndex)后运行并在创建条形图后再次运行来检查这一点。

关于刻度定位器和格式化程序、pandas 绘图默认值以及您可以定义自定义刻度和刻度标签的各种方式有太多特殊性,我在这里更详细地介绍。因此,我建议您参考文档以获取更多信息(尽管对于您的情况,您实际上并不需要任何这些):主要和次要刻度日期刻度标签时间序列的自定义刻度格式化程序使用刻度的更多示例,以及ticker 模块,其中包含刻度定位器和格式化程序及其参数的列表。

ax.get_xaxis().get_major_locator()此外,您可以使用or标识绘图函数使用的默认刻度定位器和格式化程序ax.get_xaxis().get_major_formatter()(您可以对 y 轴和次要刻度执行相同的操作),以了解幕后发生的事情。

继续解决您的问题。鉴于您想要预定义日期范围内的固定频率,我建议您避免明确选择代码定位器和格式化程序,而只需创建所需的刻度和刻度标签列表。首先,这里有一些与您类似的示例数据:

import numpy as np                 # v 1.19.2
import pandas as pd                # v 1.1.3
import matplotlib.pyplot as plt    # v 3.3.2

rng = np.random.default_rng(seed=1) # random number generator

dti = pd.bdate_range(start='2020-07-22', end='2020-09-03')
daily = rng.normal(loc=0, scale=250, size=dti.size)
total = -1900 + np.cumsum(daily)

df = pd.DataFrame({'Daily Change': daily,
                   'Total Change': total},
                  index=dti)
df.head()
            Daily Change  Total Change
2020-07-22     86.396048  -1813.603952
2020-07-23    205.404536  -1608.199416
2020-07-24     82.609269  -1525.590147
2020-07-27   -325.789308  -1851.379455
2020-07-28    226.338967  -1625.040488

日期设置为索引,这将简化创建绘图的代码(无需指定x)。我使用与您给出的示例相同的格式参数,除了图形大小。请注意,我不使用设置刻度和刻度标签,plt.xticks因为它指的是包含条形图的辅助轴,并且由于某种原因,rotationha参数被忽略。

label_daily, label_total = df.columns

# Create pandas line plot: note the 'use_index' parameter
ax = df.plot(y=label_total, color='red', alpha=1, linewidth=1.5,
             use_index=False, ylabel=label_total)

# Create pandas bar plot: note that the second ylabel must be created
# after, else it overwrites the previous label on the left
df.plot(kind='bar', y=label_daily, color='black', alpha=0.4,
        ax=ax, secondary_y=True, mark_right=False, figsize=(9, 4))
plt.ylabel(label_daily, labelpad=10)

# Place legend in a better location: note that because there are two
# Axes, the combined legend can only be edited with the fig.legend
# method, and the ax legend must be removed
ax.legend().remove()
plt.gcf().legend(loc=(0.11, 0.15))

# Create custom x ticks and tick labels
freq = 4 # business days
xticks = ax.get_xticks()
xticklabels = df.index[::freq].strftime('%b-%d')
ax.set_xticks(xticks[::freq])
ax.set_xticks(xticks, minor=True)
ax.set_xticklabels(xticklabels, rotation=0, ha='center')

plt.show()

pandas_twinax_line_bar


可以在这里找到格式化日期的代码。


为了完整起见,这里有两种创建完全相同的刻度的替代方法,但这次是通过显式使用 matplotlib 刻度定位器和格式化程序。

第一个替代方法像以前一样使用刻度和刻度标签列表,但这次将它们传递给FixedLocatorand FixedFormatter

import matplotlib.ticker as mticker

# Create custom x ticks and tick labels
freq = 4 # business days
maj_locator = mticker.FixedLocator(ax.get_xticks()[::freq])
min_locator = mticker.FixedLocator(ax.get_xticks())
ax.xaxis.set_major_locator(maj_locator)
ax.xaxis.set_minor_locator(min_locator)

maj_formatter = mticker.FixedFormatter(df.index[maj_locator.locs].strftime('%b-%d'))
ax.xaxis.set_major_formatter(maj_formatter)
plt.setp(ax.get_xticklabels(), rotation=0, ha='center')

第二种选择使用选项在使用 时在索引的每个第 n 个位置创建一个刻度IndexLocator,并将其与FuncFormatter(而不是IndexFormatter不推荐使用的)组合:

import matplotlib.ticker as mticker

# Create custom x ticks and tick labels
maj_freq = 4 # business days
min_freq = 1 # business days
maj_locator = mticker.IndexLocator(maj_freq, 0)
min_locator = mticker.IndexLocator(min_freq, 0)
ax.xaxis.set_major_locator(maj_locator)
ax.xaxis.set_minor_locator(min_locator)

maj_formatter = mticker.FuncFormatter(lambda x, pos=None:
                                      df.index[int(x)].strftime('%b-%d'))
ax.xaxis.set_major_formatter(maj_formatter)
plt.setp(ax.get_xticklabels(), rotation=0, ha='center')

如您所见,这两种替代方法都比最初的示例更冗长。


推荐阅读