Skip to content

Commit e15b144

Browse files
Add PrePostNEGD group-level summary outputs
Add experiment-specific summary statistics for PrePostNEGD by printing group-level sample sizes and pre/post means, and cover the new output in integration tests and the ANCOVA docs notebook. Closes #727 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 473f45e commit e15b144

4 files changed

Lines changed: 75 additions & 16 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# PR: Add group-level outputs to PrePostNEGD summary
2+
3+
Closes #727
4+
5+
## Issue Summary
6+
7+
`PrePostNEGD.summary()` only reported formula, causal impact, and model coefficients. The issue requested experiment-specific outputs to make the summary more informative.
8+
9+
## Root Cause
10+
11+
The summary method had no implementation for experiment-specific diagnostics beyond the generic treatment-effect and coefficient output.
12+
13+
## Solution
14+
15+
Added a group-level descriptive statistics section to the `PrePostNEGD` summary output, including sample size and pre/post means by group, and added a regression test to verify those fields are printed.
16+
17+
## Changes Made
18+
19+
- `causalpy/experiments/prepostnegd.py`: added `_group_level_summary_stats()` and updated `summary()` to print group-level `n`, `pre_mean`, and `post_mean`.
20+
- `causalpy/tests/test_integration_pymc_examples.py`: updated `test_ancova` to capture summary output and assert new experiment-specific lines are present.
21+
22+
## Testing
23+
24+
- [x] Existing tests pass
25+
- [x] New tests added (if applicable)
26+
- [x] Manual verification completed
27+
28+
## Notes
29+
30+
The documentation notebook that displays this summary output and should be re-run is `docs/source/notebooks/ancova_pymc.ipynb`.

causalpy/experiments/prepostnegd.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,23 @@ def _causal_impact_summary_stat(self, round_to: int | None = 2) -> str:
222222
causal_impact = f"{round_num(self.causal_impact.mean(), round_to)}, "
223223
return f"Causal impact = {causal_impact + ci}"
224224

225+
def _group_level_summary_stats(self, round_to: int | None = None) -> pd.DataFrame:
226+
"""Compute group-level sample sizes and pre/post means."""
227+
summary_df = (
228+
self.data.groupby(self.group_variable_name)
229+
.agg(
230+
n=(self.group_variable_name, "size"),
231+
pre_mean=(self.pretreatment_variable_name, "mean"),
232+
post_mean=(self.outcome_variable_name, "mean"),
233+
)
234+
.reset_index()
235+
)
236+
if round_to is not None:
237+
summary_df[["pre_mean", "post_mean"]] = summary_df[
238+
["pre_mean", "post_mean"]
239+
].round(round_to)
240+
return summary_df
241+
225242
def summary(self, round_to: int | None = None) -> None:
226243
"""Print summary of main results and model coefficients.
227244
@@ -230,6 +247,8 @@ def summary(self, round_to: int | None = None) -> None:
230247
"""
231248
print(f"{self.expt_type:=^80}")
232249
print(f"Formula: {self.formula}")
250+
print("\nGroup-level descriptive stats:")
251+
print(self._group_level_summary_stats(round_to).to_string(index=False))
233252
print("\nResults:")
234253
print(self._causal_impact_summary_stat(round_to))
235254
self.print_coefficients(round_to)

causalpy/tests/test_integration_pymc_examples.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ def test_sc_brexit(mock_pymc_sample):
553553

554554

555555
@pytest.mark.integration
556-
def test_ancova(mock_pymc_sample):
556+
def test_ancova(mock_pymc_sample, capsys):
557557
"""
558558
Test Pre-PostNEGD experiment on anova1 data.
559559
@@ -576,6 +576,11 @@ def test_ancova(mock_pymc_sample):
576576
assert len(result.idata.posterior.coords["chain"]) == sample_kwargs["chains"]
577577
assert len(result.idata.posterior.coords["draw"]) == sample_kwargs["draws"]
578578
result.summary()
579+
output = capsys.readouterr().out
580+
assert "Group-level descriptive stats:" in output
581+
assert "n" in output
582+
assert "pre_mean" in output
583+
assert "post_mean" in output
579584
fig, ax = result.plot()
580585
assert isinstance(fig, plt.Figure)
581586
# For multi-panel plots, ax should be an array of axes

docs/source/notebooks/ancova_pymc.ipynb

Lines changed: 20 additions & 15 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)