19

I tried to plot a rectangle on a graph with a datetime x-axis using the following code:

from datetime import datetime, timedelta
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt

# Create new plot
fig = plt.figure()
ax = fig.add_subplot(111)

# Create rectangle
startTime = datetime.now()
width = timedelta(seconds = 1)
endTime = startTime + width
rect = Rectangle((startTime, 0), width, 1, color='yellow')

# Plot rectangle
ax.add_patch(rect)   ### ERROR HERE!!! ###
plt.xlim([startTime, endTime])
plt.ylim([0, 1])
plt.show()

However, I get the error:

TypeError: unsupported operand type(s) for +: 'float' and 'datetime.timedelta'

What's going wrong? (I'm using matplotlib version 1.0.1)

mchen
  • 9,808
  • 17
  • 72
  • 125

3 Answers3

24

The problem is that matplotlib uses its own representation of dates/times (floating number of days), so you have to convert them first. Furthermore, you will have to tell the xaxis that it should have date/time ticks and labels. The code below does that:

from datetime import datetime, timedelta
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# Create new plot
fig = plt.figure()
ax = fig.add_subplot(111)

# Create rectangle x coordinates
startTime = datetime.now()
endTime = startTime + timedelta(seconds = 1)

# convert to matplotlib date representation
start = mdates.date2num(startTime)
end = mdates.date2num(endTime)
width = end - start

# Plot rectangle
rect = Rectangle((start, 0), width, 1, color='yellow')
ax.add_patch(rect)   

# assign date locator / formatter to the x-axis to get proper labels
locator = mdates.AutoDateLocator(minticks=3)
formatter = mdates.AutoDateFormatter(locator)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)

# set the limits
plt.xlim([start-width, end+width])
plt.ylim([-.5, 1.5])

# go
plt.show()

Result:

enter image description here

NOTE: Matplotlib 1.0.1 is very old. I can't guarantee that my example will work. You should try to update!

hitzg
  • 12,133
  • 52
  • 54
  • Note that if the dates in the x-axis are from `pandas`, you need to convert to python date-time first. The line `start = mdates.date2num( startTime )` would then be `start = mdates.date2num( startTime.to_pydatetime() )`. The same goes for the `end`. – Luis Aug 08 '17 at 16:15
0

The issue is that the type(startTime) datetime.datetime isn't a valid type to pass into the rectangle. It needs to be typecast into a supported type in order to use the rectangle patch.

If all you really want is to make a yellow rectangle just make a normal plot with a yellow background:

from datetime import datetime, timedelta
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt

# Create new plot
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='yellow')
plt.xticks(rotation=15)
plt.tight_layout()
# Create rectangle
startTime = datetime.now()
width = timedelta(seconds = 1)
endTime = startTime + width

#rect = Rectangle((0, 0), 1, 1, color='yellow')

# Plot rectangle
#ax.add_patch(rect)   ### ERROR HERE!!! ###

plt.xlim([startTime, endTime])
plt.ylim([0, 1])
plt.show()
kaminsknator
  • 1,135
  • 3
  • 15
  • 26
0

Another error that you can see when trying to create a patches.Rectangle artist using the datetime values for x is:

TypeError: float() argument must be a string or a number.

The reason for this is that during Rectangle object initialization x argument is converted internally to float:

self._x = float(xy[0])

It doesn't work for datetime values. Solution proposed by @hitzg will solve this issue because matplotlib.dates.date2num() returns float.

wombatonfire
  • 4,585
  • 28
  • 36