Skip to content

Report CSV columns

generate_catchment_report writes four CSVs beside the HTML report. These files are the default user interface: dates are named explicitly, the monthly table contains the flags needed to filter a timeline, and the hydrological-year table contains the three boundary markers and their quality signals. Internal condition-model and detector diagnostics are deliberately not repeated in every row of the default CSVs.

The complete diagnostic frames are available in Python as result.analysis.monthly and result.analysis.hydro_years (see the diagnostic sections below).

For monthly WOfS acquired through the default high-level workflow, the fixed scientific footprint is (DEA Multi-Year count_wet > 0) AND user AOI. n_aoi is that historical mask's constant pixel count, invalid_pct = 100 * n_invalid / n_aoi, and pixels outside the mask do not affect quality percentages. extent_pct = 100 * n_water / n_valid uses valid observations inside the same mask. The separate planning superset affects only remote reads. Date and event selection remains percentage-based, and no area, pixel-area, or km2 columns are added.

Monthly timeline (<stem>_monthly.csv)

One row is retained for every input month. date is the first day of the month; percentages are 0--100.

Column Meaning
date Month represented by the row.
extent_pct Observed surface-water extent percentage.
invalid_pct Percentage of the fixed historical-mask pixels that were invalid in that month; invalid pixels outside the mask are excluded.
max_invalid_pct User-configurable per-month invalid-pixel limit over the historical-mask denominator, used when determining usable_month.
baseline_extent_pct Record baseline extent used by both wet-event and low-spell detection.
usable_month Whether the month is admitted by the configured quality policy. In the review-oriented flag workflow, finite partial-invalid months remain usable and are flagged by quality_state.
quality_state Quality label for the month (usable, low, missing, or unknown).
hy_year Hydrological-year identifier, blank when the selected route does not define years.
confidence Hydrological-year confidence level (high, medium, low), blank when outside a resolved hydrological year.
phase Phase label according to the selected phase_scheme (two_phase [default]: rising/receding, split at the observed peak; none: unspecified).
phase_status Phase provenance (ok, provisional, unresolved_cycle, outside_cycle, unusable, or disabled).
is_hy_peak True for the detected annual maximum month.
is_hy_mid_dry True for the temporal mid-dry marker.
is_hy_trough True for the detected annual minimum/trough month.
in_wet_event / wet_event_id Whether the month belongs to a wet event and its identifier.
in_low_spell / low_spell_id Whether the month belongs to a low-extent spell and its identifier.
regime / route The regime decision and analysis route applied to the record.
rainfall_mm / rain_anomaly_mm Optional supplied-CSV or SILO rainfall context, written only when rainfall loads successfully. The anomaly is rainfall minus the median for the same calendar month. These fields never drive regime routing, boundaries, phases, events, or low spells.

Hydrological years (<stem>_hydro_years.csv)

This file is header-only for event_characterisation and other routes that do not define hydrological years. Date columns are month starts.

Column Meaning
catchment User-supplied AOI name, or HydroSeason results when the name is blank.
hy_year Hydrological-year identifier.
start_date / end_date Closed interval used for that hydrological year.
peak_date / mid_dry_date / trough_date Wet maximum, mid-dry, and dry minimum markers.
peak_extent_pct / mid_dry_extent_pct / trough_extent_pct Extent observed at each marker.
peak_invalid_pct / mid_dry_invalid_pct / trough_invalid_pct Invalid-pixel percentage at each marker. High values make the marker provisional/low confidence.
drawdown_pct Peak-to-trough extent range when available.
annual_condition Combined recharge (peak) and refuge (trough) condition relative to the record's own confirmed cycles; insufficient_baseline when too few confirmed, point-timed cycles exist to form a baseline.
confidence Deterministic quality grade (high, medium, low) assigned based on data completeness and observation flags. This is an empirical quality grade, not a probability.
status / boundary_status Result status and whether boundaries are exact, provisional, or otherwise constrained.
peak_quality The peak observation judged against its own month-of-year norm: normal or anomalous. Only anomalous downgrades the cycle.
boundary_basis Whether the boundary was detected per year or imposed from a fixed window derived from mean monthly extent.
regime / route Record-level routing metadata.
timing_status Aggregate timing identifiability for the row (point, interval, or unresolved): the weaker of peak_timing_status and trough_timing_status. boundary_status describes selection/data admissibility; timing_status describes temporal identifiability -- the two are independent.
peak_timing_status / trough_timing_status Whether the peak/trough resolves to an exact month (point), a bounded interval (interval), or cannot be resolved (unresolved).
peak_date / trough_date Populated only when the corresponding *_timing_status is point; blank for interval or unresolved so a broad plateau or diffuse peak is never presented as a fabricated exact date.
trough_boundary_date The operational boundary used for cycle segmentation, populated whenever a trough was detected regardless of timing status (blank only for a blank cycle with no trough opportunity at all). This is always trough_month, the actual date used to define hy_start/hy_end -- it is not the same as trough_interval_end_date: a refined boundary can sit strictly inside its own support interval, so the two must not be assumed equal. Use trough_date for a strict point-only claim and trough_boundary_date for "what date this cycle actually uses".
peak_interval_start_date / peak_interval_end_date / trough_interval_start_date / trough_interval_end_date Bounds of equivalent evidence for that extremum across the cycle window (or robust-loss profile support interval for refined troughs). Populated for point and interval; also populated for unresolved when candidate extrema exist to preserve complete equivalent evidence. These are deterministic non-parametric support bounds, not nominal confidence intervals or probability distributions.
detectability_floor_pp The record's detectability floor for that cycle, in percentage points: max(measurement_tolerance_pct, robust_noise_pp, peak_resolution_pp, trough_resolution_pp, machine epsilon).
amplitude_to_floor_ratio That cycle's amplitude divided by detectability_floor_pp; 0.0 when the amplitude is at or below the floor.

Trough search geometry diagnostics

These six columns are on result.analysis.hydro_years, not in the CSV. They describe how a boundary was found and never take part in finding one.

Column Meaning
trough_search_radius_used Radius in months actually used for this year's window. Equals the configured trough_search_radius_months unless the adaptive retry widened this year.
boundary_at_search_edge The published boundary sits exactly at an edge month of its own window. A warning that a lower continuation may lie outside; not a defect.
boundary_search_edge_side left, right, or none.
outside_window_observed Every month of the outside-audit span was present in the prepared frame. The span reaches at most five months from the anchor — the radius the adaptive retry can already use — so it never names a month the detector could not have selected.
outside_window_lower A strictly lower raw observed extent exists in that span.
retry_outcome not_attempted, applied, or rolled_back.

outside_window_lower is a challenge count, not a clipping rate. It states that a lower raw value was observed outside the window. It does not state that the outside month is the correct boundary: a wider search can select a competing event or damage cycle geometry. Confirming a challenge needs per-cycle truth, which no real record supplies.

The two derived rates have different denominators and must not be compared: boundary_search_edge_rate is over published boundaries, and outside_window_lower_rate is over boundaries with outside_window_observed. Both publish numerator, denominator, and an interval. Within one catchment that interval is Wilson and is labelled an understatement, because cycles inside one catchment are serially dependent; across a cohort it is a catchment-level bootstrap.

Trough refinement diagnostics

Seasonal boundaries are found in two passes: pass 1 picks a robust trough in each year's search window, and pass 2 refines it with the direct-profile Huber fit of hydroseason-v0.2.0. These columns record what pass 2 found. They are on result.analysis.hydro_years, not in the CSV.

Column Meaning
pass1_trough_month / pass1_trough_interval_start / pass1_trough_interval_end / pass1_trough_timing_status The pass-1 boundary and its timing status, retained unchanged so any refinement can be audited against what it replaced.
trough_challenger_month / trough_challenger_interval_start / trough_challenger_interval_end / trough_challenger_timing_status What pass 2 proposed. Populated even when the pass-1 boundary is kept, so the proposal stays auditable.
trough_challenger_low_state_start / trough_challenger_low_state_end The fitted low-state occupancy span. Distinct from endpoint uncertainty: a long flat low is not the same claim as an imprecisely located one.
recovery_start_month First month of sustained recovery out of the low state.
trough_refinement_status confirmed, provisional, unresolved, unavailable, or awaiting_next_peak.
trough_refinement_reason Why that status was reached, e.g. accepted, low_quality_peak, interval_peak, open_span, missing_or_unresolved_peak, recovery_crosses_gap, gap_overlaps_low_state, gap_before_low_state (a fully-observed post-gap month sits materially below the pre-gap fitted low level, so the low state is not confined to before the gap and the pre-gap segment cannot answer for it), unstable_peak_sensitivity, unstable_quality_sensitivity, boundary_deferred_to_reliable_month (the naive latest support-cluster/pre-gap month was too cloud-contaminated to publish, so the boundary moved to the latest reliable month instead), boundary_deferred_to_implausible_month (the naive latest pre-gap month was reliable-quality but its raw value sat outside the low state's own equivalence band -- a real spike immediately before a data gap -- so the boundary moved to the latest month that was both reliable and plausible), no_reliable_boundary_in_support (no month in the support cluster was reliable and plausible enough to publish at all), recovery_within_noise (the month after the boundary clears the equivalence margin, so the boundary stands, but it still falls inside the record's own noise scale -- the call is real but finer than the observation comfortably supports, so it is not reported as confirmed), and span_not_evaluable (the span's geometry left nothing to fit -- more than one gap, or a gap against a span edge; a sensitivity-ensemble scenario carrying this is excluded from the stability vote rather than counted as dissent, since it reports nothing about where the boundary lies).
trough_refinement_applied Whether the pass-2 boundary was adopted.
trough_pulse_months Months detected as rewetting pulses inside the span.
trough_local_scale_pp / trough_profile_best_loss / trough_profile_cutoff / trough_effective_support Fit diagnostics: local noise scale, achieved loss, the frozen admissibility cutoff, and effective observation support.
trough_refinement_policy_version The frozen policy version that produced these values.
trough_loss_basis Which loss the fit actually used: standardized_huber when a positive scale (residual noise, pixel floor, or an explicit measurement tolerance) was available, or exact_l1 when no positive scale exists at all. exact_l1 reports a deterministic exact-minimum support set up to numerical tolerance -- it carries no claimed confidence level; it is not a calibrated interval and must not be read as one. unavailable when pass 2 did not run.

unavailable and unresolved are different claims and are never collapsed. unavailable means pass 2 could not run for this cycle (for example missing_or_unresolved_peak); unresolved means it ran and the observations did not support a refined boundary. Reading the first as the second would turn "not attempted" into "attempted and found nothing".

Applying a refinement is atomic across the two cycles that share the moved boundary: either both are updated or neither is, so a boundary can never be adopted on one side and not the other.

Wet events (<stem>_wet_event.csv)

Wet events are contiguous runs above a robust, record-specific wet threshold. The default detector uses a noise-based threshold with hysteresis: an event opens above baseline + 3 × noise and remains open while above baseline + 1 × noise. Unusable months close an event. The event table is descriptive and is produced for every route, including aseasonal records.

Column Meaning
event_id One-based event identifier.
start_date / end_date First and last month in the event.
duration_months Number of contiguous event months.
baseline_extent_pct Record baseline extent used by the wet-event detector.
peak_date / peak_extent_pct Month and extent of the event maximum.
mean_extent_pct Mean extent during the event.
magnitude_pp_months Sum of (extent_pct - event_exit_threshold) for event months, clipped at zero. It measures event size and persistence in percentage-points × months; it is not a volume or discharge estimate.

Low-extent spells (<stem>_low_spells.csv)

Low spells are independent of wet events. They are contiguous runs at or below baseline - 1 × noise (or the configured quantile fallback), with a default minimum duration of two months. They describe unusually low extent in this record; they are not automatically a drought declaration.

Column Meaning
low_spell_id One-based low-spell identifier.
start_date / end_date First and last month in the spell.
duration_months Number of contiguous low-extent months.
baseline_extent_pct Record baseline extent used by the low-spell detector.
min_extent_pct Minimum extent observed during the spell.

Summary information

The HTML report's internal one-row summary (also returned by CatchmentAnalysis.summary_row()) includes the timing fields below. They are not repeated in the compact four-CSV bundle, so downstream code that needs them should use the analysis result or a full summary export.

Field Units / range Null / zero semantics
amplitude_snr Unitless, >=0 (possibly inf) 0.0 for insufficient records. Descriptive only; does not set the regime.
peak_timing_concentration, trough_timing_concentration Mean resultant length, 0–1 null for insufficient records.
peak_timing_concentration_ci_low, peak_timing_concentration_ci_high, trough_timing_concentration_ci_low, trough_timing_concentration_ci_high Unitless 0–1, 95% bootstrap bounds null for insufficient records.
peak_timing_uniformity_p, trough_timing_uniformity_p Kuiper probability 0–1 null for insufficient records.
peak_phase_iqr_months, trough_phase_iqr_months Circular months null with fewer than four timing observations or an insufficient record; IQR is descriptive only.
n_timing_years Non-negative integer years 0 for insufficient records; it is not a count of months. Equals n_peak_timing_years -- it keeps its historical peak-derived meaning and is never silently redefined as a minimum.
n_peak_timing_years, n_trough_timing_years Non-negative integer years Count of calendar years whose peak/trough extremum is independently identifiable (not unresolved). The conservative min() of the two is used only in the route gate, and is not itself a published field.
n_zero_months Non-negative integer months Total months with exact-zero observed extent among usable months. Descriptive only; it never enters a route or timing decision.
zero_month_fraction Unitless, 0–1 Fraction of usable months that are exact zero.
n_whole_zero_years Non-negative integer years Count of years whose usable months are all exact zero. A whole-zero year still contributes to dry-duration and event summaries; it contributes no peak or trough timing observation.
pixel_support_status "available" or "unavailable" Whether the record carries pixel counts (n_water/n_valid/n_invalid/n_aoi). Percentage-only inputs always report "unavailable", and their min_peak_water_pixels threshold is not consulted.
timing_evidence "supported", "insufficient", or "unsupported" Record-level timing verdict: insufficient when min(n_peak_timing_years, n_trough_timing_years) < min_informative_years; unsupported when the established seasonality/uniformity evidence rejects an annual cycle (today this is reachable only when regime == "aseasonal"); otherwise supported.
mean_monthly_peak_month, mean_monthly_trough_month Calendar month 1–12 Month of the maximum/minimum of mean monthly extent over qualifying years. null unless the record is routed with identifiable annual timing.

R and confidence intervals are rounded to three decimal places in the summary; IQR is rounded to two decimal places. The report uses peak R for regime evidence and trough R for per-year boundary support. It does not use IQR as a hidden decision threshold.

max_invalid_pct is always a configurable per-month invalid-pixel threshold over the historical-mask denominator, not a percentage of invalid months.

AOI naming

The name argument is optional. Use it for a catchment name, station name, or any label that identifies a custom AOI. It does not have to be a whole catchment. If it is omitted or blank, reports and metadata use HydroSeason results, and files use the safe stem hydroseason-results.