Skip to content

Results

rucola.DetectionResult(station_detections, parameter, mode='ratio') dataclass

Raw output of Rucola.run() — all test results before normalization.

summary property

Per-station detection summary.

Columns: station_id, group, n_steps_tested, n_significant, n_applied. Sorted by n_significant descending.

__repr__()

Return short summary string.

Source code in src/rucola/_results.py
def __repr__(self) -> str:
    """Return short summary string."""
    n = len(self.station_detections)
    total = sum(len(sd.corrections) for sd in self.station_detections.values())
    return f"DetectionResult(parameter={self.parameter!r}, stations={n}, corrections_applied={total})"

from_dict(d) classmethod

Deserialise from a dict produced by to_dict.

Source code in src/rucola/_results.py
@classmethod
def from_dict(cls, d: dict) -> DetectionResult:
    """Deserialise from a dict produced by to_dict."""
    return cls(
        parameter=d["parameter"],
        mode=d.get("mode", "ratio"),
        station_detections={sid: StationDetection.from_dict(sd) for sid, sd in d["station_detections"].items()},
    )

from_json(path) classmethod

Load from a JSON file written by to_json.

Source code in src/rucola/_results.py
@classmethod
def from_json(cls, path: str | Path) -> DetectionResult:
    """Load from a JSON file written by to_json."""
    return cls.from_dict(json.loads(Path(path).read_text()))

normalize(config=None)

Apply corrections based on config and return a HomogenizationResult.

Parameters:

Name Type Description Default
config NormalizationConfig | None

Normalization configuration. Defaults to NormalizationConfig() if not provided.

None
Source code in src/rucola/_results.py
def normalize(self, config: NormalizationConfig | None = None) -> HomogenizationResult:
    """Apply corrections based on config and return a HomogenizationResult.

    Parameters
    ----------
    config :
        Normalization configuration. Defaults to NormalizationConfig() if not provided.

    """
    from rucola._normalization import NormalizationConfig as _Cfg  # noqa: PLC0415
    from rucola._normalization import Normalizer  # noqa: PLC0415

    return Normalizer(config or _Cfg()).apply(self)

to_dict()

Serialise to a JSON-compatible dict.

Source code in src/rucola/_results.py
def to_dict(self) -> dict:
    """Serialise to a JSON-compatible dict."""
    return {
        "_version": 1,
        "parameter": self.parameter,
        "mode": self.mode,
        "station_detections": {sid: sd.to_dict() for sid, sd in self.station_detections.items()},
    }

to_json(path)

Write to a JSON file at path.

Source code in src/rucola/_results.py
def to_json(self, path: str | Path) -> None:
    """Write to a JSON file at path."""
    Path(path).write_text(json.dumps(self.to_dict()))

to_markdown()

Render detection results as a Markdown document.

Produces a summary table followed by a per-station section. Each station section contains a step overview and — when multiple tests are run or tests disagree on the break year — a per-step detail table showing each test's result individually.

Source code in src/rucola/_results.py
def to_markdown(self) -> str:  # noqa: C901
    """Render detection results as a Markdown document.

    Produces a summary table followed by a per-station section. Each
    station section contains a step overview and — when multiple tests are
    run or tests disagree on the break year — a per-step detail table
    showing each test's result individually.
    """

    def _step_label(step: int) -> str:
        return {61: "6a", 62: "6b", 63: "6c"}.get(step, str(step))

    def _row(*cells: str) -> str:
        return "| " + " | ".join(cells) + " |"

    lines: list[str] = [
        f"# Detection Results — {self.parameter}",
        "",
        "## Summary",
        "",
        _row("station_id", "group", "steps_tested", "n_significant", "breaks_applied"),
        _row(*["---"] * 5),
    ]

    ordered = sorted(
        self.station_detections.items(),
        key=lambda x: len(x[1].corrections),
        reverse=True,
    )

    for sid, det in ordered:
        n_sig = sum(1 for rec in det.detections_by_step.values() for tr in rec.test_results if tr.is_significant)
        lines.append(
            _row(
                sid,
                det.group or "—",
                str(len(det.detections_by_step)),
                str(n_sig),
                str(len(det.corrections)),
            )
        )

    lines += ["", "---", ""]

    for sid, det in ordered:
        if not det.detections_by_step:
            continue

        lines.append(f"## {sid}  ·  {det.group or '—'}")
        lines.append("")
        lines += [
            _row("step", "break_year", "applied"),
            _row(*["---"] * 3),
        ]
        for step, rec in sorted(det.detections_by_step.items()):
            year_str = "—" if rec.break_year is None else str(rec.break_year)
            lines.append(_row(_step_label(step), year_str, "yes" if rec.was_applied else "no"))
        lines.append("")

        for step, rec in sorted(det.detections_by_step.items()):
            if not rec.test_results:
                continue

            break_years = {tr.break_year for tr in rec.test_results if tr.is_significant}
            status = "applied" if rec.was_applied else "not applied"
            heading = f"### Step {_step_label(step)} · {status}"
            if len(break_years) > 1:
                heading += f" · ⚠ year disagreement {sorted(break_years)}"
            lines += [heading, ""]
            lines += [
                _row("test", "significant", "break_year", "relative_signal"),
                _row(*["---"] * 4),
            ]
            for tr in rec.test_results:
                sig = "**yes**" if tr.is_significant else "no"
                lines.append(_row(tr.test_name, sig, str(tr.break_year), f"{tr.relative_signal:.3f}"))
            lines.append("")

    return "\n".join(lines)

rucola.HomogenizationResult(station_results, parameter) dataclass

Full 6-step homogenization result for all tested stations.

corrections property

All applied corrections across all stations, flat DataFrame.

Columns: station_id, step, break_year, factor.

group_counts property

Count of stations per final group label.

summary property

One row per tested station.

Sorted by n_corrections descending. Columns: station_id, group, n_corrections, break_years, n_neighbors (from last step used).

from_dict(d) classmethod

Deserialise from a dict produced by to_dict.

Source code in src/rucola/_results.py
@classmethod
def from_dict(cls, d: dict) -> HomogenizationResult:
    """Deserialise from a dict produced by to_dict."""
    return cls(
        parameter=d["parameter"],
        station_results={sid: StationResult.from_dict(sr) for sid, sr in d["station_results"].items()},
    )

from_json(path) classmethod

Load from a JSON file written by to_json.

Source code in src/rucola/_results.py
@classmethod
def from_json(cls, path: str | Path) -> HomogenizationResult:
    """Load from a JSON file written by to_json."""
    return cls.from_dict(json.loads(Path(path).read_text()))

to_dict()

Serialise to a JSON-compatible dict.

Source code in src/rucola/_results.py
def to_dict(self) -> dict:
    """Serialise to a JSON-compatible dict."""
    return {
        "_version": 1,
        "parameter": self.parameter,
        "station_results": {sid: sr.to_dict() for sid, sr in self.station_results.items()},
    }

to_json(path)

Write to a JSON file at path.

Source code in src/rucola/_results.py
def to_json(self, path: str | Path) -> None:
    """Write to a JSON file at path."""
    Path(path).write_text(json.dumps(self.to_dict()))

rucola.StationDetection(station_id, group, annual_original, annual_corrected, years, detections_by_step=dict(), neighbors_by_step=dict(), corrections=list()) dataclass

Raw detection data for one station across all procedure steps.

from_dict(d) classmethod

Deserialise from a dict produced by to_dict.

Source code in src/rucola/_results.py
@classmethod
def from_dict(cls, d: dict) -> StationDetection:
    """Deserialise from a dict produced by to_dict."""
    from rucola._algorithms import NeighborInfo  # noqa: PLC0415

    return cls(
        station_id=d["station_id"],
        group=d["group"],
        annual_original=pl.Series(d["station_id"], d["annual_original"]),
        annual_corrected=pl.Series(d["station_id"], d["annual_corrected"]),
        years=pl.Series("year", d["years"]),
        detections_by_step={
            int(k): DetectionRecord.from_dict(v) for k, v in d.get("detections_by_step", {}).items()
        },
        neighbors_by_step={
            int(k): [NeighborInfo(**n) for n in v] for k, v in d.get("neighbors_by_step", {}).items()
        },
        corrections=[CorrectionRecord(**c) for c in d.get("corrections", [])],
    )

to_dict()

Serialise to a JSON-compatible dict.

Source code in src/rucola/_results.py
def to_dict(self) -> dict:
    """Serialise to a JSON-compatible dict."""
    return {
        "station_id": self.station_id,
        "group": self.group,
        "annual_original": self.annual_original.to_list(),
        "annual_corrected": self.annual_corrected.to_list(),
        "years": self.years.to_list(),
        "detections_by_step": {str(k): v.to_dict() for k, v in self.detections_by_step.items()},
        "neighbors_by_step": {
            str(k): [
                {
                    "station_id": n.station_id,
                    "distance_km": n.distance_km,
                    "correlation": n.correlation,
                    "weight": n.weight,
                }
                for n in v
            ]
            for k, v in self.neighbors_by_step.items()
        },
        "corrections": [{"step": c.step, "break_year": c.break_year, "factor": c.factor} for c in self.corrections],
    }

rucola.StationResult(station_id, group, corrections, neighbors_by_step, annual_original, annual_corrected, years) dataclass

Full homogenization result for one station.

from_dict(d) classmethod

Deserialise from a dict produced by to_dict.

Source code in src/rucola/_results.py
@classmethod
def from_dict(cls, d: dict) -> StationResult:
    """Deserialise from a dict produced by to_dict."""
    from rucola._algorithms import NeighborInfo  # noqa: PLC0415

    sid = d["station_id"]
    return cls(
        station_id=sid,
        group=d["group"],
        corrections=[CorrectionRecord(**c) for c in d.get("corrections", [])],
        neighbors_by_step={
            int(k): [NeighborInfo(**n) for n in v] for k, v in d.get("neighbors_by_step", {}).items()
        },
        annual_original=pl.Series(sid, d["annual_original"]),
        annual_corrected=pl.Series(sid, d["annual_corrected"]),
        years=pl.Series("year", d["years"]),
    )

to_dict()

Serialise to a JSON-compatible dict.

Source code in src/rucola/_results.py
def to_dict(self) -> dict:
    """Serialise to a JSON-compatible dict."""
    return {
        "station_id": self.station_id,
        "group": self.group,
        "corrections": [{"step": c.step, "break_year": c.break_year, "factor": c.factor} for c in self.corrections],
        "neighbors_by_step": {
            str(k): [
                {
                    "station_id": n.station_id,
                    "distance_km": n.distance_km,
                    "correlation": n.correlation,
                    "weight": n.weight,
                }
                for n in v
            ]
            for k, v in self.neighbors_by_step.items()
        },
        "annual_original": self.annual_original.to_list(),
        "annual_corrected": self.annual_corrected.to_list(),
        "years": self.years.to_list(),
    }