Skip to content

Commit a19a910

Browse files
committed
fix: make MVLR source filtering opt-in
1 parent ce49ff6 commit a19a910

2 files changed

Lines changed: 44 additions & 7 deletions

File tree

openenergyid/mvlr/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ValidationParameters(BaseModel):
3737
class SourceDataFilteringParameters(BaseModel):
3838
"""Parameters for source-data filtering before regression fitting."""
3939

40-
enabled: bool = True
40+
enabled: bool = False
4141
minimum_retained_fraction: float = Field(
4242
0.50,
4343
ge=0,

tests/mvlr/test_source_data_filtering.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def _solar_regression_input(
2323
*,
2424
zero_slice: slice = slice(20, 30),
2525
spikes: dict[int, float] | None = None,
26+
source_data_filtering: dict | None = None,
2627
) -> MultiVariableRegressionInput:
2728
index = pd.date_range("2025-04-01", periods=90, freq="D", tz="Europe/Brussels")
2829
solar_reference = pd.Series(
@@ -65,16 +66,35 @@ def _solar_regression_input(
6566
"f_pvalue": 0.05,
6667
"pvalues": 0.05,
6768
},
69+
"sourceDataFiltering": source_data_filtering or {},
6870
},
6971
)
7072

7173

74+
def test_source_data_filtering_is_disabled_by_default() -> None:
75+
"""Default behavior should preserve legacy MVLR source data."""
76+
data = _solar_regression_input()
77+
frame = data.data_frame()
78+
79+
cleaned, diagnostics = clean_solar_source_frame(frame, DEPENDENT)
80+
81+
assert not data.source_data_filtering.enabled
82+
assert not diagnostics.enabled
83+
assert not diagnostics.applied
84+
assert diagnostics.reason == "source-data filtering disabled"
85+
assert cleaned is frame
86+
87+
7288
def test_clean_regression_frame_removes_solar_source_outliers() -> None:
7389
"""Solar production cleaning should drop zero-line and ratio outliers."""
7490
data = _solar_regression_input()
7591
frame = data.data_frame()
7692

77-
cleaned, diagnostics = clean_solar_source_frame(frame, DEPENDENT)
93+
cleaned, diagnostics = clean_solar_source_frame(
94+
frame,
95+
DEPENDENT,
96+
SourceDataFilteringParameters(enabled=True),
97+
)
7898

7999
assert diagnostics.applied
80100
assert diagnostics.original_observation_count == 90
@@ -87,7 +107,7 @@ def test_clean_regression_frame_removes_solar_source_outliers() -> None:
87107

88108
def test_find_best_mvlr_returns_filtering_diagnostics() -> None:
89109
"""A model should fit after bad source observations are excluded."""
90-
data = _solar_regression_input()
110+
data = _solar_regression_input(source_data_filtering={"enabled": True})
91111

92112
result = find_best_mvlr(data)
93113

@@ -103,7 +123,11 @@ def test_clean_regression_frame_keeps_original_data_when_filtering_too_much() ->
103123
data = _solar_regression_input(zero_slice=slice(0, 55), spikes={})
104124
frame = data.data_frame()
105125

106-
cleaned, diagnostics = clean_solar_source_frame(frame, DEPENDENT)
126+
cleaned, diagnostics = clean_solar_source_frame(
127+
frame,
128+
DEPENDENT,
129+
SourceDataFilteringParameters(enabled=True),
130+
)
107131

108132
assert not diagnostics.applied
109133
assert diagnostics.reason == "too much source data would be removed"
@@ -119,6 +143,7 @@ def test_clean_solar_source_frame_uses_filtering_parameters() -> None:
119143
frame,
120144
DEPENDENT,
121145
SourceDataFilteringParameters(
146+
enabled=True,
122147
minimum_retained_fraction=0.30,
123148
ratio_robust_z_threshold=999.0,
124149
),
@@ -177,7 +202,11 @@ def test_clean_regression_frame_removes_non_finite_rows_for_non_solar_models() -
177202
index=index,
178203
)
179204

180-
cleaned, diagnostics = clean_regression_frame(frame, "energyConsumption")
205+
cleaned, diagnostics = clean_regression_frame(
206+
frame,
207+
"energyConsumption",
208+
SourceDataFilteringParameters(enabled=True),
209+
)
181210

182211
assert diagnostics.applied
183212
assert diagnostics.reason == "generic non-finite filtering only"
@@ -197,9 +226,17 @@ def test_non_finite_cleanup_after_resampling_preserves_aggregate_totals() -> Non
197226
index=index,
198227
)
199228

200-
source_cleaned, source_diagnostics = clean_solar_source_frame(frame, "energyConsumption")
229+
source_cleaned, source_diagnostics = clean_solar_source_frame(
230+
frame,
231+
"energyConsumption",
232+
SourceDataFilteringParameters(enabled=True),
233+
)
201234
resampled = resample_input_data(source_cleaned, Granularity.P1M)
202-
cleaned, diagnostics = clean_regression_frame(resampled, "energyConsumption")
235+
cleaned, diagnostics = clean_regression_frame(
236+
resampled,
237+
"energyConsumption",
238+
SourceDataFilteringParameters(enabled=True),
239+
)
203240

204241
assert not source_diagnostics.applied
205242
assert resampled["energyConsumption"].iloc[0] == 28.0

0 commit comments

Comments
 (0)