Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ docs/source/api/generated/

# Temporary files generated by Cursor commands
.github/pr_summaries/
.github/pr-summaries/

# Issue evaluation drafts generated by evaluate_issue command
.github/issue_comments/
.github/issue-comments/
.github/issue_summaries/
.github/issue-summaries/

# Local scratch space (not tracked)
.scratch/
18 changes: 18 additions & 0 deletions causalpy/experiments/prepostnegd.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ def _causal_impact_summary_stat(self, round_to: int | None = 2) -> str:
causal_impact = f"{round_num(self.causal_impact.mean(), round_to)}, "
return f"Causal impact = {causal_impact + ci}"

def _group_level_summary_stats(self, round_to: int | None = None) -> pd.DataFrame:
"""Compute group-level sample sizes and pre/post means."""
summary_df = (
self.data.groupby(self.group_variable_name)
.agg(
n=(self.group_variable_name, "size"),
pre_mean=(self.pretreatment_variable_name, "mean"),
post_mean=(self.outcome_variable_name, "mean"),
)
.reset_index()
)
if round_to is not None:
for col in ["pre_mean", "post_mean"]:
summary_df[col] = summary_df[col].map(lambda x: round_num(x, round_to))
return summary_df

def summary(self, round_to: int | None = None) -> None:
"""Print summary of main results and model coefficients.

Expand All @@ -230,6 +246,8 @@ def summary(self, round_to: int | None = None) -> None:
"""
print(f"{self.expt_type:=^80}")
print(f"Formula: {self.formula}")
print("\nGroup-level descriptive stats:")
print(self._group_level_summary_stats(round_to).to_string(index=False))
print("\nResults:")
print(self._causal_impact_summary_stat(round_to))
self.print_coefficients(round_to)
Expand Down
13 changes: 12 additions & 1 deletion causalpy/tests/test_integration_pymc_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ def test_sc_brexit(mock_pymc_sample):


@pytest.mark.integration
def test_ancova(mock_pymc_sample):
def test_ancova(mock_pymc_sample, capsys):
"""
Test Pre-PostNEGD experiment on anova1 data.

Expand All @@ -576,6 +576,17 @@ def test_ancova(mock_pymc_sample):
assert len(result.idata.posterior.coords["chain"]) == sample_kwargs["chains"]
assert len(result.idata.posterior.coords["draw"]) == sample_kwargs["draws"]
result.summary()
output = capsys.readouterr().out
assert "Group-level descriptive stats:" in output
assert "n" in output
assert "pre_mean" in output
assert "post_mean" in output
rounded_stats = result._group_level_summary_stats(round_to=1)
assert isinstance(rounded_stats, pd.DataFrame)
for col in ["pre_mean", "post_mean"]:
assert pd.api.types.is_string_dtype(rounded_stats[col])
for val in rounded_stats[col]:
float(val)
fig, ax = result.plot()
assert isinstance(fig, plt.Figure)
# For multi-panel plots, ax should be an array of axes
Expand Down
35 changes: 20 additions & 15 deletions docs/source/notebooks/ancova_pymc.ipynb

Large diffs are not rendered by default.

Loading