diff-diff 2.1.7__tar.gz → 2.1.9__tar.gz

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.
Files changed (35) hide show
  1. {diff_diff-2.1.7 → diff_diff-2.1.9}/PKG-INFO +19 -9
  2. {diff_diff-2.1.7 → diff_diff-2.1.9}/README.md +18 -8
  3. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/__init__.py +1 -1
  4. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/_backend.py +16 -4
  5. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/trop.py +1537 -229
  6. {diff_diff-2.1.7 → diff_diff-2.1.9}/pyproject.toml +1 -1
  7. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/Cargo.lock +7 -7
  8. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/Cargo.toml +1 -1
  9. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/src/lib.rs +5 -1
  10. diff_diff-2.1.9/rust/src/trop.rs +1772 -0
  11. diff_diff-2.1.7/rust/src/trop.rs +0 -935
  12. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/bacon.py +0 -0
  13. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/datasets.py +0 -0
  14. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/diagnostics.py +0 -0
  15. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/estimators.py +0 -0
  16. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/honest_did.py +0 -0
  17. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/linalg.py +0 -0
  18. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/power.py +0 -0
  19. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/prep.py +0 -0
  20. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/prep_dgp.py +0 -0
  21. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/pretrends.py +0 -0
  22. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/results.py +0 -0
  23. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/staggered.py +0 -0
  24. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/staggered_aggregation.py +0 -0
  25. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/staggered_bootstrap.py +0 -0
  26. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/staggered_results.py +0 -0
  27. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/sun_abraham.py +0 -0
  28. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/synthetic_did.py +0 -0
  29. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/triple_diff.py +0 -0
  30. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/twfe.py +0 -0
  31. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/utils.py +0 -0
  32. {diff_diff-2.1.7 → diff_diff-2.1.9}/diff_diff/visualization.py +0 -0
  33. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/src/bootstrap.rs +0 -0
  34. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/src/linalg.rs +0 -0
  35. {diff_diff-2.1.7 → diff_diff-2.1.9}/rust/src/weights.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diff-diff
3
- Version: 2.1.7
3
+ Version: 2.1.9
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -1173,13 +1173,15 @@ trop_est = TROP(
1173
1173
  lambda_nn_grid=[0.0, 0.1, 1.0], # Nuclear norm grid
1174
1174
  n_bootstrap=200
1175
1175
  )
1176
+ # Note: TROP infers treatment periods from the treatment indicator column.
1177
+ # The 'treated' column must be an absorbing state (D=1 for all periods
1178
+ # during and after treatment starts for each unit).
1176
1179
  results = trop_est.fit(
1177
1180
  panel_data,
1178
1181
  outcome='gdp_growth',
1179
1182
  treatment='treated',
1180
1183
  unit='state',
1181
- time='year',
1182
- post_periods=[2015, 2016, 2017, 2018]
1184
+ time='year'
1183
1185
  )
1184
1186
 
1185
1187
  # View results
@@ -1267,9 +1269,11 @@ sdid_results = sdid.fit(data, outcome='y', treatment='treated',
1267
1269
  unit='unit', time='time', post_periods=[5,6,7])
1268
1270
 
1269
1271
  # TROP (accounts for factors)
1272
+ # Note: TROP infers treatment periods from the treatment indicator column
1273
+ # (D=1 for treated observations, D=0 for control)
1270
1274
  trop_est = TROP() # Uses default grids with LOOCV selection
1271
1275
  trop_results = trop_est.fit(data, outcome='y', treatment='treated',
1272
- unit='unit', time='time', post_periods=[5,6,7])
1276
+ unit='unit', time='time')
1273
1277
 
1274
1278
  print(f"SDID estimate: {sdid_results.att:.3f}")
1275
1279
  print(f"TROP estimate: {trop_results.att:.3f}")
@@ -1298,6 +1302,7 @@ trop = TROP(
1298
1302
 
1299
1303
  ```python
1300
1304
  TROP(
1305
+ method='twostep', # Estimation method: 'twostep' (default) or 'joint'
1301
1306
  lambda_time_grid=None, # Time decay grid (default: [0, 0.1, 0.5, 1, 2, 5])
1302
1307
  lambda_unit_grid=None, # Unit distance grid (default: [0, 0.1, 0.5, 1, 2, 5])
1303
1308
  lambda_nn_grid=None, # Nuclear norm grid (default: [0, 0.01, 0.1, 1, 10])
@@ -1310,17 +1315,21 @@ TROP(
1310
1315
  )
1311
1316
  ```
1312
1317
 
1318
+ **Estimation methods:**
1319
+ - `'twostep'` (default): Per-observation model fitting following Algorithm 2 of the paper. Computes observation-specific weights and fits a model for each treated observation, then averages the individual treatment effects. More flexible but computationally intensive.
1320
+ - `'joint'`: Joint weighted least squares optimization. Estimates a single scalar treatment effect τ along with fixed effects and optional low-rank factor adjustment. Faster but assumes homogeneous treatment effects.
1321
+
1313
1322
  **Convenience function:**
1314
1323
 
1315
1324
  ```python
1316
1325
  # One-liner estimation with default tuning grids
1326
+ # Note: TROP infers treatment periods from the treatment indicator
1317
1327
  results = trop(
1318
1328
  data,
1319
1329
  outcome='y',
1320
1330
  treatment='treated',
1321
1331
  unit='unit',
1322
1332
  time='time',
1323
- post_periods=[5, 6, 7],
1324
1333
  n_bootstrap=200
1325
1334
  )
1326
1335
  ```
@@ -1912,10 +1921,11 @@ TROP(
1912
1921
  |-----------|------|-------------|
1913
1922
  | `data` | DataFrame | Panel data |
1914
1923
  | `outcome` | str | Outcome variable column name |
1915
- | `treatment` | str | Treatment indicator column (0/1) |
1924
+ | `treatment` | str | Treatment indicator column (0/1 absorbing state) |
1916
1925
  | `unit` | str | Unit identifier column |
1917
1926
  | `time` | str | Time period column |
1918
- | `post_periods` | list | List of post-treatment period values |
1927
+
1928
+ Note: TROP infers treatment periods from the treatment indicator column. The treatment column should be an absorbing state indicator where D=1 for all periods during and after treatment starts.
1919
1929
 
1920
1930
  ### TROPResults
1921
1931
 
@@ -1941,8 +1951,8 @@ TROP(
1941
1951
  | `factor_matrix` | Low-rank factor matrix L (n_periods x n_units) |
1942
1952
  | `effective_rank` | Effective rank of factor matrix |
1943
1953
  | `loocv_score` | LOOCV score for selected parameters |
1944
- | `pre_periods` | List of pre-treatment periods |
1945
- | `post_periods` | List of post-treatment periods |
1954
+ | `n_pre_periods` | Number of pre-treatment periods |
1955
+ | `n_post_periods` | Number of post-treatment periods |
1946
1956
  | `variance_method` | Variance estimation method |
1947
1957
  | `bootstrap_distribution` | Bootstrap distribution (if bootstrap) |
1948
1958
 
@@ -1138,13 +1138,15 @@ trop_est = TROP(
1138
1138
  lambda_nn_grid=[0.0, 0.1, 1.0], # Nuclear norm grid
1139
1139
  n_bootstrap=200
1140
1140
  )
1141
+ # Note: TROP infers treatment periods from the treatment indicator column.
1142
+ # The 'treated' column must be an absorbing state (D=1 for all periods
1143
+ # during and after treatment starts for each unit).
1141
1144
  results = trop_est.fit(
1142
1145
  panel_data,
1143
1146
  outcome='gdp_growth',
1144
1147
  treatment='treated',
1145
1148
  unit='state',
1146
- time='year',
1147
- post_periods=[2015, 2016, 2017, 2018]
1149
+ time='year'
1148
1150
  )
1149
1151
 
1150
1152
  # View results
@@ -1232,9 +1234,11 @@ sdid_results = sdid.fit(data, outcome='y', treatment='treated',
1232
1234
  unit='unit', time='time', post_periods=[5,6,7])
1233
1235
 
1234
1236
  # TROP (accounts for factors)
1237
+ # Note: TROP infers treatment periods from the treatment indicator column
1238
+ # (D=1 for treated observations, D=0 for control)
1235
1239
  trop_est = TROP() # Uses default grids with LOOCV selection
1236
1240
  trop_results = trop_est.fit(data, outcome='y', treatment='treated',
1237
- unit='unit', time='time', post_periods=[5,6,7])
1241
+ unit='unit', time='time')
1238
1242
 
1239
1243
  print(f"SDID estimate: {sdid_results.att:.3f}")
1240
1244
  print(f"TROP estimate: {trop_results.att:.3f}")
@@ -1263,6 +1267,7 @@ trop = TROP(
1263
1267
 
1264
1268
  ```python
1265
1269
  TROP(
1270
+ method='twostep', # Estimation method: 'twostep' (default) or 'joint'
1266
1271
  lambda_time_grid=None, # Time decay grid (default: [0, 0.1, 0.5, 1, 2, 5])
1267
1272
  lambda_unit_grid=None, # Unit distance grid (default: [0, 0.1, 0.5, 1, 2, 5])
1268
1273
  lambda_nn_grid=None, # Nuclear norm grid (default: [0, 0.01, 0.1, 1, 10])
@@ -1275,17 +1280,21 @@ TROP(
1275
1280
  )
1276
1281
  ```
1277
1282
 
1283
+ **Estimation methods:**
1284
+ - `'twostep'` (default): Per-observation model fitting following Algorithm 2 of the paper. Computes observation-specific weights and fits a model for each treated observation, then averages the individual treatment effects. More flexible but computationally intensive.
1285
+ - `'joint'`: Joint weighted least squares optimization. Estimates a single scalar treatment effect τ along with fixed effects and optional low-rank factor adjustment. Faster but assumes homogeneous treatment effects.
1286
+
1278
1287
  **Convenience function:**
1279
1288
 
1280
1289
  ```python
1281
1290
  # One-liner estimation with default tuning grids
1291
+ # Note: TROP infers treatment periods from the treatment indicator
1282
1292
  results = trop(
1283
1293
  data,
1284
1294
  outcome='y',
1285
1295
  treatment='treated',
1286
1296
  unit='unit',
1287
1297
  time='time',
1288
- post_periods=[5, 6, 7],
1289
1298
  n_bootstrap=200
1290
1299
  )
1291
1300
  ```
@@ -1877,10 +1886,11 @@ TROP(
1877
1886
  |-----------|------|-------------|
1878
1887
  | `data` | DataFrame | Panel data |
1879
1888
  | `outcome` | str | Outcome variable column name |
1880
- | `treatment` | str | Treatment indicator column (0/1) |
1889
+ | `treatment` | str | Treatment indicator column (0/1 absorbing state) |
1881
1890
  | `unit` | str | Unit identifier column |
1882
1891
  | `time` | str | Time period column |
1883
- | `post_periods` | list | List of post-treatment period values |
1892
+
1893
+ Note: TROP infers treatment periods from the treatment indicator column. The treatment column should be an absorbing state indicator where D=1 for all periods during and after treatment starts.
1884
1894
 
1885
1895
  ### TROPResults
1886
1896
 
@@ -1906,8 +1916,8 @@ TROP(
1906
1916
  | `factor_matrix` | Low-rank factor matrix L (n_periods x n_units) |
1907
1917
  | `effective_rank` | Effective rank of factor matrix |
1908
1918
  | `loocv_score` | LOOCV score for selected parameters |
1909
- | `pre_periods` | List of pre-treatment periods |
1910
- | `post_periods` | List of post-treatment periods |
1919
+ | `n_pre_periods` | Number of pre-treatment periods |
1920
+ | `n_post_periods` | Number of post-treatment periods |
1911
1921
  | `variance_method` | Variance estimation method |
1912
1922
  | `bootstrap_distribution` | Bootstrap distribution (if bootstrap) |
1913
1923
 
@@ -136,7 +136,7 @@ from diff_diff.datasets import (
136
136
  load_mpdta,
137
137
  )
138
138
 
139
- __version__ = "2.1.7"
139
+ __version__ = "2.1.9"
140
140
  __all__ = [
141
141
  # Estimators
142
142
  "DifferenceInDifferences",
@@ -23,10 +23,13 @@ try:
23
23
  project_simplex as _rust_project_simplex,
24
24
  solve_ols as _rust_solve_ols,
25
25
  compute_robust_vcov as _rust_compute_robust_vcov,
26
- # TROP estimator acceleration
26
+ # TROP estimator acceleration (twostep method)
27
27
  compute_unit_distance_matrix as _rust_unit_distance_matrix,
28
28
  loocv_grid_search as _rust_loocv_grid_search,
29
29
  bootstrap_trop_variance as _rust_bootstrap_trop_variance,
30
+ # TROP estimator acceleration (joint method)
31
+ loocv_grid_search_joint as _rust_loocv_grid_search_joint,
32
+ bootstrap_trop_variance_joint as _rust_bootstrap_trop_variance_joint,
30
33
  )
31
34
  _rust_available = True
32
35
  except ImportError:
@@ -36,10 +39,13 @@ except ImportError:
36
39
  _rust_project_simplex = None
37
40
  _rust_solve_ols = None
38
41
  _rust_compute_robust_vcov = None
39
- # TROP estimator acceleration
42
+ # TROP estimator acceleration (twostep method)
40
43
  _rust_unit_distance_matrix = None
41
44
  _rust_loocv_grid_search = None
42
45
  _rust_bootstrap_trop_variance = None
46
+ # TROP estimator acceleration (joint method)
47
+ _rust_loocv_grid_search_joint = None
48
+ _rust_bootstrap_trop_variance_joint = None
43
49
 
44
50
  # Determine final backend based on environment variable and availability
45
51
  if _backend_env == 'python':
@@ -50,10 +56,13 @@ if _backend_env == 'python':
50
56
  _rust_project_simplex = None
51
57
  _rust_solve_ols = None
52
58
  _rust_compute_robust_vcov = None
53
- # TROP estimator acceleration
59
+ # TROP estimator acceleration (twostep method)
54
60
  _rust_unit_distance_matrix = None
55
61
  _rust_loocv_grid_search = None
56
62
  _rust_bootstrap_trop_variance = None
63
+ # TROP estimator acceleration (joint method)
64
+ _rust_loocv_grid_search_joint = None
65
+ _rust_bootstrap_trop_variance_joint = None
57
66
  elif _backend_env == 'rust':
58
67
  # Force Rust mode - fail if not available
59
68
  if not _rust_available:
@@ -73,8 +82,11 @@ __all__ = [
73
82
  '_rust_project_simplex',
74
83
  '_rust_solve_ols',
75
84
  '_rust_compute_robust_vcov',
76
- # TROP estimator acceleration
85
+ # TROP estimator acceleration (twostep method)
77
86
  '_rust_unit_distance_matrix',
78
87
  '_rust_loocv_grid_search',
79
88
  '_rust_bootstrap_trop_variance',
89
+ # TROP estimator acceleration (joint method)
90
+ '_rust_loocv_grid_search_joint',
91
+ '_rust_bootstrap_trop_variance_joint',
80
92
  ]