49

I have an index array (x) of dates (datetime objects) and an array of actual values (y: bond prices). Doing the following:

plot(x,y)

produces a perfectly fine time series graph with the x-axis labeled with the dates. No problem so far. But I want to add text on certain dates. For example, on 2009-10-31, I wish to display the text "Event 1" with an arrow pointing to the y value at that date.

I have read through the Matplotlib documentation on text() and annotate() to no avail.

2 Answers 2

89

Matplotlib uses an internal floating point format for dates.

You just need to convert your date to that format (using matplotlib.dates.date2num or matplotlib.dates.datestr2num) and then use annotate as usual.

As a somewhat excessively fancy example:

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

x = [dt.datetime(2009, 05, 01), dt.datetime(2010, 06, 01), 
     dt.datetime(2011, 04, 01), dt.datetime(2012, 06, 01)]
y = [1, 3, 2, 5]

fig, ax = plt.subplots()
ax.plot_date(x, y, linestyle='--')

ax.annotate('Test', (mdates.date2num(x[1]), y[1]), xytext=(15, 15), 
            textcoords='offset points', arrowprops=dict(arrowstyle='-|>'))

fig.autofmt_xdate()
plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

This answer works like a dream but is a bit cumbersome (not Joe's fault). I've come across this blog post and realised that it's ok to just pass xy=('2009-5-1',3) in the above example and it works. Tested on matplotlib 2.2.2. jakevdp.github.io/PythonDataScienceHandbook/…
@AleksanderLidtke Using a string like this only works if you are plotting from a pandas dataframe. It will not work if you plot from matplotlib directly.
Great answer! Is it me or datetime.datetime(2010, 06, 01) yields SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers and should be replaced by datetime.datetime(2010, 6, 1)?
3

Newer versions of matplotlib (e.g. 3.7.0) doesn't require explicit date2num calls anymore. Just pass a point on the plot directly as xy= to annotate().

from datetime import datetime
import matplotlib.pyplot as plt

x = [datetime(2009, 5, 1), datetime(2010, 6, 1), 
     datetime(2011, 4, 1), datetime(2012, 6, 1)]
y = [1, 3, 2, 5]

fig, ax = plt.subplots(facecolor='white')
ax.plot(x, y, linestyle='--')

ax.annotate('Test', xy=(x[1], y[1]),   # <---- directly pass the point position
            xytext=(-15, 15), textcoords='offset points', 
            arrowprops={'arrowstyle': '->'})

fig.autofmt_xdate()

img1

A more terse (but less flexible) solution is to use text() to annotate. Again, no need to perform a datetime-to-number conversion; just pass the point as is.

plt.plot(x, y, linestyle='--')
plt.text(x[1], y[1], 'Test')        # use x-y coordinate values as is
plt.xticks(plt.xticks()[0][::2]);

img2

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.