Skip to content

Commit ad178a9

Browse files
authored
docs: clarify DiD predict support contract (#580)
1 parent 0251fee commit ad178a9

3 files changed

Lines changed: 37 additions & 7 deletions

File tree

diff_diff/estimators.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -999,26 +999,39 @@ def _validate_data(
999999

10001000
def predict(self, data: pd.DataFrame) -> np.ndarray:
10011001
"""
1002-
Predict outcomes using fitted model.
1002+
Predict outcomes using the fitted model.
1003+
1004+
Out-of-sample prediction is intentionally unsupported pending a broader
1005+
post-estimation design for estimator result objects. For fitted
1006+
training-data predictions, use ``results_.fitted_values`` after
1007+
:meth:`fit`.
10031008
10041009
Parameters
10051010
----------
10061011
data : pd.DataFrame
1007-
DataFrame with same structure as training data.
1012+
Candidate prediction data. Currently unused because out-of-sample
1013+
prediction is unsupported.
10081014
10091015
Returns
10101016
-------
10111017
np.ndarray
10121018
Predicted values.
1019+
1020+
Raises
1021+
------
1022+
RuntimeError
1023+
If called before :meth:`fit`.
1024+
NotImplementedError
1025+
Always raised after fitting until the broader post-estimation
1026+
prediction contract is designed.
10131027
"""
10141028
if not self.is_fitted_:
10151029
raise RuntimeError("Model must be fitted before calling predict()")
10161030

1017-
# This is a placeholder - would need to store column names
1018-
# for full implementation
10191031
raise NotImplementedError(
1020-
"predict() is not yet implemented. "
1021-
"Use results_.fitted_values for training data predictions."
1032+
"out-of-sample predict() is unsupported pending a broader "
1033+
"post-estimation design. Use results_.fitted_values for fitted "
1034+
"training-data predictions."
10221035
)
10231036

10241037
def get_params(self) -> Dict[str, Any]:

docs/api/estimators.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ DifferenceInDifferences (alias: ``DiD``)
3030

3131
Basic 2x2 DiD estimator.
3232

33+
``DifferenceInDifferences.predict()`` is present for sklearn-like
34+
discoverability, but out-of-sample prediction is not currently supported. Use
35+
``results_.fitted_values`` for fitted training-data predictions until a broader
36+
post-estimation result-object contract is designed.
37+
3338
.. autoclass:: diff_diff.DifferenceInDifferences
3439
:no-index:
3540
:members:
@@ -42,6 +47,7 @@ Basic 2x2 DiD estimator.
4247
.. autosummary::
4348

4449
~DifferenceInDifferences.fit
50+
~DifferenceInDifferences.predict
4551
~DifferenceInDifferences.get_params
4652
~DifferenceInDifferences.set_params
4753

@@ -84,4 +90,3 @@ Synthetic control combined with DiD (Arkhangelsky et al. 2021).
8490
:undoc-members:
8591
:show-inheritance:
8692
:inherited-members:
87-

tests/test_methodology_did.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,18 @@ def test_residuals_and_fitted_values(self):
15501550
assert np.allclose(reconstructed, original), \
15511551
"Residuals + fitted should equal original outcome"
15521552

1553+
def test_predict_contract_points_to_fitted_values(self):
1554+
"""predict() is intentionally unsupported until post-estimation is designed."""
1555+
data, _ = generate_hand_calculable_data()
1556+
1557+
did = DifferenceInDifferences()
1558+
did.fit(data, outcome='outcome', treatment='treated', time='post')
1559+
1560+
with pytest.raises(
1561+
NotImplementedError,
1562+
match="out-of-sample.*post-estimation.*results_\\.fitted_values",
1563+
):
1564+
did.predict(data)
15531565

15541566
# =============================================================================
15551567
# Multi-absorb (N>1 FE) iterative alternating-projection demeaning

0 commit comments

Comments
 (0)