|
1 | 1 | """ |
2 | | -===================================== |
3 | | -Custom tick formatter for time series |
4 | | -===================================== |
5 | | -
|
6 | | -When plotting time series, e.g., financial time series, one often wants |
7 | | -to leave out days on which there is no data, i.e. weekends. The example |
8 | | -below shows how to use an 'index formatter' to achieve the desired plot |
| 2 | +==================== |
| 3 | +Date Index Formatter |
| 4 | +==================== |
| 5 | +
|
| 6 | +When plotting daily data, a frequent request is to plot the data |
| 7 | +ignoring skips, e.g., no extra spaces for weekends. This is particularly |
| 8 | +common in financial time series, when you may have data for M-F and |
| 9 | +not Sat, Sun and you don't want gaps in the x axis. The approach is |
| 10 | +to simply use the integer index for the xdata and a custom tick |
| 11 | +Formatter to get the appropriate date string for a given index. |
9 | 12 | """ |
| 13 | + |
10 | 14 | from __future__ import print_function |
| 15 | + |
11 | 16 | import numpy as np |
| 17 | + |
12 | 18 | import matplotlib.pyplot as plt |
13 | 19 | import matplotlib.cbook as cbook |
14 | | -import matplotlib.ticker as ticker |
15 | | - |
16 | | -# Load a numpy record array from yahoo csv data with fields date, open, close, |
17 | | -# volume, adj_close from the mpl-data/example directory. The record array |
18 | | -# stores the date as an np.datetime64 with a day unit ('D') in the date column. |
19 | | -with cbook.get_sample_data('goog.npz') as datafile: |
20 | | - r = np.load(datafile)['price_data'].view(np.recarray) |
21 | | -r = r[-30:] # get the last 30 days |
22 | | -# Matplotlib works better with datetime.datetime than np.datetime64, but the |
23 | | -# latter is more portable. |
24 | | -date = r.date.astype('O') |
25 | | - |
26 | | -# first we'll do it the default way, with gaps on weekends |
27 | | -fig, axes = plt.subplots(ncols=2, figsize=(8, 4)) |
28 | | -ax = axes[0] |
29 | | -ax.plot(date, r.adj_close, 'o-') |
30 | | -ax.set_title("Default") |
31 | | -fig.autofmt_xdate() |
| 20 | +from matplotlib.dates import bytespdate2num, num2date |
| 21 | +from matplotlib.ticker import Formatter |
32 | 22 |
|
33 | | -# next we'll write a custom formatter |
34 | | -N = len(r) |
35 | | -ind = np.arange(N) # the evenly spaced plot indices |
36 | 23 |
|
| 24 | +datafile = cbook.get_sample_data('msft.csv', asfileobj=False) |
| 25 | +print('loading %s' % datafile) |
| 26 | +msft_data = np.genfromtxt(datafile, delimiter=',', names=True, |
| 27 | + converters={0: bytespdate2num('%d-%b-%y')})[-40:] |
37 | 28 |
|
38 | | -def format_date(x, pos=None): |
39 | | - thisind = np.clip(int(x + 0.5), 0, N - 1) |
40 | | - return date[thisind].strftime('%Y-%m-%d') |
41 | 29 |
|
42 | | -ax = axes[1] |
43 | | -ax.plot(ind, r.adj_close, 'o-') |
44 | | -ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date)) |
45 | | -ax.set_title("Custom tick formatter") |
46 | | -fig.autofmt_xdate() |
| 30 | +class MyFormatter(Formatter): |
| 31 | + def __init__(self, dates, fmt='%Y-%m-%d'): |
| 32 | + self.dates = dates |
| 33 | + self.fmt = fmt |
| 34 | + |
| 35 | + def __call__(self, x, pos=0): |
| 36 | + 'Return the label for time x at position pos' |
| 37 | + ind = int(np.round(x)) |
| 38 | + if ind >= len(self.dates) or ind < 0: |
| 39 | + return '' |
| 40 | + |
| 41 | + return num2date(self.dates[ind]).strftime(self.fmt) |
47 | 42 |
|
| 43 | +formatter = MyFormatter(msft_data['Date']) |
| 44 | + |
| 45 | +fig, ax = plt.subplots() |
| 46 | +ax.xaxis.set_major_formatter(formatter) |
| 47 | +ax.plot(np.arange(len(msft_data)), msft_data['Close'], 'o-') |
| 48 | +fig.autofmt_xdate() |
48 | 49 | plt.show() |
0 commit comments