matplotlib datetime xlabel问题 [英] matplotlib datetime xlabel issue

查看:170
本文介绍了matplotlib datetime xlabel问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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年),每次旅行bug所需的天数大约为140(在这种情况下为136/137)。任何人都知道这里发生了什么这是一个已知的错误,如果是,是否有解决方法?



几个注释:在上述命令中,我使用--pylab模式下的IPython创建绘图,但是我首先使用matplotlib直接遇到这个问题,并且可以以脚本形式重现(即我不认为这是IPython问题)。此外,我已经在matplotlib 1.1.0和1.2.X中观察到了这一点。



更新:



看起来有一个窗口,如果你在前面推得足够远,那么标签将再次起作用。对于上述示例,标签从5月18日至5月31日保持乱码,但在6月1日,标签开始正常绘制。因此,

 (标签为乱码)
plot([dt(2013,1,1),dt ,5,31)],[1,1],linestyle ='无',marker ='。')

(标签很好)
plot([dt(2013,1 ,1),dt(2013,6,1)],[1,1],linestyle ='无',marker ='。')


解决方案

它引发了 AutoDateLocator 中的错误。似乎这个bug还没有被报告给问题跟踪器。_
看起来很奇怪,只因为太多的标签和蜱被绘制了。



使用日期绘制数据时,默认情况下,matplotlib使用 matplotlib.dates.AutoDateLocator 作为主要的定位器。即, AutoDateLocator 用于确定刻度间隔和刻度位置。



假设数据序列由 [datetime(2013,1,1),datetime(2013,5,18)]

时差是4个月和17天。月份增量为4,日差值为4 * 31 + 17 = 141。



根据matplotlib docs


class matplotlib.dates.AutoDateLocator(tz =没有,minticks = 5,maxticks = None,interval_multiples = False)


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



maxticks是所需的最大刻度数,它控制刻度之间的任何间隔(每隔一秒滴答一次,每3等)。对于真正的细粒度控制,这可以是将个体rrule频率常数(YEARLY,MONTHLY等)映射到自己的最大刻度数的字典。这可以用于保持与类中所选格式相匹配的刻度数:AutoDateFormatter。该字典中没有指定的任何频率均被赋予默认值。



AutoDateLocator具有一个间隔字典,用于映射tick的频率(来自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]
} / code>

间隔用于指定适合滴答频率的倍数。例如,每7天对于每日滴答敏感,但是分钟/秒,15或30是有意义的。您可以通过执行以下操作来自定义此字典:



由于月份增量为4,小于5,日期三角洲是141,不小于5.滴答的类型将是每天。

解决滴答的类型后, AutoDateLocator 将使用间隔字典和maxticks字典来确定刻度间隔。



maxticks AutoDateLocator 使用其默认maxticks字典。
文档显示了我们默认的间隔字典,并没有告诉我们默认的maxticks字典是什么样子。_
我们可以在 dates.py

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

确定刻度间隔的算法

 #查找第一个可用的间隔在self.intervald [freq]中没有给出太多的ticks 
,如果num <= interval *(self.maxticks [freq] - 1):
break
else:
#我们经历了整个循环而不中断,默认为1
interval = 1

现在,滴答的类型是 DAILY 。所以 freq DAILY num 是141,当天三角洲。上述代码将相当于[1,2,3,7,14]中间隔的

 
如果141 <=间隔*(11-1):
break
else:
interval = 1

141太大了。每一天的间隔时间都会太多。将执行 else 子句,并将刻度间隔设置为1.。
这意味着140个标签和刻度将被绘制。如果数据序列由 [datetime(2013,1,1)),datetime(2013)提供,那么我们可以期待一个丑陋的x轴。



,5,17)] ,只有一天更短。日三角洲是140.
然后 AutoDateLocator 将选择14作为刻度间隔,只绘制10个标签。
因此,您的第一张图表看起来很好。



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



结论: / strong>

给定范围大于或等于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()


I'm seeing some strange behavior in the x-axis auto-labeling for dates in matplotlib. When I issue the command:

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

I get the very reasonably labeled chart:

But if I increase the end date by 1 day:

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

I get this:

I've reproduced this at several different calendar date ranges(in 2012), and each time the magic number of days required to trip the bug is around 140 (in this case 136/137). Anyone know what's going on here? Is this a known bug, and if so, is there a workaround?

A couple notes: In the above commands, I'm using IPython in --pylab mode to create the plots, but I first encountered this issue using matplotlib directly, and it is reproducible in script form (i.e. I don't think this is an IPython issue). Also, I've observed this in both matplotlib 1.1.0 and 1.2.X.

UPDATE:

It looks like there is a window where, if you push far enough ahead, the labels start behaving normally again. For the example above, the labels remain garbled from May 18 through May 31, but on June 1, the labels start plotting normally again. So,

(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='.')

解决方案

It cauesed by a bug in AutoDateLocator. It seems this bug has not been reported to the issue tracker yet.
It looks weird only because too many labels and ticks have been plotted.

When plotting with data with dates, by default, matplotlib uses matplotlib.dates.AutoDateLocator as the major locator. Namely, AutoDateLocator is used to determine the tick interval and tick locations.

Suppose, the data sequence is given by [datetime(2013, 1, 1), datetime(2013, 5, 18)].
The time delta is 4 months and 17 days. The month delta is 4, and the day delta is 4*31+17=141.

According to matplotlib docs:

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

minticks is the minimum number of ticks desired, which is used to select the type of ticking (yearly, monthly, etc.).

maxticks is the maximum number of ticks desired, which controls any interval between ticks (ticking every other, every 3, etc.). For really fine-grained control, this can be a dictionary mapping individual rrule frequency constants (YEARLY, MONTHLY, etc.) to their own maximum number of ticks. This can be used to keep the number of ticks appropriate to the format chosen in class:AutoDateFormatter. Any frequency not specified in this dictionary is given a default value.

The AutoDateLocator has an interval dictionary that maps the frequency of the tick (a constant from dateutil.rrule) and a multiple allowed for that ticking. The default looks like this:

    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]
      }

The interval is used to specify multiples that are appropriate for the frequency of ticking. For instance, every 7 days is sensible for daily ticks, but for minutes/seconds, 15 or 30 make sense. You can customize this dictionary by doing:

Since the month delta is 4, less than 5, and the day delta is 141, not less than 5. The type of ticking will be daily.
After resolving the type of ticking, AutoDateLocator will use the interval dictionary and maxticks dictionary to determine the tick interval.

When maxticks is None, AutoDateLocator uses its default maxticks dictionary. The documentation shows us the default interval dictionary, and does not tell us what the default maxticks dictionary looks like.
We can find it in the dates.py.

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

The algorithm to determine the tick interval is

# 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

The type of ticking is DAILY now. So freq is DAILY and num is 141, the day delta. The above code will be equivalent to

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

141 is too large. All daily intervals will give too many ticks. else clause will be executed and the tick interval will be set to 1.
It means 140+ labels and ticks would beplotted. We can expect an ugly x-axis.

If the data sequence is given by [datetime(2013, 1, 1), datetime(2013, 5, 17)], just one day shorter. the day delta is 140. Then AutoDateLocator will choose 14 as the tick interval and only 10 labels will be plotted. Thus your first graph looks fine.

Actually I don't understand why matplotlib choose to set the interval to 1 if maxticks constraint cannot be satisfied. It will only result in a much larger number of ticks if the interval is 1. I prefer to use the longest interval.

CONCLUSION:
Given any date sequence whose range is greater than or equal to 4 months and 18 days, and less than 5 months, AutoDateLocator will choose 1 as the tick interval. You will see some ugly behavior in the x-axis or y-axis when plotting such date sequence with the default major locator, namely, AutoDateLocator.

SOLUTION:
The simplest solution is increasing the daily maxticks to 12. For example:

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 datetime xlabel问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆