matplotlib中带滑块小部件的日期时间

时间:2015-06-24 00:38:36

标签: python matplotlib

我希望能够滚动表示日期/时间的x(水平)线。为此我正在使用slider widget。问题是,当我滚动太远(没有y值存在)时,我不再能够使用滑块小部件提供的滚动功能,并且整个绘制的线条消失并且仍然只是空图。我在终端上遇到的错误如下:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1535, in __call__
    return self.func(*args)
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 586, in callit
    func(*args)
  File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_tkagg.py", line 365, in idle_draw
    self.draw()
  File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_tkagg.py", line 349, in draw
    FigureCanvasAgg.draw(self)
  File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_agg.py", line 469, in draw
    self.figure.draw(self.renderer)
  File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 59, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1079, in draw
    func(*args)
  File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 59, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/axes/_base.py", line 2092, in draw
    a.draw(renderer)
  File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 59, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/axis.py", line 1114, in draw
    ticks_to_draw = self._update_ticks(renderer)
  File "/usr/lib/python2.7/dist-packages/matplotlib/axis.py", line 957, in _update_ticks
    tick_tups = [t for t in self.iter_ticks()]
  File "/usr/lib/python2.7/dist-packages/matplotlib/axis.py", line 901, in iter_ticks
    majorLocs = self.major.locator()
  File "/usr/lib/python2.7/dist-packages/matplotlib/dates.py", line 867, in __call__
    return self._locator()
  File "/usr/lib/python2.7/dist-packages/matplotlib/dates.py", line 676, in __call__
    start = dmin - delta
  File "/usr/local/lib/python2.7/dist-packages/dateutil/relativedelta.py", line 309, in __rsub__
    return self.__neg__().__radd__(other)
  File "/usr/local/lib/python2.7/dist-packages/dateutil/relativedelta.py", line 306, in __radd__
    return self.__add__(other)
  File "/usr/local/lib/python2.7/dist-packages/dateutil/relativedelta.py", line 293, in __add__
    microseconds=self.microseconds))
OverflowError: date value out of range
Exception in Tkinter callback

代码:

#!/usr/bin/python

import csv
import sys
import datetime
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

if len(sys.argv[1:]) != 1:
  print "Pass csv file(s) to read from"
  sys.exit(1)

x  = []
y1 = []
y2 = []

# read csv
with open(sys.argv[1], 'rt') as inputFile:
  csvReader = csv.reader(inputFile)

  # skip header row
  csvReader.next()

  for row in csvReader:
    timestamp = datetime.datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S")
    x.append(timestamp)
    y1.append(row[1])
    y2.append(row[2])


# plot
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

# I've also tried convert the dates to matplotlib format but did not help
# import matplotlib
# x = matplotlib.dates.date2num(x)
# matplotlib.pyplot.plot_date(x, y1)

consumption, = plt.plot(x, y1, "b-", label="kw_energy_consumption")
prediction,  = plt.plot(x, y2, "r-", label="prediction")

axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], axisbg=axcolor)
spos = Slider(axpos, 'Pos', 0.1, 90.0)

def update(val):
    pos = spos.val
    ax.axis([pos,pos+10,-1,1])
    fig.canvas.draw_idle()

spos.on_changed(update)

plt.gcf().autofmt_xdate()
plt.show()

输入(简短版)需要说这个简短版本也会导致描述错误:

timestamp,kw_energy_consumption,prediction
2010-07-02 00:00:00,21.2,21.2
2010-07-02 01:00:00,16.4,16.4
2010-07-02 02:00:00,4.7,16.4
2010-07-02 03:00:00,4.7,4.7
2010-07-02 04:00:00,4.6,4.7
2010-07-02 05:00:00,23.5,4.67
2010-07-02 06:00:00,47.5,47.5
2010-07-02 07:00:00,45.4,47.5
2010-07-02 08:00:00,46.1,45.4
2010-07-02 09:00:00,41.5,45.61
2010-07-02 10:00:00,43.4,45.61
2010-07-02 11:00:00,43.8,45.61
2010-07-02 12:00:00,37.8,43.519999999999996
2010-07-02 13:00:00,36.6,43.519999999999996
2010-07-02 14:00:00,35.7,37.44
2010-07-02 15:00:00,38.9,37.44
2010-07-02 16:00:00,36.2,38.9
2010-07-02 17:00:00,36.6,38.9
2010-07-02 18:00:00,37.2,37.188

我怀疑这与Slider的最大尺寸或其中一些限制有关。

编辑:这是工作解决方案,万一有兴趣(csv阅读器被随机生成器替换为更短的代码)

#!/usr/bin/python

import sys
import numpy as np
import datetime
import random
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

x = [datetime.datetime(2015,6,25) + datetime.timedelta(hours=i) for i in range(25)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]

l, = plt.plot(x,y)

x_min_index = 0
x_max_index = 5

x_min = x[x_min_index]
x_max = x[x_max_index]

# timedelta
x_dt = x_max - x_min

# plt.axis(x_min, x_max, y_min, y_max)
y_min = plt.axis()[2]
y_max = plt.axis()[3]

plt.axis([x_min, x_max, y_min, y_max])


axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], axisbg=axcolor)

slider_max = len(x) - x_max_index - 1

# Slider(axes, name, min, max)
spos = Slider(axpos, 'Pos', matplotlib.dates.date2num(x_min), matplotlib.dates.date2num(x[slider_max]))

# pretty date names
plt.gcf().autofmt_xdate()

def update(val):
    pos = spos.val
    xmin_time = matplotlib.dates.num2date(pos)
    xmax_time = matplotlib.dates.num2date(pos) + x_dt
    # print "x_min: %s, x_max: %s" % (xmin_time.strftime("%H:%M:%S.%f"), xmax_time.strftime("%H:%M:%S.%f"))

    ########################################################
    # RETURNS THE SAME RESULT:

    # xmin_time is datetime.datetime
    # print type(xmin_time)
    # ax.axis([xmin_time, xmax_time, y_min, y_max])

    # xmin_time is numpy.float64
    xmin_time = pos
    print type(xmin_time)
    ax.axis([xmin_time, xmax_time, y_min, y_max])
    ########################################################
    fig.canvas.draw_idle()

spos.on_changed(update)

plt.show()

1 个答案:

答案 0 :(得分:2)

您的问题在于轴的定义

如果你添加

print plt.axis()

在您的第一个情节之后,您将获得

(733955.0, 733955.75, 0.0, 50.0)

From the documentation of the slider widget您可以看到滑块正在创建0.1到90之间的值

然后滑块更新功能正在为图表创建新轴

ax.axis([pos,pos+10,-1,1])

其中pos是滑块的值。因此,新轴与形状和绘制数据的位置不匹配

因此,您应该更新滑块的范围和值以及更新轴的形状。

此外,错误是因为您尝试将非常小的数字解析为日期,但在这种情况下这是无关紧要的