I want to be able to scroll across x (horizontal) line which represents date/time. For this purpose I'm using slider widget. The problem is that when I scroll too far (where no y values exists) I am no more able to use scrolling feature provided by slider widget and the whole plotted lines disappear and remains just empty figure. The error I get on terminal is following:
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()
Input (short version) Need to say that this short version also causes 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 that this has something to do with maximum size of Slider or some of it's limits.
Edit: Here is working solution, in case anyone interested (csv reader is replaced by 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()