Skip to content

Commit 1c6bb7d

Browse files
igerberclaude
andcommitted
Fix tutorial 07: use correct post_periods for event study
The notebook was passing all periods (pre and post) as post_periods to MultiPeriodDiD, causing HonestDiD to fail with "No pre-period effects found" since the results had no pre-period classification. Fix: pass only actual post-treatment periods [5-9] to post_periods. MultiPeriodDiD automatically estimates pre-period coefficients for the event study, and HonestDiD can now correctly identify them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0dc7e2c commit 1c6bb7d

1 file changed

Lines changed: 4 additions & 81 deletions

File tree

docs/tutorials/07_pretrends_power.ipynb

Lines changed: 4 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -131,28 +131,7 @@
131131
"id": "cell-6",
132132
"metadata": {},
133133
"outputs": [],
134-
"source": [
135-
"# Fit event study with ALL periods (pre and post) relative to reference period\n",
136-
"# For pre-trends power analysis, we need coefficients for pre-periods too\n",
137-
"mp_did = MultiPeriodDiD()\n",
138-
"\n",
139-
"# Use period 4 as the reference period (last pre-period, excluded from estimation)\n",
140-
"# Estimate coefficients for all other periods: 0, 1, 2, 3 (pre) and 5, 6, 7, 8, 9 (post)\n",
141-
"all_estimation_periods = [0, 1, 2, 3, 5, 6, 7, 8, 9] # All except reference period 4\n",
142-
"\n",
143-
"event_results = mp_did.fit(\n",
144-
" df,\n",
145-
" outcome='outcome',\n",
146-
" treatment='treated',\n",
147-
" time='period',\n",
148-
" post_periods=all_estimation_periods # Include all periods for full event study\n",
149-
")\n",
150-
"\n",
151-
"# Note: For standard DiD analysis, we'd normally use post_periods=[5,6,7,8,9]\n",
152-
"# But for pre-trends power analysis, we need pre-period coefficients too\n",
153-
"\n",
154-
"print(event_results.summary())"
155-
]
134+
"source": "# Fit event study with ALL periods (pre and post) relative to reference period\n# For pre-trends power analysis, we need coefficients for pre-periods too\nmp_did = MultiPeriodDiD()\n\n# Use period 4 as the reference period (last pre-period, excluded from estimation)\n# Specify post_periods as the actual post-treatment periods; MultiPeriodDiD\n# automatically estimates pre-period coefficients for the event study.\nevent_results = mp_did.fit(\n df,\n outcome='outcome',\n treatment='treated',\n time='period',\n post_periods=[5, 6, 7, 8, 9]\n)\n\nprint(event_results.summary())"
156135
},
157136
{
158137
"cell_type": "code",
@@ -199,24 +178,7 @@
199178
"id": "cell-10",
200179
"metadata": {},
201180
"outputs": [],
202-
"source": [
203-
"# Create a PreTrendsPower object\n",
204-
"pt = PreTrendsPower(\n",
205-
" alpha=0.05, # Significance level for pre-trends test\n",
206-
" power=0.80, # Target power for MDV calculation\n",
207-
" violation_type='linear' # Type of violation to consider\n",
208-
")\n",
209-
"\n",
210-
"# Define the actual pre-treatment periods (those before treatment starts at period 5)\n",
211-
"# These are the periods we want to analyze for pre-trends power\n",
212-
"pre_treatment_periods = [0, 1, 2, 3]\n",
213-
"\n",
214-
"# Fit to the event study results, specifying which periods are pre-treatment\n",
215-
"# This is needed because we estimated all periods as post_periods in the event study\n",
216-
"pt_results = pt.fit(event_results, pre_periods=pre_treatment_periods)\n",
217-
"\n",
218-
"print(pt_results.summary())"
219-
]
181+
"source": "# Create a PreTrendsPower object\npt = PreTrendsPower(\n alpha=0.05, # Significance level for pre-trends test\n power=0.80, # Target power for MDV calculation\n violation_type='linear' # Type of violation to consider\n)\n\n# Define the actual pre-treatment periods (those before treatment starts at period 5)\n# These are the periods we want to analyze for pre-trends power\npre_treatment_periods = [0, 1, 2, 3]\n\n# Fit to the event study results, specifying which periods are pre-treatment\npt_results = pt.fit(event_results, pre_periods=pre_treatment_periods)\n\nprint(pt_results.summary())"
220182
},
221183
{
222184
"cell_type": "markdown",
@@ -558,46 +520,7 @@
558520
"id": "cell-30",
559521
"metadata": {},
560522
"outputs": [],
561-
"source": [
562-
"# Typical workflow for pre-trends power analysis\n",
563-
"\n",
564-
"# Step 1: Estimate event study with ALL periods (pre and post) relative to reference\n",
565-
"# For pre-trends power analysis, we need pre-period coefficients\n",
566-
"mp_did = MultiPeriodDiD()\n",
567-
"\n",
568-
"# Reference period is 4 (last pre-period)\n",
569-
"# Estimate coefficients for periods 0, 1, 2, 3 (pre) and 5, 6, 7, 8, 9 (post)\n",
570-
"all_estimation_periods = [0, 1, 2, 3, 5, 6, 7, 8, 9]\n",
571-
"pre_treatment_periods = [0, 1, 2, 3] # Define which are pre-treatment\n",
572-
"\n",
573-
"results = mp_did.fit(\n",
574-
" df, \n",
575-
" outcome='outcome',\n",
576-
" treatment='treated', \n",
577-
" time='period',\n",
578-
" post_periods=all_estimation_periods\n",
579-
")\n",
580-
"\n",
581-
"# Step 2: Assess power of the pre-trends test \n",
582-
"print(\"Step 2: Pre-Trends Power Analysis\")\n",
583-
"pt = PreTrendsPower(alpha=0.05, power=0.80, violation_type='linear')\n",
584-
"pt_results = pt.fit(results, pre_periods=pre_treatment_periods)\n",
585-
"print(f\"MDV (80% power): {pt_results.mdv:.3f}\")\n",
586-
"print(\"\")\n",
587-
"\n",
588-
"# Step 3: Interpret\n",
589-
"print(\"Step 3: Interpretation\")\n",
590-
"print(f\"Your pre-trends test could only detect violations >= {pt_results.mdv:.3f}\")\n",
591-
"print(f\"Violations smaller than this would likely go undetected.\")\n",
592-
"print(\"\")\n",
593-
"\n",
594-
"# Step 4: Connect to Honest DiD for robust inference\n",
595-
"print(\"Step 4: Robust Inference with Honest DiD\")\n",
596-
"honest = HonestDiD(method='smoothness', M=pt_results.mdv)\n",
597-
"honest_results = honest.fit(results)\n",
598-
"print(f\"Robust 95% CI (M=MDV): [{honest_results.ci_lb:.3f}, {honest_results.ci_ub:.3f}]\")\n",
599-
"print(f\"Conclusion: {'Effect is robust' if honest_results.is_significant else 'Effect may not be robust'}\")"
600-
]
523+
"source": "# Typical workflow for pre-trends power analysis\n\n# Step 1: Estimate event study with proper pre/post period classification\nmp_did = MultiPeriodDiD()\n\n# Specify actual post-treatment periods; pre-period coefficients are\n# estimated automatically by MultiPeriodDiD for the event study\npre_treatment_periods = [0, 1, 2, 3] # Define which are pre-treatment\n\nresults = mp_did.fit(\n df, \n outcome='outcome',\n treatment='treated', \n time='period',\n post_periods=[5, 6, 7, 8, 9]\n)\n\n# Step 2: Assess power of the pre-trends test \nprint(\"Step 2: Pre-Trends Power Analysis\")\npt = PreTrendsPower(alpha=0.05, power=0.80, violation_type='linear')\npt_results = pt.fit(results, pre_periods=pre_treatment_periods)\nprint(f\"MDV (80% power): {pt_results.mdv:.3f}\")\nprint(\"\")\n\n# Step 3: Interpret\nprint(\"Step 3: Interpretation\")\nprint(f\"Your pre-trends test could only detect violations >= {pt_results.mdv:.3f}\")\nprint(f\"Violations smaller than this would likely go undetected.\")\nprint(\"\")\n\n# Step 4: Connect to Honest DiD for robust inference\nprint(\"Step 4: Robust Inference with Honest DiD\")\nhonest = HonestDiD(method='smoothness', M=pt_results.mdv)\nhonest_results = honest.fit(results)\nprint(f\"Robust 95% CI (M=MDV): [{honest_results.ci_lb:.3f}, {honest_results.ci_ub:.3f}]\")\nprint(f\"Conclusion: {'Effect is robust' if honest_results.is_significant else 'Effect may not be robust'}\")"
601524
},
602525
{
603526
"cell_type": "markdown",
@@ -693,4 +616,4 @@
693616
},
694617
"nbformat": 4,
695618
"nbformat_minor": 5
696-
}
619+
}

0 commit comments

Comments
 (0)