Skip to content

Plotting

The synhydro.plotting module provides standardized plotting utilities for ensemble hydrologic data. All functions accept an Ensemble object directly and return (fig, ax) (or (fig, axes) for multi-panel layouts), so they compose easily inside larger figures.

The module follows a few consistent conventions:

  • Ensemble-first inputs. Every function takes an Ensemble as the first positional argument; observed is optional and rendered as an overlay.
  • Percentile bands by default. Ensemble-vs-time plots show a median line with 10th-90th percentile shading, optionally with a few member traces.
  • Single source of truth. Colors, line widths, and layout defaults live in COLORS, STYLE, LAYOUT, and LABELS. Override these dicts and call apply_plotting_style() to re-theme every plot in a script.
  • Optional auto-save. Pass filename=... to write the figure to disk at dpi (default 300) without an explicit fig.savefig call.

Quick reference

Function Description
plot_timeseries Ensemble timeseries with median, percentile band, and optional members
plot_flow_ranges Min/max/median ranges aggregated by daily, weekly, or monthly periods
plot_seasonal_cycle Per-period mean or std with inter-realization band
plot_flow_duration_curve FDC with ensemble uncertainty and optional annual range
plot_cdf Empirical CDF with ensemble uncertainty
plot_histogram Flow histogram with optional KDE overlay
plot_monthly_distributions Monthly boxplot or violin plot of ensemble vs observed
plot_autocorrelation Lagged autocorrelation with ensemble range
plot_spatial_correlation Multi-site correlation heatmap (ensemble, observed, or difference)
plot_drought_characteristics Scatter of drought duration, magnitude, and severity
plot_ssi_timeseries SSI timeseries with drought-severity shading
plot_validation_panel 5-panel marginal and seasonal validation figure
apply_plotting_style Apply current COLORS/STYLE to matplotlib rcParams
warn_if_many_realizations Emit a warning if an ensemble is too large for a given plot
warn_if_few_realizations Emit a warning if an ensemble is too small for stable percentiles

Timeseries

plot_timeseries

plot_timeseries(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, percentiles: Optional[List[float]] = None, show_members: Optional[int] = None, start_date: Optional[str] = None, end_date: Optional[str] = None, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot ensemble timeseries with uncertainty bands.

Displays ensemble percentiles as shaded regions, optionally overlays individual ensemble members and observed data.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
percentiles List[float]

Percentiles to display as uncertainty bands. Default [10, 50, 90].

None
show_members int

Number of individual ensemble members to show with transparency

None
start_date str

Start date for plot (ISO format or pandas-compatible)

None
end_date str

End date for plot

None
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates from metadata

None
xlabel str

X-axis label. If None, uses 'Date'

None
ylabel str

Y-axis label. If None, uses units

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
log_scale bool

Use logarithmic y-axis

False
units str

Flow units for y-axis label

'cms'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> fig, ax = plot_timeseries(ensemble, observed=Q_obs, site='site_A')
>>> # Plot on existing axes
>>> fig, axes = plt.subplots(2, 1)
>>> plot_timeseries(ensemble, ax=axes[0], percentiles=[25, 50, 75])
>>> # Show individual members
>>> plot_timeseries(ensemble, show_members=10, filename='timeseries.png')

plot_flow_ranges

plot_flow_ranges(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, timestep: str = 'daily', aggregate: str = 'median', ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot flow ranges across temporal aggregation periods.

Shows min/max ranges and central tendency (median or mean) for each period (day of year, week, month, etc.).

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
timestep str

Temporal aggregation: 'daily', 'weekly', 'monthly', 'annual'

'daily'
aggregate str

Central tendency measure: 'median' or 'mean'

'median'
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses timestep label

None
ylabel str

Y-axis label. If None, uses units

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
log_scale bool

Use logarithmic y-axis

False
units str

Flow units for y-axis label

'cms'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_flow_ranges(ensemble, observed=Q_obs, timestep='monthly')
>>> plot_flow_ranges(ensemble, timestep='weekly', log_scale=True)

plot_seasonal_cycle

plot_seasonal_cycle(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, statistic: str = 'mean', timestep: str = 'monthly', ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot seasonal-cycle line of a per-period statistic with ensemble band.

Computes the chosen statistic (mean or std) of flow within each period (day-of-year, week, or month) for each realization, then plots the ensemble central tendency (median across realizations) and a 10th-90th percentile band over realizations. Observed data, when provided, is plotted as a single line at the same period statistic.

This shows the spread of the per-realization period statistic across the ensemble, which is more meaningful for ensemble validation than the raw min/max envelope shown by plot_flow_ranges.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data.

required
observed Series

Observed timeseries for comparison.

None
site str

Site name to plot. If None, uses first site in ensemble.

None
statistic (mean, std)

Per-period statistic to summarize.

'mean'
timestep str

Period for the seasonal grouper: 'daily' (day-of-year), 'weekly' (week-of-year), or 'monthly' (month-of-year).

'monthly'
ax Axes

Existing axes to plot on. If None, creates new figure.

None
figsize tuple

Figure size (width, height) in inches.

from config
title str

Plot title. If None, auto-generates.

None
xlabel str

X-axis label. If None, uses the timestep label.

None
ylabel str

Y-axis label. If None, uses units with statistic prefix.

None
legend bool

Whether to display legend.

True
grid bool

Whether to display grid.

True
log_scale bool

Use logarithmic y-axis.

False
units str

Flow units for y-axis label.

'cms'
filename str

Path to save figure.

None
dpi int

Resolution for saved figure.

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Raises:

Type Description
ValueError

If statistic is not 'mean' or 'std', or if timestep is not one of 'daily', 'weekly', 'monthly'.

Examples:

>>> fig, ax = plot_seasonal_cycle(ensemble, observed=Q_obs)
>>> plot_seasonal_cycle(ensemble, statistic='std', timestep='weekly')

Distributions

plot_flow_duration_curve

plot_flow_duration_curve(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, show_annual_range: bool = True, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = True, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot flow duration curve with ensemble uncertainty.

Shows flow exceedance probabilities with ensemble range and total period FDC.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
show_annual_range bool

Show range of annual FDCs as shaded region

True
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses 'Non-Exceedance Probability'

None
ylabel str

Y-axis label. If None, uses units

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
log_scale bool

Use logarithmic y-axis

True
units str

Flow units for y-axis label

'cms'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> fig, ax = plot_flow_duration_curve(ensemble, observed=Q_obs)
>>> plot_flow_duration_curve(ensemble, show_annual_range=False, log_scale=False)

plot_cdf

plot_cdf(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, show_annual_range: bool = True, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot empirical cumulative distribution function with ensemble uncertainty.

Shows the empirical CDF (cumulative non-exceedance probability) of flow values, with the ensemble band reflecting the spread of annual CDFs across realizations. Companion to plot_flow_duration_curve: same data, different plotting convention (CDF y-axis is cumulative probability, x-axis is flow value).

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data.

required
observed Series

Observed timeseries for comparison.

None
site str

Site name to plot. If None, uses first site in ensemble.

None
show_annual_range bool

Show range of annual CDFs as a shaded region across the x-axis.

True
ax Axes

Existing axes to plot on. If None, creates new figure.

None
figsize tuple

Figure size (width, height) in inches.

from config
title str

Plot title. If None, auto-generates.

None
xlabel str

X-axis label. If None, uses units.

None
ylabel str

Y-axis label. If None, uses 'Cumulative Probability'.

None
legend bool

Whether to display legend.

True
grid bool

Whether to display grid.

True
log_scale bool

Use logarithmic x-axis (CDF y-axis is always 0-1).

False
units str

Flow units for x-axis label.

'cms'
filename str

Path to save figure.

None
dpi int

Resolution for saved figure.

from config
**kwargs

Forwarded to ax.plot and ax.fill_betweenx.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> fig, ax = plot_cdf(ensemble, observed=Q_obs)
>>> plot_cdf(ensemble, show_annual_range=False, log_scale=True)

plot_histogram

plot_histogram(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, bins: Union[int, str] = 'auto', density: bool = True, show_kde: bool = True, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_x: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot histogram of flow values with optional KDE.

Shows distribution of all flow values across ensemble and optionally overlays kernel density estimate.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
bins int or str

Number of bins or binning strategy ('auto', 'sturges', 'fd', etc.)

'auto'
density bool

If True, plot probability density; else raw counts

True
show_kde bool

Overlay kernel density estimate

True
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses units

None
ylabel str

Y-axis label. If None, uses 'Density' or 'Count'

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
log_x bool

Use logarithmic x-axis

False
units str

Flow units for x-axis label

'cms'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.hist and ax.plot (KDE overlay).

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_histogram(ensemble, observed=Q_obs, bins=50)
>>> plot_histogram(ensemble, density=False, show_kde=False)

plot_monthly_distributions

plot_monthly_distributions(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, plot_type: str = 'box', ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['wide_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, log_scale: bool = False, units: str = 'cms', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot distribution of flows for each month.

Shows monthly boxplots or violin plots comparing ensemble and observed distributions.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
plot_type str

Type of distribution plot: 'box' or 'violin'

'box'
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses 'Month'

None
ylabel str

Y-axis label. If None, uses units

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
log_scale bool

Use logarithmic y-axis

False
units str

Flow units for y-axis label

'cms'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.boxplot or ax.violinplot.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_monthly_distributions(ensemble, observed=Q_obs)
>>> plot_monthly_distributions(ensemble, plot_type='violin')

Correlation

plot_autocorrelation

plot_autocorrelation(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, max_lag: int = 30, timestep: str = 'daily', show_members: Optional[int] = None, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot autocorrelation function with ensemble uncertainty.

Shows temporal autocorrelation structure and how well ensemble preserves it compared to observed data.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to plot. If None, uses first site in ensemble

None
max_lag int

Maximum lag to compute

30
timestep str

Temporal resolution for lag calculation: 'daily', 'weekly', 'monthly'

'daily'
show_members int

Number of individual member ACFs to show with transparency

None
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses lag with timestep

None
ylabel str

Y-axis label. If None, uses 'Autocorrelation'

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_autocorrelation(ensemble, observed=Q_obs, max_lag=24)
>>> plot_autocorrelation(ensemble, timestep='monthly', show_members=5)

plot_spatial_correlation

plot_spatial_correlation(ensemble: Ensemble, observed: Optional[Union[Series, DataFrame]] = None, realization: Optional[int] = None, timestep: str = 'daily', method: Literal['pearson', 'spearman', 'kendall'] = 'pearson', show_difference: bool = False, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['square_figsize'], title: Optional[str] = None, cmap: str = 'RdBu_r', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, List[plt.Axes]]

Plot spatial correlation heatmap across sites.

Shows correlation matrix between multiple sites for the ensemble (mean across realizations or a single realization) and optionally compares with observed data.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series or DataFrame

Multi-site observed data (sites as columns, time as index). A single-column Series will be promoted to a DataFrame, but spatial correlation requires at least two sites.

None
realization int

Which ensemble realization to plot. If None (default), use the ensemble-mean correlation matrix (mean of per-realization correlations). If int, use that single realization.

None
timestep str

Temporal resolution: 'daily', 'weekly', 'monthly'

'daily'
method (pearson, spearman, kendall)

Correlation method

'pearson'
show_difference bool

Show difference heatmap (synthetic - observed) instead of side-by-side

False
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
cmap str

Colormap for heatmap

'RdBu_r'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to seaborn.heatmap.

{}

Returns:

Name Type Description
fig Figure
axes list of matplotlib.axes.Axes

List of length 1 (single heatmap or difference) or 2 (side-by-side).

Examples:

>>> plot_spatial_correlation(ensemble, observed=Q_obs_multisite)
>>> plot_spatial_correlation(ensemble, show_difference=True)
Notes

This function requires multi-site ensemble data. Single-site ensembles cannot produce meaningful spatial correlation plots.


Drought

plot_drought_characteristics

plot_drought_characteristics(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, x_metric: str = 'magnitude', y_metric: str = 'duration', color_metric: str = 'severity', method: str = 'ssi', window: int = 12, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['square_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, cmap: str = 'viridis_r', filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Scatter plot of drought characteristics.

Shows drought events in 2D space (e.g., magnitude vs duration) with color indicating a third metric (e.g., severity).

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to analyze. If None, uses first site in ensemble

None
x_metric str

Metric for x-axis: 'magnitude', 'duration', 'severity'

'magnitude'
y_metric str

Metric for y-axis: 'magnitude', 'duration', 'severity'

'duration'
color_metric str

Metric for color mapping: 'magnitude', 'duration', 'severity'

'severity'
method str

Drought identification method (currently only 'ssi' supported)

'ssi'
window int

Rolling window size for SSI calculation (months)

12
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses x_metric

None
ylabel str

Y-axis label. If None, uses y_metric

None
legend bool

Whether to display legend

True
cmap str

Colormap for scatter points

'viridis_r'
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.scatter.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_drought_characteristics(ensemble, observed=Q_obs)
>>> plot_drought_characteristics(ensemble, x_metric='duration', y_metric='severity')

plot_ssi_timeseries

plot_ssi_timeseries(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, percentiles: Optional[list] = None, window: int = 12, start_date: Optional[str] = None, end_date: Optional[str] = None, ax: Optional[Axes] = None, figsize: Tuple[float, float] = LAYOUT['default_figsize'], title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, legend: bool = True, grid: bool = True, filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, plt.Axes]

Plot SSI timeseries with drought period shading.

Shows Standardized Streamflow Index over time with shaded regions indicating drought severity levels.

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for comparison

None
site str

Site name to analyze. If None, uses first site in ensemble

None
percentiles list

Percentiles for ensemble uncertainty bands. Default [10, 50, 90].

None
window int

Rolling window size for SSI calculation (months)

12
start_date str

Start date for plot

None
end_date str

End date for plot

None
ax Axes

Existing axes to plot on. If None, creates new figure

None
figsize tuple

Figure size (width, height) in inches

from config
title str

Plot title. If None, auto-generates

None
xlabel str

X-axis label. If None, uses 'Date'

None
ylabel str

Y-axis label. If None, uses 'SSI'

None
legend bool

Whether to display legend

True
grid bool

Whether to display grid

True
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Forwarded to ax.plot and ax.fill_between.

{}

Returns:

Name Type Description
fig Figure
ax Axes

Examples:

>>> plot_ssi_timeseries(ensemble, observed=Q_obs, window=12)
>>> plot_ssi_timeseries(ensemble, start_date='2000-01-01', end_date='2010-12-31')

Validation

plot_validation_panel

plot_validation_panel(ensemble: Ensemble, observed: Optional[Series] = None, site: Optional[str] = None, timestep: str = 'monthly', log_space: bool = False, figsize: Tuple[float, float] = LAYOUT['validation_figsize'], filename: Optional[str] = None, dpi: int = LAYOUT['save_dpi'], **kwargs) -> Tuple[plt.Figure, List[plt.Axes]]

Multi-panel validation plot (EXCEPTION to single-panel rule).

Creates 5-panel figure with comprehensive statistical validation: 1. Overall distributions (boxplots by month) 2. Monthly mean comparison 3. Monthly std comparison 4. Wilcoxon test p-values 5. Levene test p-values

Parameters:

Name Type Description Default
ensemble Ensemble

Ensemble object containing synthetic data

required
observed Series

Observed timeseries for validation. Panels 4 and 5 (Wilcoxon and Levene tests) require observed data and will display a placeholder message when it is not provided.

None
site str

Site name to analyze. If None, uses first site in ensemble

None
timestep str

Temporal aggregation for validation: 'monthly' or 'weekly'

'monthly'
log_space bool

Perform validation in log space

False
figsize tuple

Figure size (width, height) in inches

from config
filename str

Path to save figure

None
dpi int

Resolution for saved figure

from config
**kwargs

Reserved for future expansion; currently unused.

{}

Returns:

Name Type Description
fig Figure
axes list of matplotlib.axes.Axes

List of 5 axes objects.

Examples:

>>> fig, axes = plot_validation_panel(ensemble, observed=Q_obs)
>>> fig, axes = plot_validation_panel(ensemble, Q_obs, log_space=True)
Notes

This is the only multi-panel function in the plotting module, preserved from the original codebase due to its comprehensive validation capabilities.


Configuration

The configuration dictionaries are imported directly from synhydro.plotting. Mutate them in place and then call apply_plotting_style() to push the changes into matplotlib's rcParams.

apply_plotting_style

apply_plotting_style()

Apply global matplotlib style settings for SynHydro plots.


Helpers

warn_if_many_realizations

warn_if_many_realizations(n: int, *, threshold: int = 1000, context: str = '') -> None

Log a warning when the ensemble has more realizations than threshold.

Functions that iterate per-realization or render per-realization traces can become slow for large N; this surfaces the cost in the log so the user knows what's happening.

Parameters:

Name Type Description Default
n int

Number of realizations.

required
threshold int

Warn when n exceeds this value.

1000
context str

Short label appended to the warning (e.g. "show_members").

''

warn_if_few_realizations

warn_if_few_realizations(n: int, *, threshold: int = 30, context: str = 'percentile bands') -> None

Log a warning when ensemble is too small for stable percentile estimates.

Parameters:

Name Type Description Default
n int

Number of realizations.

required
threshold int

Warn when n is below this value.

30
context str

Short label for the warning.

'percentile bands'