【问题标题】:matplotlib datetime xlabel issuematplotlib datetime xlabel问题
【发布时间】:2013-02-16 09:17:19
【问题描述】:

我在 matplotlib 中的日期的 x 轴自动标记中看到一些奇怪的行为。当我发出命令时:

from datetime import datetime as dt
plot( [ dt(2013, 1, 1), dt(2013, 5, 17)], [ 1 , 1 ], linestyle='None', marker='.')

我得到了非常合理的标记图表:

但如果我将结束日期增加 1 天:

plot( [ dt(2013, 1, 1), dt(2013, 5, 18)], [ 1 , 1 ], linestyle='None', marker='.')

我明白了:

我已经在几个不同的日历日期范围(2012 年)复制了这个,每次触发错误所需的神奇天数大约是 140 天(在本例中为 136/137)。有人知道这里发生了什么吗?这是一个已知的错误吗?如果是,是否有解决方法?

几个注意事项:在上面的命令中,我在 --pylab 模式下使用 IPython 来创建绘图,但我第一次遇到这个问题是直接使用 matplotlib 的,它可以以脚本形式重现(即我没有认为这是一个 IPython 问题)。另外,我在 matplotlib 1.1.0 和 1.2.X 中都观察到了这一点。

更新:

看起来有一个窗口,如果您向前推得足够远,标签会再次开始正常运行。对于上面的示例,从 5 月 18 日到 5 月 31 日,标签仍然是乱码,但在 6 月 1 日,标签又开始正常绘图。所以,

(labels are garbled)
plot( [ dt(2013, 1, 1), dt(2013, 5, 31)], [ 1 , 1 ], linestyle='None', marker='.')

(labels are fine)
plot( [ dt(2013, 1, 1), dt(2013, 6, 1)], [ 1 , 1 ], linestyle='None', marker='.')

【问题讨论】:

    标签: python datetime matplotlib plot


    【解决方案1】:

    您需要将 locator.MAXTICKS 重置为更大的数字以避免错误:超过 Locator.MAXTICKS * 2 (2000)

    例如:

        alldays = DayLocator()              # minor ticks on the days
        alldays.MAXTICKS = 2000
    

    【讨论】:

      【解决方案2】:

      它是由 AutoDateLocator 中的错误引起的。似乎此错误尚未报告给问题跟踪器。
      它看起来很奇怪,只是因为绘制了太多的标签和刻度。

      当使用带有日期的数据进行绘图时,默认情况下,matplotlib 使用 matplotlib.dates.AutoDateLocator 作为主要定位符。即AutoDateLocator用于确定tick间隔和tick位置。

      假设,数据序列由[datetime(2013, 1, 1), datetime(2013, 5, 18)]给出。
      时间增量为 4 个月零 17 天。月 delta 为 4,日 delta 为 4*31+17=141。

      根据matplotlib docs

      类 matplotlib.dates.AutoDateLocator(tz=None, minticks=5, maxticks=None, interval_multiples=False)

      minticks 是所需的最小刻度数,用于选择刻度类型(每年、每月等)。

      maxticks 是所需的最大刻度数,它控制刻度之间的任何间隔(每隔一个刻度,每 3 个刻度等)。对于真正细粒度的控制,这可以是一个字典,将单个规则频率常数(每年、每月等)映射到它们自己的最大刻度数。这可用于保持刻度数适合于在 class:AutoDateFormatter 中选择的格式。任何未在本词典中指定的频率都会被赋予一个默认值。

      AutoDateLocator 有一个间隔字典,用于映射滴答的频率(来自 dateutil.rrule 的常量)和允许该滴答的倍数。默认如下所示:

          self.intervald = {
            YEARLY  : [1, 2, 4, 5, 10],
            MONTHLY : [1, 2, 3, 4, 6],
            DAILY   : [1, 2, 3, 7, 14],
            HOURLY  : [1, 2, 3, 4, 6, 12],
            MINUTELY: [1, 5, 10, 15, 30],
            SECONDLY: [1, 5, 10, 15, 30]
            }

      间隔用于指定适合滴答频率的倍数。例如,对于每日刻度,每 7 天是合理的,但对于分钟/秒,15 或 30 是有意义的。您可以通过以下方式自定义此词典:

      由于月份 delta 为 4,小于 5,而日 delta 为 141,不小于 5。tick 的类型将是每天。
      在解析了tick的类型后,AutoDateLocator会使用interval字典和maxticks字典来确定tick的间隔。

      maxticksNone 时,AutoDateLocator 使用其默认的 maxticks 字典。 文档向我们展示了默认的区间字典,并没有告诉我们默认的 maxticks 字典是什么样的。
      我们可以在dates.py找到它。

      self.maxticks = {YEARLY : 16, MONTHLY : 12, DAILY : 11, HOURLY : 16,
                  MINUTELY : 11, SECONDLY : 11}
      

      The algorithm to determine the tick interval

      # Find the first available interval that doesn't give too many ticks
      for interval in self.intervald[freq]:
          if num <= interval * (self.maxticks[freq] - 1):
              break
      else:
          # We went through the whole loop without breaking, default to 1
          interval = 1
      

      现在的类型是DAILY。所以freqDAILYnum 是141,日增量。上面的代码相当于

      for interval in [1, 2, 3, 7, 14]:
          if 141 <= interval * (11 - 1):
              break
      else:
          interval = 1
      

      141 太大。所有的每日间隔都会给出太多的滴答声。 else 子句将被执行,并且刻度间隔将设置为 1。
      这意味着将绘制 140 多个标签和刻度。我们可以期待一个丑陋的 x 轴。

      如果数据序列由[datetime(2013, 1, 1), datetime(2013, 5, 17)] 给出,则仅短一天。日差为 140。 然后AutoDateLocator 将选择 14 作为刻度间隔,并且只会绘制 10 个标签。 因此,您的第一个图表看起来不错。

      其实我不明白如果maxticks约束不能满足,matplotlib为什么选择将区间设置为1。 如果间隔为 1,它只会导致更多的滴答声。我更喜欢使用最长的间隔。

      结论:
      给定范围大于或等于 4 个月 18 天且小于 5 个月的任何日期序列,AutoDateLocator 将选择 1 作为刻度间隔。 当使用默认的主要定位器(即AutoDateLocator)绘制此类日期序列时,您会在 x 轴或 y 轴上看到一些丑陋的行为。

      解决方案:
      最简单的解决方案是将每日 maxticks 增加到 12。 例如:

      import numpy as np
      import matplotlib.pyplot as plt
      from matplotlib.dates import DAILY
      from datetime import datetime
      
      ax = plt.subplot(111)
      plt.plot_date([datetime(2013, 1, 1), datetime(2013, 5, 31)],
                    [datetime(2013, 1, 1), datetime(2013, 5, 10)])
      
      loc = ax.xaxis.get_major_locator()
      loc.maxticks[DAILY] = 12
      
      plt.show()
      

      【讨论】:

      • 太好了,感谢您的解释!您是否将此(连同您的解决方案)报告给 matplotlib 邮件列表?如果没有,我会这样做,并确保将分析归功于您。
      • @jeremiahbuddha:我没有举报。
      • 作为记录,我很确定我在 matplotlib (v1.4.1) 的最后一个版本中修复了这个问题。
      【解决方案3】:

      感觉确实像个错误;我会把它带到 matplotlib 邮件列表,看看那里的人能说些什么。

      我可以提供的一种解决方法如下:

      from datetime import datetime as dt
      from matplotlib import pylab as pl
      
      fig = pl.figure()
      axes = fig.add_subplot(111)
      axes.plot( [ dt(2013, 1, 1), dt(2013, 5, 18)], [ 1 , 1 ], linestyle='None', marker='.')
      ticks = axes.get_xticks()
      n = len(ticks)//6
      axes.set_xticks(ticks[::n])
      fig.savefig('dateticks.png')
      

      为 OO 方法道歉(这不是您所做的),但这使得将刻度与情节联系起来变得容易得多。 数字 6 只是我想要沿 x 轴的标签数,然后我将 matplotlib 得出的实际刻度数减少为计算得出的 n

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-08
        • 2018-01-19
        • 2013-04-15
        • 2018-05-22
        • 1970-01-01
        • 1970-01-01
        • 2020-02-16
        • 1970-01-01
        相关资源
        最近更新 更多