8000 DOC: add units to user/explain [ci doc] · matplotlib/matplotlib@dcd3f05 · GitHub
[go: up one dir, main page]

Skip to content

Commit dcd3f05

Browse files
committed
DOC: add units to user/explain [ci doc]
1 parent 2cb8c6a commit dcd3f05

File tree

5 files changed

+285
-0
lines changed

5 files changed

+285
-0
lines changed

galleries/examples/ticks/date_concise_formatter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""
2+
.. _date_concise_formatter:
3+
24
================================================
35
Formatting date ticks using ConciseDateFormatter
46
================================================

galleries/examples/ticks/date_formatters_locators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""
2+
.. _date_formatters_locators:
3+
24
=================================
35
Date tick locators and formatters
46
=================================

galleries/examples/units/basic_units.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""
2+
.. _basic_units:
3+
24
===========
35
Basic Units
46
===========
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
"""
2+
.. user_axes_units:
3+
4+
=========================================
5+
Plotting beyond floats: dates and strings
6+
=========================================
7+
8+
The most basic way to use Matplotlib plotting methods is to pass coordinates in
9+
as numerical numpy arrays. For example, ``plot(x, y)`` will work if ``x`` and
10+
``y`` are numpy arrays of floats (or integers). Plotting methods will also
11+
work if `numpy.asarray` will convert ``x`` and ``y`` to an array of floating
12+
point numbers; e.g. ``x`` could be a python list.
13+
14+
Matplotlib also has the ability to convert other data types if a "unit
15+
converter" exists for the data type. Matplotlib has two built-in converters,
16+
one for dates and the other for lists of strings. Other downstream libraries
17+
have their own converters to handle their data types.
18+
19+
The method to add converters to Matplotlib is described in `matplotlib.units`.
20+
Here we briefly overview the built-in date and string converters.
21+
22+
Date conversion
23+
===============
24+
25+
If ``x`` and/or ``y`` are a list of `datetime` or an array of
26+
`numpy.datetime64`, Matplotlib has a built in converter that will convert the
27+
datetime to a float, and add locators and formatters to the axis that are
28+
appropriate for dates.
29+
30+
In the following example, the x-axis gains a converter that converts from
31+
`numpy.datetime64` to float, and a locator that put ticks at the beginning of
32+
the month, and a formatter that label the ticks appropriately:
33+
"""
34+
35+
import numpy as np
36+
37+
import matplotlib.dates as mdates
38+
import matplotlib.units as munits
39+
40+
import matplotlib.pyplot as plt
41+
42+
fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
43+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
44+
x = np.arange(len(time))
45+
ax.plot(time, x)
46+
47+
# %%
48+
#
49+
# Note that if we try to plot a float on the x-axis, it will be plotted in
50+
# units of days since the "epoch" for the converter, in this case 1970-01-01
51+
# (see :ref:`date-format`). So when we plot the value 0, the ticks start at
52+
# 1970-01-01, and note that the locator now chooses every two years for a tick
53+
# instead of every month:
54+
55+
fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
56+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
57+
x = np.arange(len(time))
58+
ax.plot(time, x)
59+
# 0 gets labeled as 1970-01-01
60+
ax.plot(0, 0, 'd')
61+
ax.text(0, 0, ' Float x=0', rotation=45)
62+
63+
64+
# %%
65+
#
66+
# We can customize the locator and the formatter; see :ref:`date-locators` and
67+
# :ref:`date-formatters` for a complete list, and
68+
# :ref:`date_formatters_locators` for examples of them in use. Here we locate
69+
# by every second month, and format just with the month's 3-letter name using
70+
# ``"%b"`` (see `~datetime.datetime.strftime` for format codes):
71+
72+
fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
73+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
74+
x = np.arange(len(time))
75+
ax.plot(time, x)
76+
ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=np.arange(1, 13, 2)))
77+
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
78+
ax.set_xlabel('1980')
79+
80+
# %%
81+
#
82+
# The default locator is the `~.dates.AutoDateLocator`, and the default
83+
# Formatter `~.dates.AutoDateFormatter`. There is also a "concise"
84+
# formatter/locator that gives a more compact labelling, and can be set via
85+
# rcParams. Note how instead of the redundant "Jan" label at the start of the
86+
# year, "1980" is used instead. See :ref:`date_concise_formatter` for more
87+
# examples.
88+
89+
plt.rcParams['date.converter'] = 'concise'
90+
91+
fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
92+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
93+
x = np.arange(len(time))
94+
ax.plot(time, x)
95+
96+
# %%
97+
#
98+
# We can set the limits on the axis either by passing the appropriate dates in
99+
# or by passing a floating point value in the proper units of floating days
100+
# since the epoch. We can get this value from `~.matplotlib.date2num`.
101+
102+
fig, axs = plt.subplots(2, 1, figsize=(5.4, 3), layout='constrained')
103+
for ax in axs.flat:
104+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
105+
x = np.arange(len(time))
106+
ax.plot(time, x)
107+
108+
# set xlim using datetime64:
109+
axs[0].set_xlim(np.datetime64('1980-02-01'), np.datetime64('1980-04-01'))
110+
111+
# set xlim using floats:
112+
# Note can get from mdates.date2num(np.datetime64('1980-02-01'))
113+
axs[1].set_xlim(3683, 3683+60)
114+
115+
# %%
116+
#
117+
# String conversion: categorical plots
118+
# ====================================
119+
#
120+
# Sometimes we want to label categories on an axis rather than numbers.
121+
# Matplotlib allows this using a "categorical" converter (see
122+
# `~.matplotlib.category`).
123+
124+
data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
125+
names = list(data.keys())
126+
values = list(data.values())
127+
128+
fig, axs = plt.subplots(1, 3, figsize=(7, 3), sharey=True, layout='constrained')
129+
axs[0].bar(names, values)
130+
axs[1].scatter(names, values)
131+
axs[2].plot(names, values)
132+
fig.suptitle('Categorical Plotting')
133+
134+
# %%
135+
#
136+
# Note that the "categories" are plotted in the order that they are first
137+
# specified and that subsequent plotting in a different order will not affect
138+
# the original order. Further, new additions will be added on the end:
139+
140+
fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
141+
ax.bar(names, values)
142+
143+
# plot in a different order:
144+
ax.scatter(['lemon', 'apple'], [7, 12])
145+
146+
# add a new category, and out of order:
147+
ax.plot(['pear', 'orange', 'apple', 'lemon'], [13, 10, 7, 12], color='C1')
148+
149+
150+
# %%
151+
< F987 span class=pl-c>#
152+
# Note that when using ``plot`` like in the above, the order of the plotting is
153+
# mapped onto the original order of the data, so the new line goes in the order
154+
# specified.
155+
#
156+
# The category converter maps from categories to integers, starting at zero. So
157+
# data can also be manually added to the axis using a float. However, note
158+
# that a float that is not a category will not get a label.
159+
160+
fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
161+
ax.bar(names, values)
162+
163+
# 0 gets labeled as "apple"
164+
ax.plot(0, 0, 'd', color='C1')
165+
ax.text(0, 0, ' Float x=0', rotation=45, color='C1')
166+
167+
# 2 gets labeled as "lemon"
168+
ax.plot(2, 0, 'd', color='C1')
169+
ax.text(2, 0, ' Float x=2', rotation=45, color='C1')
170+
171+
# 4 doesn't get a label
172+
ax.plot(4, 0, 'd', color='C1')
173+
ax.text(4, 0, ' Float x=4', rotation=45, color='C1')
174+
175+
# 2.5 doesn't get a label
176+
ax.plot(2.5, 0, 'd', color='C1')
177+
ax.text(2.5, 0, ' Float x=2.5', rotation=45, color='C1')
178+
179+
180+
# %%
181+
#
182+
# Setting the limits for a category axis can be done by specifying the
183+
# categories, or by specifying floating point numbers:
184+
185+
fig, axs = plt.subplots(2, 1, figsize=(5, 5), layout='constrained')
186+
ax = axs[0]
187+
ax.bar(names, values)
188+
ax.set_xlim('orange', 'lemon')
189+
ax.set_xlabel('limits set with categories')
190+
ax = axs[1]
191+
ax.bar(names, values)
192+
ax.set_xlim(0.5, 2.5)
193+
ax.set_xlabel('limits set with floats')
194+
195+
# %%
196+
#
197+
# The category axes are helpful for some plot types, but can lead to confusion
198+
# if data is read in as a list of strings, even if it is meant to be a list of
199+
# floats or dates. This sometimes happens when reading comma-separated value
200+
# (CSV) files. The categorical locator and formatter will put a tick at every
201+
# string value and label each one as well:
202+
203+
fig, ax = plt.subplots(figsize=(5.4, 2.5), layout='constrained')
204+
x = [str(xx) for xx in np.arange(100)] # list of strings
205+
ax.plot(x, np.arange(100))
206+
ax.set_xlabel('x is list of strings')
207+
208+
# %%
209+
#
210+
# If this is not desired, then simply convert the data to floats before plotting:
211+
212+
fig, ax = plt.subplots(figsize=(5.4, 2.5), layout='constrained')
213+
x = np.asarray(x, dtype='float') # array of float.
214+
ax.plot(x, np.arange(100))
215+
ax.set_xlabel('x is array of floats')
216+
217+
# %%
218+
#
219+
# Determine converter, formatter, and locator on an axis
220+
# ======================================================
221+
#
222+
# Sometimes it is helpful to be able to debug what Matplotlib is using to
223+
# convert the incoming data: we can do that by querying the ``converter``
224+
# property on the axis. We can also query the formatters and locators using
225+
# `~.axis.get_major_locator` and `~.axis.get_major_formatter`.
226+
#
227+
# Note that by default the converter is *None*.
228+
229+
fig, axs = plt.subplots(3, 1, figsize=(6.4, 7), layout='constrained')
230+
x = np.arange(100)
231+
ax = axs[0]
232+
ax.plot(x, x)
233+
label = f'Converter: {ax.xaxis.converter}\n '
234+
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
235+
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
236+
ax.set_xlabel(label)
237+
238+
ax = axs[1]
239+
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
240+
x = np.arange(len(time))
241+
ax.plot(time, x)
242+
label = f'Converter: {ax.xaxis.converter}\n '
243+
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
244+
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
245+
ax.set_xlabel(label)
246+
247+
ax = axs[2]
248+
data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
249+
names = list(data.keys())
250+
values = list(data.values())
251+
ax.plot(names, values)
252+
label = f'Converter: {ax.xaxis.converter}\n '
253+
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
254+
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
255+
ax.set_xlabel(label)
256+
257+
# %%
258+
#
259+
# General unit support
260+
# ====================
261+
#
262+
# The support for dates and categories is part of "units" support that is
263+
# built into Matplotlib. This is described at `.matplotlib.units` and in the #
264+
# :ref:`basic_units` example.
265+
#
266+
# Unit support works by querying the type of data passed to the plotting
267+
# function and dispatching to the first converter in a list that accepts that
268+
# type of data. So below, if ``x`` has ``datetime`` objects in it, the
269+
# converter will be ``_SwitchableDateConverter``; if it has has strings in it,
270+
# it will be sent to the ``StrCategoryConverter``.
271+
272+
for k in munits.registry:
273+
print(f"type: {k};\n converter: {munits.registry[k]}")
274+
275+
# %%
276+
#
277+
# Downstream libraries like pandas, astropy, and pint all can add their own
278+
# converters to Matplotlib.

galleries/users_explain/axes/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ annotations like x- and y-labels, titles, and legends.
4343

4444
axes_scales
4545
axes_ticks
46+
axes_units
4647
Legends <legend_guide>
4748
Subplot mosaic <mosaic>
4849

0 commit comments

Comments
 (0)
0