我在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='.')
答案 0 :(得分:7)
它被AutoDateLocator
中的错误所左右。似乎尚未向问题跟踪器报告此错误。
它看起来很奇怪,因为已经绘制了太多的标签和刻度。
使用日期数据绘图时,默认情况下,matplotlib使用matplotlib.dates.AutoDateLocator
作为主要定位器。即,AutoDateLocator
用于确定刻度间隔和刻度位置。
假设数据序列由[datetime(2013, 1, 1), datetime(2013, 5, 18)]
给出
时间差为4个月和17天。月份增量为4,日期增量为4 * 31 + 17 = 141。
class matplotlib.dates.AutoDateLocator(tz = None,minticks = 5,maxticks = None,interval_multiples = False)
minticks是所需的最小滴答数,用于选择滴答类型(年,月等)。
maxticks是所需的最大滴答数,它控制滴答之间的任何间隔(每隔一个滴答,每隔3滴等)。对于非常细粒度的控制,这可以是将各个rrule频率常数(YEARLY,MONTHLY等)映射到它们自己的最大tick数的字典。这可以用于保持适合在类中选择的格式的刻度数: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是有意义的。您可以通过执行以下操作来自定义此词典:
由于月份增量为4,小于5,日期增量为141,不小于5.滴答类型为每日。
在解析滴答类型后,AutoDateLocator
将使用区间字典和maxticks字典来确定滴答间隔。
当maxticks
为None
时,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
。因此freq
为DAILY
而num
为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作为刻度间隔。
当使用默认主要定位器绘制此类日期序列时,您会在x轴或y轴上看到一些丑陋的行为,即AutoDateLocator
。
<强> SOLUTION:强>
最简单的解决方案是将每日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()
答案 1 :(得分:2)
它确实感觉像一个bug;我会把它带到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轴的标签数量,然后我通过计算的n
减少了matplotlib提出的实际滴答数。
答案 2 :(得分:0)
您需要将locator.MAXTICKS重置为更大的数字以避免错误:超过Locator.MAXTICKS * 2(2000)
例如:
alldays = DayLocator() # minor ticks on the days
alldays.MAXTICKS = 2000