Created
December 27, 2023 08:31
-
-
Save mmore500/9bdde8fc1a943e72f1659ac795323b06 to your computer and use it in GitHub Desktop.
Code for outset Rain Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import datetime, timedelta | |
import itertools as it | |
import typing | |
from matplotlib import pyplot as plt | |
from matplotlib import text as mpl_text | |
import numpy as np | |
import outset as otst | |
import opytional as opyt | |
import pandas as pd | |
import seaborn as sns | |
# ----- prepare data ----- | |
nwls = "NW Lysimeter\n(35.18817624°N, -102.09791°W)" | |
swls = "SW Lysimeter\n(35.18613985°N, -102.0979187°W)" | |
df = pd.read_csv("https://osf.io/6mx3e/download") | |
df[nwls] = df["NW precip in mm"] | |
df[swls] = df["SW precip in mm"] | |
df["Max precip"] = np.maximum(df["SW precip in mm"], df["NW precip in mm"]) | |
march_df = df[ # filter down to just data from March 2019 | |
(df["Decimal DOY"] >= 59) & (df["Decimal DOY"] <= 90) | |
].reset_index() | |
# ----- setup axes grid ----- | |
grid = otst.OutsetGrid( # initialize axes grid manager | |
data=[ | |
(71.6, 0, 72.2, 2), # (x0, y0, x1, y1) region to outset | |
(59, 0, 90, 0.2), | |
(81.3, 0, 82.2, 16), | |
], | |
x="Time", | |
y="Max precip", | |
marqueeplot_kws={"frame_outer_pad": 0, "mark_glyph_kws": {"zorder": 11}}, | |
marqueeplot_source_kws={"zorder": 10, "frame_face_kws": {"zorder": 10}}, | |
aspect=2, | |
col_wrap=2, | |
) | |
# ----- plot content ----- | |
grid.broadcast( # apply white underlay for lineplot to all axes | |
plt.stackplot, | |
march_df["Decimal DOY"].to_numpy(), | |
march_df["Max precip"].to_numpy(), | |
colors=["white"], | |
lw=20, | |
edgecolor="white", | |
zorder=10, | |
) | |
# draw semi-transparent filled lineplot on all axes for each lysimeter | |
for y, color in zip([nwls, swls], ["fuchsia", "aquamarine"]): | |
grid.broadcast( | |
plt.stackplot, | |
march_df["Decimal DOY"].to_numpy(), | |
march_df[y].to_numpy(), | |
colors=[color], | |
labels=[y], | |
lw=2, | |
edgecolor=color, | |
alpha=0.4, | |
zorder=10, | |
) | |
# dispatch marquee render | |
grid.marqueeplot(equalize_aspect=False) # allow axes aspect ratios to vary | |
# ----- replace numeric axes tick labels with datetimes ----- | |
def to_dt(day_of_year: float, year: int = 2019) -> datetime: | |
"""Convert decimal day of the year to a datetime object.""" | |
return datetime(year=year, month=1, day=1) + timedelta(days=day_of_year) | |
def format_tick_value( | |
prev_value: typing.Optional[mpl_text.Text], | |
value: mpl_text.Text, | |
) -> str: | |
decimal_doy = float(value.get_text()) | |
prev_decimal_doy = opyt.apply_if(prev_value, lambda x: float(x.get_text())) | |
if int(decimal_doy) == opyt.apply_if(prev_decimal_doy, int): | |
return to_dt(decimal_doy).strftime("%H:%M") | |
elif decimal_doy % 1.0 < 1 / 24: | |
return to_dt(decimal_doy).strftime("%b %-d") | |
else: | |
return to_dt(decimal_doy).strftime("%H:%M\n%b %-d") | |
for ax in grid.axes.flat: | |
ax.set_xticks( | |
# keep only x ticks that are within axes limits | |
[val for val in ax.get_xticks() if np.clip(val, *ax.get_xlim()) == val] | |
) | |
ax.set_xticklabels( | |
list( | |
it.starmap( | |
format_tick_value, | |
it.pairwise((None, *ax.get_xticklabels())), | |
), | |
), | |
) | |
# ----- finalize plot ----- | |
grid.axes.flat[0].legend( | |
loc="upper left", | |
bbox_to_anchor=(0.08, 0.95), | |
bbox_transform=grid.figure.transFigure, | |
frameon=True, | |
) | |
grid.set_ylabels("Precipitation (mm)") | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment