Pre-Trends Power Analysis ========================= Power analysis for pre-trends tests (Roth 2022). .. module:: diff_diff.pretrends Overview -------- Passing a pre-trends test does not guarantee that parallel trends holds. Roth (2022) shows that pre-trends tests are often underpowered to detect economically meaningful violations of parallel trends. This module provides tools to assess: 1. **Power**: The probability of rejecting the null (no pre-trends) given a violation 2. **Minimum Detectable Violation (MDV)**: The smallest violation detectable at target power Key insights from Roth (2022): - Pre-trends tests are joint tests that pre-period coefficients equal zero - Standard pre-trends tests often have low power against linear trends - A "passed" pre-trends test may simply reflect lack of statistical power - MDV provides the minimum violation the test could have detected PreTrendsPower -------------- Main class for pre-trends power analysis. .. autoclass:: diff_diff.PreTrendsPower :no-index: :members: :undoc-members: :show-inheritance: .. rubric:: Methods .. autosummary:: ~PreTrendsPower.fit ~PreTrendsPower.power_curve ~PreTrendsPower.sensitivity_to_honest_did Example ~~~~~~~ .. code-block:: python from diff_diff import MultiPeriodDiD, PreTrendsPower # First fit an event study model = MultiPeriodDiD() results = model.fit(data, outcome='y', treatment='treated', time='period', unit='unit_id', post_periods=[5, 6, 7], reference_period=4) # Compute pre-trends power for linear violations. # Default acceptance region is the Roth (2022) NIS box probability. pt = PreTrendsPower(alpha=0.05, power=0.80, violation_type='linear') pt_results = pt.fit(results) print(f"MDV: {pt_results.mdv:.3f}") print(f"Power: {pt_results.power:.2%}") print(f"NIS box probability (accept H0): {pt_results.nis_box_probability:.4f}") # Select the Wald (noncentral-χ²) acceptance-region form instead of the # default NIS box probability. Wald preserves the pre-PR-B acceptance- # region math byte-identically; numerical-output bit-identity to pre-PR-B # fitted results only holds on regular pre-period grids and on the # legacy `relative_times=None` path. PR-B Step 4's `relative_times` # threading applies to BOTH NIS and Wald, so on irregular grids the # Wald MDV is also in Roth's γ units (see REGISTRY linear-pattern Note). pt_wald = PreTrendsPower( alpha=0.05, power=0.80, violation_type='linear', pretest_form='wald' ) PreTrendsPowerResults --------------------- Results from pre-trends power analysis. .. autoclass:: diff_diff.PreTrendsPowerResults :no-index: :members: :undoc-members: :show-inheritance: PreTrendsPowerCurve ------------------- Power curve across violation magnitudes. .. autoclass:: diff_diff.PreTrendsPowerCurve :no-index: :members: :undoc-members: :show-inheritance: Convenience Functions --------------------- compute_pretrends_power ~~~~~~~~~~~~~~~~~~~~~~~ Quick computation of pre-trends power. .. autofunction:: diff_diff.compute_pretrends_power compute_mdv ~~~~~~~~~~~ Compute minimum detectable violation. .. autofunction:: diff_diff.compute_mdv plot_pretrends_power ~~~~~~~~~~~~~~~~~~~~ Plot a pre-trends test power curve. .. autofunction:: diff_diff.plot_pretrends_power Violation Types --------------- The module supports several types of pre-trends violations: **linear** Linear trend violations where each pre-period differs from the reference by an amount proportional to distance. ``delta[t] = M * t`` for pre-periods. **constant** Constant violations where all pre-periods have the same deviation. ``delta[t] = M`` for all pre-periods. **last_period** Only the period immediately before treatment is violated. ``delta[-1] = M``, all other pre-periods are zero. **custom** User-specified violation pattern via the ``violation_weights`` parameter. Accepted by both ``PreTrendsPower`` (constructor kwarg) and the convenience helpers ``compute_pretrends_power`` / ``compute_mdv`` (forwarded kwarg). Complete Example ---------------- .. code-block:: python import numpy as np from diff_diff import ( MultiPeriodDiD, PreTrendsPower, compute_mdv, plot_pretrends_power, ) # Fit event study model = MultiPeriodDiD() results = model.fit(data, outcome='y', treatment='treated', time='period', unit='unit_id', post_periods=[5, 6, 7], reference_period=4) # Compute MDV mdv = compute_mdv(results, alpha=0.05, target_power=0.80) print(f"Minimum Detectable Violation: {mdv:.3f}") # Power curve analysis pt = PreTrendsPower(alpha=0.05, violation_type='linear') curve = pt.power_curve(results, n_points=50) # Plot power curve ax = plot_pretrends_power(curve, target_power=0.80) ax.figure.savefig('pretrends_power.png') # Integration with HonestDiD sensitivity = pt.sensitivity_to_honest_did(results) References ---------- - Roth, J. (2022). Pretest with Caution: Event-Study Estimates after Testing for Parallel Trends. *American Economic Review: Insights*, 4(3), 305-322. - R package: `pretrends `_ See Also -------- - :doc:`honest_did` - Sensitivity analysis under parallel trends violations - :doc:`utils` - Standard parallel trends tests