115

I am trying to plot information against dates. I have a list of dates in the format "01/02/1991".

I converted them by doing the following:

x = parser.parse(date).strftime('%Y%m%d'))

which gives 19910102

Then I tried to use num2date

import matplotlib.dates as dates
new_x = dates.num2date(x)

Plotting:

plt.plot_date(new_x, other_data, fmt="bo", tz=None, xdate=True)

But I get an error. It says "ValueError: year is out of range". Any solutions?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
irl_irl
  • 3,785
  • 9
  • 49
  • 60

5 Answers5

187

You can do this more simply using plot() instead of plot_date().

First, convert your strings to instances of Python datetime.date:

import datetime as dt

dates = ['01/02/1991','01/03/1991','01/04/1991']
x = [dt.datetime.strptime(d,'%m/%d/%Y').date() for d in dates]
y = range(len(x)) # many thanks to Kyss Tao for setting me straight here

Then plot:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m/%d/%Y'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator())
plt.plot(x,y)
plt.gcf().autofmt_xdate()

Result:

enter image description here

mechanical_meat
  • 163,903
  • 24
  • 228
  • 223
  • 12
    @bernie Thanks, it really helped me. However, in my problem I have too many days, so the whole plot gets clogged up. How to show every fifth date, or every tenth date on the x-axis with your method? – user1506145 Jul 03 '13 at 08:26
  • 2
    @erik I know this is a bit late, but I removed the two set_major lines and that cleared it up for me. Lost some functionality, but still an improvement. – TheBoro Mar 18 '16 at 09:19
  • 1
    Unfortunately, it shows me not 3 dates, but 6 other dates (in that interval though). Using matplotlib-1.5.1 – Groosha May 22 '16 at 09:16
  • Your example (from 2012) does not work for me (in 2023): I get `TypeError: float() argument must be a string or a number, not 'datetime.date'` for `plt.plot(x, y)`. Using “Python 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] on linux”, matplotlib 3.7.1. – PointedEars Mar 20 '23 at 18:44
78

I have too low reputation to add comment to @bernie response, with response to @user1506145. I have run in to same issue.

1

The answer to it is an interval parameter which fixes things up

2

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
import datetime as dt

np.random.seed(1)

N = 100
y = np.random.rand(N)

now = dt.datetime.now()
then = now + dt.timedelta(days=100)
days = mdates.drange(now,then,dt.timedelta(days=1))

plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.plot(days,y)
plt.gcf().autofmt_xdate()
plt.show()
Jacek Szałęga
  • 883
  • 6
  • 6
  • 3
    Please note that if you are taking the first date of a series or index and the last date, then since this is a range the last date will be cutoff, and you will run into errors with your code. Assuming you have a `Date` column on your index, then you can do: `days = mdates.drange(d1.index.min(),d1.index.max() + dt.timedelta(days=1),dt.timedelta(days=1))`. Essentially, I added 1 day to the the date of the upperbound value of the range. – David Erickson Dec 05 '20 at 04:16
  • What changes should I do in Jacek Szałęga's code in order, not to assign random dates on x-axis, but put on x-axis dates gathered/sampled from excel file? – just_learning Sep 10 '21 at 07:16
  • 2
    Better to use `mdates.AutoDateLocator()` instead of `mdates.DayLocator(interval=5)` here to automatically calculate how many date labels should be shown on the x-axis. This may answer @just_learning 's question above. – HongchaoZhang Sep 15 '22 at 03:11
26

As @KyssTao has been saying, help(dates.num2date) says that the x has to be a float giving the number of days since 0001-01-01 plus one. Hence, 19910102 is not 2/Jan/1991, because if you counted 19910101 days from 0001-01-01 you'd get something in the year 54513 or similar (divide by 365.25, number of days in a year).

Use datestr2num instead (see help(dates.datestr2num)):

new_x = dates.datestr2num(date) # where date is '01/02/1991'
mathematical.coffee
  • 55,977
  • 11
  • 154
  • 194
2

Adapting @Jacek Szałęga's answer for the use of a figure fig and corresponding axes object ax:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
import datetime as dt

np.random.seed(1)

N = 100
y = np.random.rand(N)

now = dt.datetime.now()
then = now + dt.timedelta(days=100)
days = mdates.drange(now,then,dt.timedelta(days=1))


fig = plt.figure()
ax = fig.add_subplot(111)
    
ax.plot(days,y)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=5))
ax.tick_params(axis='x', labelrotation=45)

plt.show()
DomTomCat
  • 8,189
  • 1
  • 49
  • 64
1
date = raw_date[:20]
# plot lines
plt.plot(date,target[:20] , label = "Real")
plt.xlabel('Date', fontsize=10)
plt.ylabel('Ylabel', fontsize=10)
plt.legend()
plt.title('Date to show')
plt.xticks(date_to_show_as_list,rotation=90)
plt.figure().set_figwidth(30)
plt.show()
kamran kausar
  • 4,117
  • 1
  • 23
  • 17