Annotate Time Series plot
Asked Answered
G

2

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.

Glossary answered 16/6, 2012 at 22:1 Comment(0)
S
88

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

Schizophrenia answered 17/6, 2012 at 0:30 Comment(3)
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/…Amphictyony
@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.Konopka
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)?Cadence
P
1

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

Plaster answered 15/4, 2023 at 6:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.