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()
source to share
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.
source to share