Skip to content

Instantly share code, notes, and snippets.

@mjul
Last active August 28, 2023 03:17
Show Gist options
  • Save mjul/edace992c97528721242b91979f04844 to your computer and use it in GitHub Desktop.
Save mjul/edace992c97528721242b91979f04844 to your computer and use it in GitHub Desktop.
Create secondary x-axis with date and days since start in Matplotlib
Display the source blob
Display the rendered blob
Raw
# Matplotlib secondary x-axis with dates and days since start
There is more than one way to do it, but only on right way to do it.
You can use `twiny()` to get another Axes and set its x-axis.
`secondary_xaxis` is your friend, but you need the right conversion functions.
#%%
import datetime
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
start_dato = np.datetime64('2020-03-01', 'D')
days = np.arange(45)
dates = start_dato + days
#%%
# Testdata
a = 42
b = 0.03
ys = a*np.exp(b*days)
#%% md
## This is kind of a hack
#%%
# Use two axes (graphing areas)
fig, ax = plt.subplots()
fig.set_facecolor('0.9')
ax.plot(days, ys)
ax.set_yscale('log')
ax.grid(True)
ax2 = ax.twiny()
ax2.set_axisbelow(True)
xa2 = ax2.get_xaxis()
xa2.axis_date()
xa2.set_major_locator(mdates.MonthLocator())
xa2.set_major_formatter(mdates.DateFormatter('%d-%b-%Y'))
xa2.set_minor_locator(mdates.DayLocator())
#xa2.set_minor_formatter(mdates.DateFormatter('%d'))
ax2.plot(dates, ys)
plt.show()
#%% md
## This is another kind of hack
#%%
# Using two axes on top of each other, each with its own x-axis
fig, ax = plt.subplots()
fig.set_facecolor('green')
ax.set_yscale('log')
ax.grid(True)
ax.plot(dates, ys)
ax.xaxis_date()
x1 = ax.get_xaxis()
x1.set_major_locator(mdates.MonthLocator())
x1.set_major_formatter(mdates.DateFormatter('%d-%b-%Y'))
x1.set_minor_locator(mdates.DayLocator())
fig.autofmt_xdate()
x1.set_label('Dato')
ax2 = ax.twiny()
ax2.set_xlabel('Dage siden epidemistart')
x2 = ax2.get_xaxis()
x2.set_major_locator(ticker.MultipleLocator(10))
ax2.plot(days, ys, alpha=0) # invisible plot, but it sets the axis to match the other one
plt.show()
#%% md
## This is the right approach
Use `secondary_xaxis`:
#%%
# Use secondary axis
# Konverteringsfunktioner, se
# https://matplotlib.org/3.1.0/gallery/subplots_axes_and_figures/secondary_axis.html
#
def dato_til_dag(x):
"""
x is in matplotlib datenums, so they are floats.
"""
y = x - mdates.date2num(start_dato)
return y
def dag_til_dato(x):
"""
return a matplotlib datenum (x is days since start of year)
"""
y = x + mdates.date2num(start_dato)
return y
fig, ax = plt.subplots()
fig.set_facecolor('xkcd:cream')
fig.autofmt_xdate()
ax.set_yscale('log')
ax.grid(True)
ax.plot(dates, ys)
ax.xaxis_date()
ax.set_xlabel('Dato')
dage_x = ax.secondary_xaxis('top', functions=(dato_til_dag, dag_til_dato))
dage_x.set_xlabel('Dage siden epidemistart')
plt.show()
#%%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment