The regime pass looks for relationships whose linear equation changes across subsets of rows. It fits a model tree with linear models in its leaves, using an M5-style tree after Quinlan and smoothing after Wang & Witten. It reports local equations when they improve on a single global line on held-out rows. A separate residual-level probe checks whether the residuals of a global linear model shift along the first date column.
When it runs¶
Numeric targets are shortlisted by
the predictability screen, and at most 4 targets (8 on deep) are
fitted, over at most 5,000 rows (10,000) with at most 8 numeric
predictors (10), to depth 3 (4). A 1/5 holdout partition — the stable
row-id hash shared with the rule evaluator — must contain at least 60
rows, or the target is skipped. Only numeric predictors participate; the
first date column, if any, is used for ordering and for the residual
probe.
The model tree¶
Training features and targets are standardized so the stopping rules do not depend on units. Reported equations are converted back to original units. Splits maximize standard-deviation reduction over coarse quantile candidates, followed by local refinement. Each leaf’s linear model is fitted using Householder QR decomposition. Corrected-error pruning removes branches whose fit improvement does not justify their complexity. Ancestor smoothing shrinks leaf models toward their parents, following M5’, to reduce instability in small segments.
The finding reports leaf conditions, intercepts, coefficients, support,
and holdout R². It also reports the holdout R² of one global linear model
on the same rows. A finding requires holdout R² ≥ 0.7 and at least a 10%
error reduction relative to the global line. Its statement is
dependencies/predictive with form piecewise_linear.
Residual level shifts¶
For the global linear model fitted to each regime target, the pass orders the training residuals by the first date column and scans for one breakpoint — a centered cumulative-sum scan that finds the single split minimizing the sum of squared deviations from each segment’s mean residual. A fixed correction (before-breakpoint rows shift by one offset, after-breakpoint rows by another) is then evaluated on the holdout: it must reduce holdout squared error, and the reduction is the effect the finding reports.
The statement is changes/residual_level. It records the breakpoint date,
both offsets, the error reduction, the train/holdout sizes, and the
reference model. The reference model includes the intercept and
coefficients needed to reproduce residuals from the input columns.
The finding measures a level change under that model and includes no
p-value.
import numpy as np
import polars as pl
import onceover
rng = np.random.default_rng(41)
n = 6_000
x = rng.uniform(0, 10, n)
y = np.where(x < 5, x, 3 * x - 10) + rng.normal(0, 0.15, n) # slope changes at x = 5
df = pl.DataFrame({"x": x, "y": y,
"noise": rng.random(n)})
report = onceover.profile(df)
piecewise = [f for f in report.findings
if f["specification"].get("form") == "piecewise_linear"
and f["subject"].get("target") == "y"]
print("piecewise findings for y:", len(piecewise))
for f in piecewise:
print(f["claim"])
for leaf in f["evidence"][0]["metrics"]["leaves"]:
conds = " and ".join(f"{c['column']} {c['operator']} {c['threshold']:.3f}"
for c in leaf["conditions"])
coefs = " + ".join(f"{v:.3f}·{k}" for k, v in leaf["coefficients"].items())
print(f" when {conds}:")
print(f" y ≈ {leaf['intercept']:.3f} + {coefs} (n_train={leaf['n_train']}, n_holdout={leaf['n_holdout']})")piecewise findings for y: 1
y follows 4 local linear regimes (first split x ≤ 6.082270; holdout R² 0.999 versus 0.940 for one linear model)
when x <= 6.082 and x <= 3.329:
y ≈ -0.001 + -0.016·noise + 1.002·x (n_train=1317, n_holdout=343)
when x <= 6.082 and x > 3.329 and x <= 5.187:
y ≈ -0.322 + 0.011·noise + 1.086·x (n_train=713, n_holdout=185)
when x <= 6.082 and x > 3.329 and x > 5.187:
y ≈ -9.378 + 0.001·noise + 2.889·x (n_train=373, n_holdout=86)
when x > 6.082:
y ≈ -9.916 + 0.005·noise + 2.988·x (n_train=1597, n_holdout=386)
In this example, the tree finds four local segments around the planted breakpoint. Each has an equation in original units and train/holdout support counts. Slopes are near one below the change and near three above it. The tree is compared with a single global line on the same holdout rows, and a regime finding requires the specified improvement in error.