diff-diff 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
diff_diff/__init__.py CHANGED
@@ -81,6 +81,11 @@ from diff_diff.sun_abraham import (
81
81
  SunAbraham,
82
82
  SunAbrahamResults,
83
83
  )
84
+ from diff_diff.triple_diff import (
85
+ TripleDifference,
86
+ TripleDifferenceResults,
87
+ triple_difference,
88
+ )
84
89
  from diff_diff.utils import (
85
90
  WildBootstrapResults,
86
91
  check_parallel_trends,
@@ -98,7 +103,7 @@ from diff_diff.visualization import (
98
103
  plot_sensitivity,
99
104
  )
100
105
 
101
- __version__ = "1.2.0"
106
+ __version__ = "1.2.1"
102
107
  __all__ = [
103
108
  # Estimators
104
109
  "DifferenceInDifferences",
@@ -107,6 +112,7 @@ __all__ = [
107
112
  "SyntheticDiD",
108
113
  "CallawaySantAnna",
109
114
  "SunAbraham",
115
+ "TripleDifference",
110
116
  # Bacon Decomposition
111
117
  "BaconDecomposition",
112
118
  "BaconDecompositionResults",
@@ -123,6 +129,8 @@ __all__ = [
123
129
  "GroupTimeEffect",
124
130
  "SunAbrahamResults",
125
131
  "SABootstrapResults",
132
+ "TripleDifferenceResults",
133
+ "triple_difference",
126
134
  # Visualization
127
135
  "plot_event_study",
128
136
  "plot_group_effects",
diff_diff/staggered.py CHANGED
@@ -1603,30 +1603,25 @@ class CallawaySantAnna:
1603
1603
  weights * bootstrap_atts_gt[b, gt_indices]
1604
1604
  )
1605
1605
 
1606
- # Compute bootstrap statistics
1607
- # ATT(g,t) statistics
1606
+ # Compute bootstrap statistics for ATT(g,t)
1608
1607
  gt_ses = {}
1609
1608
  gt_cis = {}
1610
1609
  gt_p_values = {}
1611
1610
 
1612
1611
  for j, gt in enumerate(gt_pairs):
1613
- original_effect = original_atts[j]
1614
- boot_dist = bootstrap_atts_gt[:, j]
1615
-
1616
- se = float(np.std(boot_dist, ddof=1))
1617
- ci = self._compute_percentile_ci(boot_dist, self.alpha)
1618
- p_value = self._compute_bootstrap_pvalue(original_effect, boot_dist)
1619
-
1612
+ se, ci, p_value = self._compute_effect_bootstrap_stats(
1613
+ original_atts[j], bootstrap_atts_gt[:, j]
1614
+ )
1620
1615
  gt_ses[gt] = se
1621
1616
  gt_cis[gt] = ci
1622
1617
  gt_p_values[gt] = p_value
1623
1618
 
1624
- # Overall ATT statistics
1625
- overall_se = float(np.std(bootstrap_overall, ddof=1))
1626
- overall_ci = self._compute_percentile_ci(bootstrap_overall, self.alpha)
1627
- overall_p_value = self._compute_bootstrap_pvalue(original_overall, bootstrap_overall)
1619
+ # Compute bootstrap statistics for overall ATT
1620
+ overall_se, overall_ci, overall_p_value = self._compute_effect_bootstrap_stats(
1621
+ original_overall, bootstrap_overall
1622
+ )
1628
1623
 
1629
- # Event study statistics
1624
+ # Compute bootstrap statistics for event study effects
1630
1625
  event_study_ses = None
1631
1626
  event_study_cis = None
1632
1627
  event_study_p_values = None
@@ -1637,16 +1632,14 @@ class CallawaySantAnna:
1637
1632
  event_study_p_values = {}
1638
1633
 
1639
1634
  for e in rel_periods:
1640
- original_effect = event_study_info[e]['effect']
1641
- boot_dist = bootstrap_event_study[e]
1642
-
1643
- event_study_ses[e] = float(np.std(boot_dist, ddof=1))
1644
- event_study_cis[e] = self._compute_percentile_ci(boot_dist, self.alpha)
1645
- event_study_p_values[e] = self._compute_bootstrap_pvalue(
1646
- original_effect, boot_dist
1635
+ se, ci, p_value = self._compute_effect_bootstrap_stats(
1636
+ event_study_info[e]['effect'], bootstrap_event_study[e]
1647
1637
  )
1638
+ event_study_ses[e] = se
1639
+ event_study_cis[e] = ci
1640
+ event_study_p_values[e] = p_value
1648
1641
 
1649
- # Group effect statistics
1642
+ # Compute bootstrap statistics for group effects
1650
1643
  group_effect_ses = None
1651
1644
  group_effect_cis = None
1652
1645
  group_effect_p_values = None
@@ -1657,14 +1650,12 @@ class CallawaySantAnna:
1657
1650
  group_effect_p_values = {}
1658
1651
 
1659
1652
  for g in groups:
1660
- original_effect = group_agg_info[g]['effect']
1661
- boot_dist = bootstrap_group[g]
1662
-
1663
- group_effect_ses[g] = float(np.std(boot_dist, ddof=1))
1664
- group_effect_cis[g] = self._compute_percentile_ci(boot_dist, self.alpha)
1665
- group_effect_p_values[g] = self._compute_bootstrap_pvalue(
1666
- original_effect, boot_dist
1653
+ se, ci, p_value = self._compute_effect_bootstrap_stats(
1654
+ group_agg_info[g]['effect'], bootstrap_group[g]
1667
1655
  )
1656
+ group_effect_ses[g] = se
1657
+ group_effect_cis[g] = ci
1658
+ group_effect_p_values[g] = p_value
1668
1659
 
1669
1660
  return CSBootstrapResults(
1670
1661
  n_bootstrap=self.n_bootstrap,
@@ -1817,6 +1808,35 @@ class CallawaySantAnna:
1817
1808
 
1818
1809
  return float(p_value)
1819
1810
 
1811
+ def _compute_effect_bootstrap_stats(
1812
+ self,
1813
+ original_effect: float,
1814
+ boot_dist: np.ndarray,
1815
+ ) -> Tuple[float, Tuple[float, float], float]:
1816
+ """
1817
+ Compute bootstrap statistics for a single effect.
1818
+
1819
+ Parameters
1820
+ ----------
1821
+ original_effect : float
1822
+ Original point estimate.
1823
+ boot_dist : np.ndarray
1824
+ Bootstrap distribution of the effect.
1825
+
1826
+ Returns
1827
+ -------
1828
+ se : float
1829
+ Bootstrap standard error.
1830
+ ci : Tuple[float, float]
1831
+ Percentile confidence interval.
1832
+ p_value : float
1833
+ Bootstrap p-value.
1834
+ """
1835
+ se = float(np.std(boot_dist, ddof=1))
1836
+ ci = self._compute_percentile_ci(boot_dist, self.alpha)
1837
+ p_value = self._compute_bootstrap_pvalue(original_effect, boot_dist)
1838
+ return se, ci, p_value
1839
+
1820
1840
  def get_params(self) -> Dict[str, Any]:
1821
1841
  """Get estimator parameters (sklearn-compatible)."""
1822
1842
  return {