Skip to content

Commit ddde7e8

Browse files
authored
Merge pull request #10841 from jklymak/enh-new-date-formatter
ENH: new date formatter
2 parents 18a8389 + a76c70f commit ddde7e8

File tree

6 files changed

+559
-26
lines changed

6 files changed

+559
-26
lines changed

.flake8

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ per-file-ignores =
240240
examples/text_labels_and_annotations/tex_demo.py: E402
241241
examples/text_labels_and_annotations/watermark_text.py: E402
242242
examples/ticks_and_spines/auto_ticks.py: E501
243+
examples/ticks_and_spines/date_concise_formatter.py: E402
243244
examples/user_interfaces/canvasagg.py: E402
244245
examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py: E402
245246
examples/user_interfaces/embedding_in_gtk3_sgskip.py: E402
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
:orphan:
2+
3+
New date formatter: `~.dates.ConciseDateFormatter`
4+
--------------------------------------------------
5+
6+
The automatic date formatter used by default can be quite verbose. A new
7+
formatter can be accessed that tries to make the tick labels appropriately
8+
concise.
9+
10+
.. plot::
11+
12+
import datetime
13+
import matplotlib.pyplot as plt
14+
import matplotlib.dates as mdates
15+
import numpy as np
16+
17+
# make a timeseries...
18+
base = datetime.datetime(2005, 2, 1)
19+
dates = np.array([base + datetime.timedelta(hours= 2 * i)
20+
for i in range(732)])
21+
N = len(dates)
22+
np.random.seed(19680801)
23+
y = np.cumsum(np.random.randn(N))
24+
25+
lims = [(np.datetime64('2005-02'), np.datetime64('2005-04')),
26+
(np.datetime64('2005-02-03'), np.datetime64('2005-02-15')),
27+
(np.datetime64('2005-02-03 11:00'), np.datetime64('2005-02-04 13:20'))]
28+
fig, axs = plt.subplots(3, 1, constrained_layout=True)
29+
for nn, ax in enumerate(axs):
30+
# activate the formatter here.
31+
locator = mdates.AutoDateLocator()
32+
formatter = mdates.ConciseDateFormatter(locator)
33+
ax.xaxis.set_major_locator(locator)
34+
ax.xaxis.set_major_formatter(formatter)
35+
36+
ax.plot(dates, y)
37+
ax.set_xlim(lims[nn])
38+
axs[0].set_title('Concise Date Formatter')
39+
40+
plt.show()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""
2+
================================================
3+
Formatting date ticks using ConciseDateFormatter
4+
================================================
5+
6+
Finding good tick values and formatting the ticks for an axis that
7+
has date data is often a challenge. `~.dates.ConciseDateFormatter` is
8+
meant to improve the strings chosen for the ticklabels, and to minimize
9+
the strings used in those tick labels as much as possible.
10+
11+
.. note::
12+
13+
This formatter is a candidate to become the default date tick formatter
14+
in future versions of Matplotlib. Please report any issues or
15+
suggestions for improvement to the github repository or mailing list.
16+
17+
"""
18+
import datetime
19+
import matplotlib.pyplot as plt
20+
import matplotlib.dates as mdates
21+
import numpy as np
22+
23+
#############################################################################
24+
# First, the default formatter.
25+
26+
base = datetime.datetime(2005, 2, 1)
27+
dates = np.array([base + datetime.timedelta(hours=(2 * i))
28+
for i in range(732)])
29+
N = len(dates)
30+
np.random.seed(19680801)
31+
y = np.cumsum(np.random.randn(N))
32+
33+
fig, axs = plt.subplots(3, 1, constrained_layout=True, figsize=(6, 6))
34+
lims = [(np.datetime64('2005-02'), np.datetime64('2005-04')),
35+
(np.datetime64('2005-02-03'), np.datetime64('2005-02-15')),
36+
(np.datetime64('2005-02-03 11:00'), np.datetime64('2005-02-04 13:20'))]
37+
for nn, ax in enumerate(axs):
38+
ax.plot(dates, y)
39+
ax.set_xlim(lims[nn])
40+
# rotate_labels...
41+
for label in ax.get_xticklabels():
42+
label.set_rotation(40)
43+
label.set_horizontalalignment('right')
44+
axs[0].set_title('Default Date Formatter')
45+
plt.show()
46+
47+
#############################################################################
48+
# The default date formater is quite verbose, so we have the option of
49+
# using `~.dates.ConciseDateFormatter`, as shown below. Note that
50+
# for this example the labels do not need to be rotated as they do for the
51+
# default formatter because the labels are as small as possible.
52+
53+
fig, axs = plt.subplots(3, 1, constrained_layout=True, figsize=(6, 6))
54+
for nn, ax in enumerate(axs):
55+
locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
56+
formatter = mdates.ConciseDateFormatter(locator)
57+
ax.xaxis.set_major_locator(locator)
58+
ax.xaxis.set_major_formatter(formatter)
59+
60+
ax.plot(dates, y)
61+
ax.set_xlim(lims[nn])
62+
axs[0].set_title('Concise Date Formatter')
63+
64+
plt.show()
65+
66+
#############################################################################
67+
# If all calls to axes that have dates are to be made using this converter,
68+
# it is probably most convenient to use the units registry where you do
69+
# imports:
70+
71+
import matplotlib.units as munits
72+
converter = mdates.ConciseDateConverter()
73+
munits.registry[np.datetime64] = converter
74+
munits.registry[datetime.date] = converter
75+
munits.registry[datetime.datetime] = converter
76+
77+
fig, axs = plt.subplots(3, 1, figsize=(6, 6), constrained_layout=True)
78+
for nn, ax in enumerate(axs):
79+
ax.plot(dates, y)
80+
ax.set_xlim(lims[nn])
81+
axs[0].set_title('Concise Date Formatter')
82+
83+
plt.show()
84+
85+
#############################################################################
86+
# Localization of date formats
87+
# ============================
88+
#
89+
# Dates formats can be localized if the default formats are not desirable by
90+
# manipulating one of three lists of strings.
91+
#
92+
# The ``formatter.formats`` list of formats is for the normal tick labels,
93+
# There are six levels: years, months, days, hours, minutes, seconds.
94+
# The ``formatter.offset_formats`` is how the "offset" string on the right
95+
# of the axis is formatted. This is usually much more verbose than the tick
96+
# labels. Finally, the ``formatter.zero_formats`` are the formats of the
97+
# ticks that are "zeros". These are tick values that are either the first of
98+
# the year, month, or day of month, or the zeroth hour, minute, or second.
99+
# These are usually the same as the format of
100+
# the ticks a level above. For example if the axis limts mean the ticks are
101+
# mostly days, then we label 1 Mar 2005 simply with a "Mar". If the axis
102+
# limits are mostly hours, we label Feb 4 00:00 as simply "Feb-4".
103+
#
104+
# Note that these format lists can also be passed to `.ConciseDateFormatter`
105+
# as optional kwargs.
106+
#
107+
# Here we modify the labels to be "day month year", instead of the ISO
108+
# "year month day":
109+
110+
fig, axs = plt.subplots(3, 1, constrained_layout=True, figsize=(6, 6))
111+
112+
for nn, ax in enumerate(axs):
113+
locator = mdates.AutoDateLocator()
114+
formatter = mdates.ConciseDateFormatter(locator)
115+
formatter.formats = ['%y', # ticks are mostly years
116+
'%b', # ticks are mostly months
117+
'%d', # ticks are mostly days
118+
'%H:%M', # hrs
119+
'%H:%M', # min
120+
'%S.%f', ] # secs
121+
# these are mostly just the level above...
122+
formatter.zero_formats = [''] + formatter.formats[:-1]
123+
# ...except for ticks that are mostly hours, then it is nice to have
124+
# month-day:
125+
formatter.zero_formats[3] = '%d-%b'
126+
127+
formatter.offset_formats = ['',
128+
'%Y',
129+
'%b %Y',
130+
'%d %b %Y',
131+
'%d %b %Y',
132+
'%d %b %Y %H:%M', ]
133+
ax.xaxis.set_major_locator(locator)
134+
ax.xaxis.set_major_formatter(formatter)
135+
136+
ax.plot(dates, y)
137+
ax.set_xlim(lims[nn])
138+
axs[0].set_title('Concise Date Formatter')
139+
140+
plt.show()
141+
142+
#############################################################################
143+
# Registering a converter with localization
144+
# =========================================
145+
#
146+
# `.ConciseDateFormatter` doesn't have rcParams entries, but localization
147+
# can be accomplished by passing kwargs to `~.ConciseDateConverter` and
148+
# registering the datatypes you will use with the units registry:
149+
150+
import datetime
151+
152+
formats = ['%y', # ticks are mostly years
153+
'%b', # ticks are mostly months
154+
'%d', # ticks are mostly days
155+
'%H:%M', # hrs
156+
'%H:%M', # min
157+
'%S.%f', ] # secs
158+
# these can be the same, except offset by one level....
159+
zero_formats = [''] + formats[:-1]
160+
# ...except for ticks that are mostly hours, then its nice to have month-day
161+
zero_formats[3] = '%d-%b'
162+
offset_formats = ['',
163+
'%Y',
164+
'%b %Y',
165+
'%d %b %Y',
166+
'%d %b %Y',
167+
'%d %b %Y %H:%M', ]
168+
169+
converter = mdates.ConciseDateConverter(formats=formats,
170+
zero_formats=zero_formats,
171+
offset_formats=offset_formats)
172+
173+
munits.registry[np.datetime64] = converter
174+
munits.registry[datetime.date] = converter
175+
munits.registry[datetime.datetime] = converter
176+
177+
fig, axs = plt.subplots(3, 1, constrained_layout=True, figsize=(6, 6))
178+
for nn, ax in enumerate(axs):
179+
ax.plot(dates, y)
180+
ax.set_xlim(lims[nn])
181+
axs[0].set_title('Concise Date Formatter registered non-default')
182+
183+
plt.show()

0 commit comments

Comments
 (0)