Datetime with slider in matplotlib

I want to be able to scroll through the x (horizontal) line that represents the date / time. For this I am using a slider widget . The problem is that when I scroll too far (where there are no y values), I can no longer use the scroll function provided by the slider, and all the drawn lines disappear and remain an empty shape. The error I am getting on the terminal is as follows:

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

      

code:

#!/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()

      

Login (short version) It must be said that this short version also causes the described errors:

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

      

I suspect it has something to do with the maximum size of the slider or some of its limitations.

Edit: here is a working solution, in case anyone is interested (csv reader is replaced with a random generator for shorter code)

#!/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()

      

+3


source to share


1 answer


Your problem is defining your axes

If you add

print plt.axis()

      

Right after your first plots, you will receive

(733955.0, 733955.75, 0.0, 50.0)

      

From the slider widget documentation, you can see that your slider creates values ​​between 0.1 and 90



And then the slider update function creates new axes for your graph in

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

      

Where pos is the value of your slider. Thus, the new axis does not match the shape or position of your plotted data.

So, you have to update the range and values ​​of your slider and the shape of the updated axis.

Also, errors occur because you are trying to parse a very small number as a date, but in this case it doesn't matter.

+2


source







All Articles