Matplotlib 显示重叠的 x-tick 标签

Posted

技术标签:

【中文标题】Matplotlib 显示重叠的 x-tick 标签【英文标题】:Matplotlib showing x-tick labels overlapping 【发布时间】:2014-12-29 07:17:38 【问题描述】:

请看下图:

这是这个大图的子图:

我发现它有两个问题。首先,x 轴标签相互重叠(这是我的主要问题)。第二。 x 轴次要网格线的位置似乎有点不稳定。在图表的左侧,它们看起来间隔适当。但在右侧,它们似乎挤满了主要网格线......好像主要网格线位置不是次要刻度线位置的正确倍数。

我的设置是我有一个名为df 的DataFrame,它的行上有一个DatetimeIndex,一个名为value 的列包含浮点数。如有必要,我可以在要点中提供df 内容的示例。文章底部有十几行df,供参考。

这是生成图形的代码:

now = dt.datetime.now()

fig, axes = plt.subplots(2, 2, figsize=(15, 8), dpi=200)
for i, d in enumerate([360, 30, 7, 1]):
    ax = axes.flatten()[i]
    earlycut = now - relativedelta(days=d)
    data = df.loc[df.index>=earlycut, :]
    ax.plot(data.index, data['value'])
    ax.xaxis_date()

    ax.get_xaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())
    ax.get_yaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())

    ax.grid(b=True, which='major', color='w', linewidth=1.5)
    ax.grid(b=True, which='minor', color='w', linewidth=0.75)

让 x 轴标签停止相互重叠(在四个子图中)的最佳选择是什么?另外,单独(但不那么紧急),左上角子图中的小勾号问题是怎么回事?

我使用的是 Pandas 0.13.1、numpy 1.8.0 和 matplotlib 1.4.x。

这是df的小sn-p供参考:

                                    id scale  tempseries_id    value
timestamp                                                           
2014-11-02 14:45:10.302204+00:00  7564     F              1  68.0000
2014-11-02 14:25:13.532391+00:00  7563     F              1  68.5616
2014-11-02 14:15:12.102229+00:00  7562     F              1  68.9000
2014-11-02 14:05:13.252371+00:00  7561     F              1  69.0116
2014-11-02 13:55:11.792191+00:00  7560     F              1  68.7866
2014-11-02 13:45:10.782227+00:00  7559     F              1  68.6750
2014-11-02 13:35:10.972248+00:00  7558     F              1  68.4500
2014-11-02 13:25:10.362213+00:00  7557     F              1  68.1116
2014-11-02 13:15:10.822247+00:00  7556     F              1  68.2250
2014-11-02 13:05:10.102200+00:00  7555     F              1  68.5616
2014-11-02 12:55:10.292217+00:00  7554     F              1  69.0116
2014-11-02 12:45:10.382226+00:00  7553     F              1  69.3500
2014-11-02 12:35:10.642245+00:00  7552     F              1  69.2366
2014-11-02 12:25:12.642255+00:00  7551     F              1  69.1250
2014-11-02 12:15:11.122382+00:00  7550     F              1  68.7866
2014-11-02 12:05:11.332224+00:00  7549     F              1  68.5616
2014-11-02 11:55:11.662311+00:00  7548     F              1  68.2250
2014-11-02 11:45:11.122193+00:00  7547     F              1  68.4500
2014-11-02 11:35:11.162271+00:00  7546     F              1  68.7866
2014-11-02 11:25:12.102211+00:00  7545     F              1  69.2366
2014-11-02 11:15:10.422226+00:00  7544     F              1  69.4616
2014-11-02 11:05:11.412216+00:00  7543     F              1  69.3500
2014-11-02 10:55:10.772212+00:00  7542     F              1  69.1250
2014-11-02 10:45:11.332220+00:00  7541     F              1  68.7866
2014-11-02 10:35:11.332232+00:00  7540     F              1  68.5616
2014-11-02 10:25:11.202411+00:00  7539     F              1  68.2250
2014-11-02 10:15:11.932326+00:00  7538     F              1  68.5616
2014-11-02 10:05:10.922229+00:00  7537     F              1  68.9000
2014-11-02 09:55:11.602357+00:00  7536     F              1  69.3500

编辑:尝试fig.autofmt_xdate(): 我不认为这会奏效。这似乎对左侧的两个图和右侧的两个图都使用了相同的 x-tick 标签。鉴于我的数据,这是不正确的。请看下面有问题的输出:

【问题讨论】:

【参考方案1】:

好的,终于搞定了。诀窍是使用plt.setp 手动旋转刻度标签。使用 fig.autofmt_xdate() 不起作用,因为当您的图中有多个子图时,它会做一些意想不到的事情。这是带有输出的工作代码:

for i, d in enumerate([360, 30, 7, 1]):
    ax = axes.flatten()[i]
    earlycut = now - relativedelta(days=d)
    data = df.loc[df.index>=earlycut, :]
    ax.plot(data.index, data['value'])

    ax.get_xaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())
    ax.get_yaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())

    ax.grid(b=True, which='major', color='w', linewidth=1.5)
    ax.grid(b=True, which='minor', color='w', linewidth=0.75)

    plt.setp(ax.get_xticklabels(), rotation=30, horizontalalignment='right')

fig.tight_layout()

顺便说一句,前面关于 matplotlib 的一些东西永远占用的评论在这里非常有趣。我正在使用树莓派作为远程位置的气象站。它正在收集数据并通过网络提供结果。天啊,天啊,想把这些图形放出来真的让人喘不过气来。

【讨论】:

对我来说,我只需要plt.setp(ax.get_xticklabels(), rotation=30, horizontalalignment='right', fontsize='x-small')。伟大的工作顺便说一句。谢谢!【参考方案2】:

由于 matplotlib 中处理文本渲染的方式,自动检测重叠文本确实会减慢速度。 (文本占用的空间直到绘制完成后才能准确计算。)因此,matplotlib 不会尝试自动执行此操作。

因此,最好旋转长刻度标签。因为日期最常出现这个问题,所以有一个图形方法fig.autofmt_xdate() 将(除其他外)旋转刻度标签以使其更具可读性。 (注意:如果你使用的是 pandas 的 plot 方法,它会返回一个轴对象,所以你需要使用 ax.figure.autofmt_xdate()。)

举个简单的例子:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

time = pd.date_range('01/01/2014', '4/01/2014', freq='H')
values = np.random.normal(0, 1, time.size).cumsum()

fig, ax = plt.subplots()
ax.plot_date(time, values, marker='', linestyle='-')

fig.autofmt_xdate()
plt.show()

如果我们要离开fig.autofmt_xdate()

如果我们使用fig.autofmt_xdate():

【讨论】:

此解决方案是否适用于所有具有不同日期时间轴的多个子图(例如 2x2 数组)? (我尝试实现此解决方案,但它仅在子图的底行显示刻度标签,并且与子图的顶行不正确对应。)【参考方案3】:

对于在 x 轴上没有日期值而不是字符串的问题,您可以在 x 轴值中插入 \n 字符,这样它们就不会重叠。这是一个例子-

数据框是

somecol               value
category 1 of column   16
category 2 of column   13
category 3 of column   21
category 4 of column   20
category 5 of column   11
category 6 of column   22
category 7 of column   19
category 8 of column   14
category 9 of column   18
category 10 of column   23
category 11 of column   10
category 12 of column   24
category 13 of column   17
category 14 of column   15
category 15 of column   12

我需要在 y 轴上绘制 value,在 x 轴上绘制 somecol,通常会这样绘制 -

如您所见,有很多重叠之处。现在在 somecol 列中引入\n 字符。

somecol = df['somecol'].values.tolist()
for i in range(len(somecol)):   
    x = somecol[i].split(' ')   
    # insert \n before 'of'     
    x.insert(x.index('of'),'\n')
    somecol[i] = ' '.join(x) 

现在,如果你绘制,它会看起来像这样 -

plt.plot(somecol, df['val'])

如果您不想旋转标签,此方法效果很好。

到目前为止,我在此方法中发现的唯一缺点是您需要调整标签 3-4 次,即尝试使用多种格式以最佳格式显示图。

【讨论】:

以上是关于Matplotlib 显示重叠的 x-tick 标签的主要内容,如果未能解决你的问题,请参考以下文章

Matplotlib条形图仅在非零条形图处显示x-ticks

Matplotlib 在所有子图上显示 x-ticks 和唯一的 y 标签

PyQt5嵌入matplotlib-plot,改变x-ticks等属性[重复]

感谢matplotlib,如何在同一图中显示具有重叠数据的多个图形?

为啥 Matplotlib savefig 图像重叠?

matplotlib 中具有重叠点的散点图的可视化