-
-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Problem
Feature request
Please add an option to matplotlib.dates.ConciseDateFormatter option to control the location of the offset label. Options could be the ‘right’/‘top’ [default], ‘left’/‘bottom’, or ‘both’ ends of a time axis.
Issue
Often, I plot time series data for one day (or one month). The ConciseDateFormatter is super convenient for labeling. Here’s an example with points every hour for one day:
"""Demonstrate behavior of ConciseDateFormatter offset."""
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime
import numpy as np
fig, ax = plt.subplots()
N = 24
d0 = datetime.datetime(1997, 12, 31)
days = [d0 + datetime.timedelta(hours=i) for i in range(N)]
np.random.seed(19680801)
signal = np.cumsum(np.random.randn(N))
ax.set_title("ConciseDateFormatter behavior")
locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
ax.plot(days, signal)
plt.show()

Note the prominent offset label on the right end of the axis, 1998-Jan-01. At a quick glance, a viewer may (reasonably) assume the data correspond to that day, 1998-Jan-01. In fact, however, all the data are for the previous day, 1997-Dec-31. In this example, the offset label indicates the year, month, and day following the data. (I admit the example is contrived for maximum effect!)
The axis labels and offset are not incorrect, per se, but they require the viewer to expend mental effort to figure out the date range of the data. This is a disservice to the viewer.
Workaround
There’s a pretty simple workaround: suppress the ConciseDateFormatter offset label (show_offset = False) and insert an alternate label.
"""Workaround for ConciseDateFormatter offset label."""
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime
import numpy as np
fig, ax = plt.subplots()
N = 24
d0 = datetime.datetime(1997, 12, 31)
days = [d0 + datetime.timedelta(hours=i) for i in range(N)]
np.random.seed(19680801)
signal = np.cumsum(np.random.randn(N))
ax.set_title("Desired behavior")
locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator, show_offset=False)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
ax.plot(days, signal)
dlab = mdates.num2date(ax.xaxis.get_ticklocs()[0]).strftime ( "%Y-%b-%d")
ax.text(0, -0.11, dlab, ha="left", color="r", transform=ax.transAxes)
plt.show()

This requires, however, extra code and fiddling with the label position. And, of course, the extra code will be prone to bugs, etc., especially when handling edge cases.
Because ConciseDateFormatter already contains the machinery to generate offset labels, it would be nice to access that machinery to avoid code duplication.
To some extent, the position of the offset label is a matter of taste. Not every situation is the same and not everyone will agree with my preference. It would be nice, however, to have an option available to change the default behavior.
Proposed solution
Please add an 'offset_loc' parameter to matplotlib.dates.ConciseDateFormatter. The options could be ‘right’/‘top’ [default], ‘left’/‘bottom’, or ‘both’.