Skip to content

Instantly share code, notes, and snippets.

@mmore500
Created December 27, 2023 08:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmore500/9bdde8fc1a943e72f1659ac795323b06 to your computer and use it in GitHub Desktop.
Save mmore500/9bdde8fc1a943e72f1659ac795323b06 to your computer and use it in GitHub Desktop.
Code for outset Rain Example
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