cbps 0.2.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.
Files changed (70) hide show
  1. cbps/__init__.py +3462 -0
  2. cbps/constants.py +46 -0
  3. cbps/core/__init__.py +93 -0
  4. cbps/core/cbps_binary.py +1943 -0
  5. cbps/core/cbps_continuous.py +945 -0
  6. cbps/core/cbps_multitreat.py +1123 -0
  7. cbps/core/cbps_optimal.py +507 -0
  8. cbps/core/results.py +1447 -0
  9. cbps/data/Blackwell.csv +571 -0
  10. cbps/data/LaLonde.csv +3213 -0
  11. cbps/data/npcbps_continuous_sim.csv +501 -0
  12. cbps/data/nsw.csv +723 -0
  13. cbps/data/nsw_dw.csv +446 -0
  14. cbps/data/political_ads_urban_niebler.csv +16266 -0
  15. cbps/data/psid_controls.csv +2491 -0
  16. cbps/data/psid_controls2.csv +254 -0
  17. cbps/data/psid_controls3.csv +129 -0
  18. cbps/data/simulation_dgp1_seed12345.csv +201 -0
  19. cbps/data/simulation_dgp2_seed12345.csv +201 -0
  20. cbps/data/simulation_dgp3_seed12345.csv +201 -0
  21. cbps/data/simulation_dgp4_seed12345.csv +201 -0
  22. cbps/datasets/__init__.py +78 -0
  23. cbps/datasets/blackwell.py +112 -0
  24. cbps/datasets/continuous.py +223 -0
  25. cbps/datasets/lalonde.py +272 -0
  26. cbps/datasets/npcbps_sim.py +101 -0
  27. cbps/diagnostics/__init__.py +101 -0
  28. cbps/diagnostics/balance.py +760 -0
  29. cbps/diagnostics/balance_cbmsm_addon.py +162 -0
  30. cbps/diagnostics/continuous_diagnostics.py +259 -0
  31. cbps/diagnostics/normality.py +173 -0
  32. cbps/diagnostics/ocbps_conditions.py +197 -0
  33. cbps/diagnostics/overlap.py +198 -0
  34. cbps/diagnostics/plots.py +1193 -0
  35. cbps/diagnostics/weights_diag.py +205 -0
  36. cbps/highdim/__init__.py +84 -0
  37. cbps/highdim/gmm_loss.py +340 -0
  38. cbps/highdim/hdcbps.py +1078 -0
  39. cbps/highdim/lasso_utils.py +498 -0
  40. cbps/highdim/weight_funcs.py +298 -0
  41. cbps/inference/__init__.py +42 -0
  42. cbps/inference/asyvar.py +621 -0
  43. cbps/inference/vcov_outcome.py +217 -0
  44. cbps/iv/__init__.py +48 -0
  45. cbps/iv/cbiv.py +2603 -0
  46. cbps/logging_config.py +45 -0
  47. cbps/msm/__init__.py +45 -0
  48. cbps/msm/cbmsm.py +1871 -0
  49. cbps/msm/rank_diagnostics.py +112 -0
  50. cbps/nonparametric/__init__.py +58 -0
  51. cbps/nonparametric/cholesky_whitening.py +232 -0
  52. cbps/nonparametric/empirical_likelihood.py +339 -0
  53. cbps/nonparametric/npcbps.py +1036 -0
  54. cbps/nonparametric/taylor_approx.py +207 -0
  55. cbps/py.typed +0 -0
  56. cbps/sklearn/__init__.py +42 -0
  57. cbps/sklearn/estimator.py +378 -0
  58. cbps/utils/__init__.py +82 -0
  59. cbps/utils/formula.py +415 -0
  60. cbps/utils/helpers.py +378 -0
  61. cbps/utils/numerics.py +438 -0
  62. cbps/utils/r_compat.py +109 -0
  63. cbps/utils/validation.py +224 -0
  64. cbps/utils/variance_transform.py +483 -0
  65. cbps/utils/weights.py +586 -0
  66. cbps-0.2.0.dist-info/METADATA +1090 -0
  67. cbps-0.2.0.dist-info/RECORD +70 -0
  68. cbps-0.2.0.dist-info/WHEEL +5 -0
  69. cbps-0.2.0.dist-info/licenses/LICENSE +661 -0
  70. cbps-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,223 @@
1
+ """
2
+ Continuous Treatment Dataset Loaders
3
+ =====================================
4
+
5
+ This module provides datasets for continuous treatment CBPS methods, including
6
+ simulation data and real-world application data from Fong, Hazlett, and Imai (2018).
7
+
8
+ The simulation data enables systematic evaluation of CBPS estimators under
9
+ various model misspecification scenarios. The political ads dataset provides
10
+ a real-world application for continuous treatment effect estimation.
11
+
12
+ References
13
+ ----------
14
+ Fong, C., Hazlett, C., and Imai, K. (2018). Covariate balancing propensity
15
+ score for a continuous treatment: Application to the efficacy of political
16
+ advertisements. *The Annals of Applied Statistics*, 12(1), 156-177.
17
+ DOI: 10.1214/17-AOAS1101
18
+
19
+ Urban, C. and Niebler, S. (2014). Dollars on the sidewalk: Should U.S.
20
+ Presidential candidates advertise in uncontested states? *American Journal
21
+ of Political Science*, 58(2), 322-336.
22
+ """
23
+ from pathlib import Path
24
+ from typing import Tuple, Dict, Any
25
+ import pandas as pd
26
+ import numpy as np
27
+
28
+
29
+ def load_continuous_simulation(
30
+ dgp: int = 1,
31
+ seed: int = 12345
32
+ ) -> Tuple[pd.DataFrame, Dict[str, Any]]:
33
+ """Load continuous treatment simulation data (Fong et al. 2018, Section 4).
34
+
35
+ Parameters
36
+ ----------
37
+ dgp : {1, 2, 3, 4}, default=1
38
+ Data generating process type:
39
+
40
+ - **DGP1**: Both treatment and outcome correctly specified (linear)
41
+ - **DGP2**: Treatment model misspecified (contains (X2+0.5)² nonlinear term)
42
+ - **DGP3**: Outcome model misspecified (contains 2(X2+0.5)² nonlinear term)
43
+ - **DGP4**: Doubly misspecified (both models incorrect)
44
+
45
+ seed : int, default=12345
46
+ Random seed (current CSV files use seed=12345).
47
+
48
+ Returns
49
+ -------
50
+ data : DataFrame
51
+ Simulation data (200 observations × 12 columns: T, Y, X1-X10).
52
+ metadata : dict
53
+ Metadata dictionary containing:
54
+
55
+ - ``'dgp'``: DGP type (1-4)
56
+ - ``'n'``: Number of observations (200)
57
+ - ``'k'``: Number of covariates (10)
58
+ - ``'true_ate'``: True ATE (1.0)
59
+ - ``'cov_structure'``: Covariance structure (equicorrelated MVN, ρ=0.2)
60
+
61
+ Notes
62
+ -----
63
+ Based on Fong, Hazlett, and Imai (2018) Section 4.1 simulation design.
64
+
65
+ **Column Ordering**: T (treatment), Y (outcome), X1-X10 (covariates).
66
+
67
+ **Paper Benchmarks** (500 Monte Carlo replications):
68
+
69
+ - DGP1: CBGPS balance F-statistic < 0.01 (near-perfect balance)
70
+ - DGP2: CBGPS F < 0.1 (robust despite treatment misspecification)
71
+ - DGP3: CBGPS F < 0.1 (robust despite outcome misspecification)
72
+ - DGP4: All methods biased (double misspecification, expected)
73
+
74
+ References
75
+ ----------
76
+ Fong, C., Hazlett, C., and Imai, K. (2018). Covariate balancing propensity
77
+ score for a continuous treatment: Application to the efficacy of political
78
+ advertisements. *The Annals of Applied Statistics*, 12(1), 156-177.
79
+
80
+ Examples
81
+ --------
82
+ >>> data, meta = load_continuous_simulation(dgp=1, seed=12345)
83
+ >>> data.shape
84
+ (200, 12)
85
+ >>> meta['true_ate']
86
+ 1.0
87
+ >>> list(data.columns)
88
+ ['T', 'Y', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', 'X10']
89
+ """
90
+ if dgp not in [1, 2, 3, 4]:
91
+ raise ValueError(f"dgp must be one of [1, 2, 3, 4], got: {dgp}")
92
+
93
+ # Get data directory (supports both installed and development modes)
94
+ package_data_dir = Path(__file__).parent.parent / "data"
95
+ if package_data_dir.exists():
96
+ data_dir = package_data_dir
97
+ else:
98
+ # Fall back to project root data directory (development mode)
99
+ current_file = Path(__file__)
100
+ cbps_package = current_file.parent.parent
101
+ cbps_python = cbps_package.parent
102
+ project_root = cbps_python.parent
103
+ data_dir = project_root / "data"
104
+
105
+ if not data_dir.exists():
106
+ raise FileNotFoundError(
107
+ f"Cannot find data directory. Tried:\n"
108
+ f" 1. Package data: {package_data_dir}\n"
109
+ f" 2. Project data: {data_dir}\n"
110
+ f"Please ensure the package is properly installed with data files."
111
+ )
112
+
113
+ # Construct filename
114
+ filename = f"simulation_dgp{dgp}_seed{seed}.csv"
115
+ filepath = data_dir / filename
116
+
117
+ # Read CSV file
118
+ df = pd.read_csv(filepath, dtype=np.float64)
119
+
120
+ # Metadata
121
+ metadata = {
122
+ 'dgp': dgp,
123
+ 'n': 200,
124
+ 'k': 10,
125
+ 'true_ate': 1.0,
126
+ 'cov_structure': 'Equicorrelated MVN with rho=0.2',
127
+ 'sigma_xi_sq': 4.0 if dgp == 1 else 2.25,
128
+ 'sigma_epsilon_sq': 25.0,
129
+ 'misspecified_treatment': dgp in [2, 4],
130
+ 'misspecified_outcome': dgp in [3, 4]
131
+ }
132
+
133
+ return df, metadata
134
+
135
+
136
+ def load_political_ads() -> Tuple[pd.DataFrame, Dict[str, Any]]:
137
+ """Load political advertising data (Urban and Niebler 2014).
138
+
139
+ Returns
140
+ -------
141
+ data : DataFrame
142
+ Political advertising data (16265 observations × 155 columns).
143
+ metadata : dict
144
+ Metadata containing:
145
+
146
+ - ``'n'``: 16265 (number of observations)
147
+ - ``'p'``: 155 (number of columns)
148
+ - ``'treatment_col'``: 'TotAds' (total advertising count)
149
+ - ``'boxcox_lambda'``: -0.16 (transformation parameter)
150
+ - ``'paper_f_stat_cbgps'``: 9.33e-5 (paper benchmark)
151
+
152
+ Notes
153
+ -----
154
+ **Treatment Variable**: TotAds (advertising count, range: 0-22,380).
155
+
156
+ The treatment requires Box-Cox transformation (λ=-0.16) for approximate
157
+ normality before applying continuous treatment CBPS.
158
+
159
+ **Paper Benchmarks** (Fong et al. 2018, Section 5):
160
+
161
+ - Table 1: 15 covariates with Pearson correlation < 0.001 (CBGPS)
162
+ - Figure 3: Full model F-statistic = 9.33e-5 (CBGPS) vs 215.3 (MLE)
163
+
164
+ References
165
+ ----------
166
+ Urban, C. and Niebler, S. (2014). Dollars on the sidewalk: Should U.S.
167
+ Presidential candidates advertise in uncontested states? *American Journal
168
+ of Political Science*, 58(2), 322-336.
169
+
170
+ Fong, C., Hazlett, C., and Imai, K. (2018). Covariate balancing propensity
171
+ score for a continuous treatment. *The Annals of Applied Statistics*,
172
+ 12(1), 156-177.
173
+
174
+ Examples
175
+ --------
176
+ >>> data, meta = load_political_ads()
177
+ >>> data.shape
178
+ (16265, 155)
179
+ >>> 'TotAds' in data.columns
180
+ True
181
+ >>> meta['boxcox_lambda']
182
+ -0.16
183
+ """
184
+ # Get data directory (supports both installed and development modes)
185
+ package_data_dir = Path(__file__).parent.parent / "data"
186
+ if package_data_dir.exists():
187
+ data_dir = package_data_dir
188
+ else:
189
+ # Fall back to project root data directory (development mode)
190
+ current_file = Path(__file__)
191
+ cbps_package = current_file.parent.parent
192
+ cbps_python = cbps_package.parent
193
+ project_root = cbps_python.parent
194
+ data_dir = project_root / "data"
195
+
196
+ if not data_dir.exists():
197
+ raise FileNotFoundError(
198
+ f"Cannot find data directory. Tried:\n"
199
+ f" 1. Package data: {package_data_dir}\n"
200
+ f" 2. Project data: {data_dir}\n"
201
+ f"Please ensure the package is properly installed with data files."
202
+ )
203
+
204
+ filepath = data_dir / "political_ads_urban_niebler.csv"
205
+
206
+ # Read CSV file
207
+ df = pd.read_csv(filepath, low_memory=False)
208
+
209
+ metadata = {
210
+ 'n': 16265,
211
+ 'p': 155,
212
+ 'treatment_col': 'TotAds',
213
+ 'treatment_range': (0, 22380),
214
+ 'boxcox_lambda': -0.16,
215
+ 'paper_f_stat_cbgps': 9.33e-5,
216
+ 'paper_f_stat_mle': 215.3,
217
+ 'covariates_count': 15,
218
+ 'squared_terms_count': 8
219
+ }
220
+
221
+ return df, metadata
222
+
223
+
@@ -0,0 +1,272 @@
1
+ """
2
+ LaLonde Dataset Loaders
3
+ ========================
4
+
5
+ This module provides functions to load the National Supported Work (NSW)
6
+ demonstration dataset, widely used as a benchmark in propensity score and
7
+ causal inference research.
8
+
9
+ The NSW demonstration was a labor training program conducted in the mid-1970s.
10
+ LaLonde (1986) used this dataset to evaluate econometric methods for estimating
11
+ treatment effects from observational data by comparing results to experimental
12
+ benchmarks.
13
+
14
+ References
15
+ ----------
16
+ LaLonde, R. J. (1986). Evaluating the econometric evaluations of training
17
+ programs with experimental data. *American Economic Review*, 76(4), 604-620.
18
+
19
+ Dehejia, R. H. and Wahba, S. (1999). Causal effects in nonexperimental
20
+ studies: Reevaluating the evaluation of training programs. *Journal of the
21
+ American Statistical Association*, 94(448), 1053-1062.
22
+ """
23
+
24
+ from pathlib import Path
25
+ from typing import Tuple, Union
26
+ import pandas as pd
27
+ import numpy as np
28
+
29
+
30
+ def _get_data_dir() -> Path:
31
+ """
32
+ Get the absolute path to the data directory.
33
+
34
+ Supports two modes:
35
+ 1. Installed mode: Load from package data directory (site-packages/cbps/data/)
36
+ 2. Development mode: Fall back to project root data directory
37
+
38
+ Priority is given to the package data directory; if it does not exist,
39
+ falls back to the project root data directory.
40
+
41
+ Returns
42
+ -------
43
+ Path
44
+ Absolute path to the data directory.
45
+ """
46
+ # Option 1: Try package data directory (installed mode)
47
+ package_data_dir = Path(__file__).parent.parent / "data"
48
+ if package_data_dir.exists():
49
+ return package_data_dir
50
+
51
+ # Option 2: Fall back to project root data directory (development mode)
52
+ current_file = Path(__file__)
53
+ cbps_package = current_file.parent.parent
54
+ cbps_python = cbps_package.parent
55
+ project_root = cbps_python.parent
56
+ project_data_dir = project_root / "data"
57
+
58
+ if project_data_dir.exists():
59
+ return project_data_dir
60
+
61
+ # Raise clear error if neither exists
62
+ raise FileNotFoundError(
63
+ f"Cannot find data directory. Tried:\n"
64
+ f" 1. Package data: {package_data_dir}\n"
65
+ f" 2. Project data: {project_data_dir}\n"
66
+ f"Please ensure the package is properly installed with data files."
67
+ )
68
+
69
+
70
+ def load_lalonde(
71
+ dehejia_wahba_only: bool = True,
72
+ return_X_y: bool = False
73
+ ) -> Union[pd.DataFrame, Tuple[pd.DataFrame, pd.Series]]:
74
+ """Load the LaLonde dataset (NSW job training program evaluation).
75
+
76
+ Parameters
77
+ ----------
78
+ dehejia_wahba_only : bool, default=True
79
+ If True, load the Dehejia-Wahba subsample (445 observations).
80
+ If False, load the full LaLonde dataset (3212 observations).
81
+ return_X_y : bool, default=False
82
+ If True, return (X, y) tuple; otherwise return DataFrame.
83
+
84
+ Returns
85
+ -------
86
+ DataFrame or (DataFrame, Series)
87
+ If return_X_y=False: Complete DataFrame with standardized column names.
88
+ If return_X_y=True: (covariates, treatment) tuple.
89
+
90
+ Notes
91
+ -----
92
+ The dataset contains data from the National Supported Work (NSW)
93
+ demonstration, a labor training program conducted in the mid-1970s.
94
+
95
+ **Variables**
96
+
97
+ - ``treat``: Treatment indicator (1=received training, 0=control)
98
+ - ``age``: Age in years
99
+ - ``educ``: Years of education
100
+ - ``black``: African American indicator
101
+ - ``hisp``: Hispanic indicator
102
+ - ``married``: Marital status indicator
103
+ - ``nodegr``: No high school degree indicator
104
+ - ``re74``, ``re75``: Real earnings in 1974, 1975 (pre-treatment)
105
+ - ``re78``: Real earnings in 1978 (outcome)
106
+
107
+ **Data Dimensions**
108
+
109
+ - Dehejia-Wahba subsample: 445 observations, 11 columns
110
+ - Full dataset: 3212 observations, 12 columns
111
+
112
+ References
113
+ ----------
114
+ LaLonde, R. J. (1986). Evaluating the econometric evaluations of training
115
+ programs with experimental data. *American Economic Review*, 76(4), 604-620.
116
+
117
+ Dehejia, R. H. and Wahba, S. (1999). Causal effects in nonexperimental
118
+ studies: Reevaluating the evaluation of training programs. *Journal of the
119
+ American Statistical Association*, 94(448), 1053-1062.
120
+
121
+ Examples
122
+ --------
123
+ >>> df = load_lalonde(dehejia_wahba_only=True)
124
+ >>> df.shape
125
+ (445, 11)
126
+ >>> 'educ' in df.columns
127
+ True
128
+ """
129
+ data_dir = _get_data_dir()
130
+
131
+ if dehejia_wahba_only:
132
+ # Load Dehejia-Wahba subsample
133
+ filepath = data_dir / "nsw_dw.csv"
134
+ df = pd.read_csv(filepath)
135
+
136
+ # Standardize column names
137
+ column_mapping = {
138
+ 'education': 'educ',
139
+ 'hispanic': 'hisp',
140
+ 'nodegree': 'nodegr'
141
+ }
142
+ df = df.rename(columns=column_mapping)
143
+
144
+ # Set appropriate dtypes
145
+ df = df.astype({
146
+ 'treat': np.int64, 'age': np.int64, 'educ': np.int64,
147
+ 'black': np.int64, 'hisp': np.int64, 'married': np.int64,
148
+ 'nodegr': np.int64, 're74': np.float64, 're75': np.float64,
149
+ 're78': np.float64
150
+ })
151
+ else:
152
+ # Load full LaLonde dataset
153
+ filepath = data_dir / "LaLonde.csv"
154
+ df = pd.read_csv(filepath)
155
+ # Column names already standardized in this file
156
+
157
+ if return_X_y:
158
+ y = df['treat']
159
+ X = df.drop('treat', axis=1)
160
+ return X, y
161
+ else:
162
+ return df
163
+
164
+
165
+ def load_lalonde_psid_combined(
166
+ psid_version: str = 'main',
167
+ return_combined: bool = True
168
+ ) -> Union[Tuple[pd.DataFrame, pd.DataFrame], pd.DataFrame]:
169
+ """Load NSW experimental and PSID control group data for evaluation bias tests.
170
+
171
+ Parameters
172
+ ----------
173
+ psid_version : {'main', 'controls2', 'controls3'}, default='main'
174
+ PSID control group version:
175
+
176
+ - ``'main'``: Full PSID controls (2490 observations)
177
+ - ``'controls2'``: Restricted PSID controls (253 observations)
178
+ - ``'controls3'``: Further restricted PSID controls (128 observations)
179
+
180
+ return_combined : bool, default=True
181
+ If True (default), return combined DataFrame.
182
+ If False, return (nsw_exp, psid_ctrl) tuple.
183
+
184
+ Returns
185
+ -------
186
+ combined : DataFrame (default)
187
+ Combined DataFrame with standardized column names.
188
+
189
+ - main version: (3212, 11)
190
+ - controls2 version: (975, 11)
191
+ - controls3 version: (850, 11)
192
+
193
+ (nsw_exp, psid_ctrl) : tuple of DataFrame (when return_combined=False)
194
+ Tuple of NSW experimental sample and PSID control sample.
195
+
196
+ - nsw_exp: (722, 10) - NSW experimental sample (missing re74)
197
+ - psid_ctrl: (2490, 11) or (253, 11) or (128, 11) - PSID controls
198
+
199
+ Notes
200
+ -----
201
+ Used for reproducing evaluation bias tests from Smith and Todd (2005) and
202
+ the original CBPS paper. The NSW sample contains experimental participants,
203
+ while the PSID samples serve as observational comparison groups.
204
+
205
+ **Data Dimensions**
206
+
207
+ - main: nsw=(722, 10), psid=(2490, 11), combined=(3212, 11)
208
+ - controls2: nsw=(722, 10), psid=(253, 11), combined=(975, 11)
209
+ - controls3: nsw=(722, 10), psid=(128, 11), combined=(850, 11)
210
+
211
+ References
212
+ ----------
213
+ Smith, J. A. and Todd, P. E. (2005). Does matching overcome LaLonde's
214
+ critique of nonexperimental estimators? *Journal of Econometrics*,
215
+ 125(1-2), 305-353.
216
+
217
+ Examples
218
+ --------
219
+ >>> df = load_lalonde_psid_combined()
220
+ >>> df.shape
221
+ (3212, 11)
222
+ >>> int((df['treat'] == 0).sum()), int((df['treat'] == 1).sum())
223
+ (2915, 297)
224
+ >>> 'educ' in df.columns
225
+ True
226
+
227
+ >>> # Return separate DataFrames
228
+ >>> nsw_exp, psid_ctrl = load_lalonde_psid_combined(return_combined=False)
229
+ >>> nsw_exp.shape, psid_ctrl.shape
230
+ ((722, 10), (2490, 11))
231
+ """
232
+ data_dir = _get_data_dir()
233
+
234
+ # Load NSW experimental sample (722 observations, 10 columns - missing re74)
235
+ nsw = pd.read_csv(data_dir / "nsw.csv")
236
+
237
+ # Standardize column names to match package convention
238
+ column_mapping = {
239
+ 'education': 'educ',
240
+ 'hispanic': 'hisp',
241
+ 'nodegree': 'nodegr'
242
+ }
243
+ nsw = nsw.rename(columns=column_mapping)
244
+
245
+ # Load corresponding PSID control group
246
+ psid_files = {
247
+ 'main': 'psid_controls.csv',
248
+ 'controls2': 'psid_controls2.csv',
249
+ 'controls3': 'psid_controls3.csv'
250
+ }
251
+
252
+ if psid_version not in psid_files:
253
+ raise ValueError(
254
+ f"psid_version must be one of {list(psid_files.keys())}, "
255
+ f"got: {psid_version}"
256
+ )
257
+
258
+ psid = pd.read_csv(data_dir / psid_files[psid_version])
259
+ # Standardize column names for PSID control sample
260
+ psid = psid.rename(columns=column_mapping)
261
+
262
+ if return_combined:
263
+ # Return combined DataFrame with consistent column ordering
264
+ # NSW sample is missing re74, which will be filled with NaN
265
+ nsw_with_re74 = nsw.copy()
266
+ if 're74' not in nsw_with_re74.columns:
267
+ nsw_with_re74['re74'] = np.nan
268
+ combined = pd.concat([nsw_with_re74, psid], ignore_index=True)
269
+ return combined
270
+ else:
271
+ # Return separate DataFrames: (nsw_exp, psid_ctrl)
272
+ return nsw, psid
@@ -0,0 +1,101 @@
1
+ """
2
+ npCBPS Simulation Data Loaders
3
+ ===============================
4
+
5
+ This module provides functions to load continuous treatment simulation data
6
+ for validating nonparametric CBPS (npCBPS) implementations.
7
+
8
+ The npCBPS methodology estimates stabilizing inverse propensity score weights
9
+ using empirical likelihood without requiring a parametric model for the
10
+ generalized propensity score, as described in Fong, Hazlett, and Imai (2018).
11
+
12
+ References
13
+ ----------
14
+ Fong, C., Hazlett, C., and Imai, K. (2018). Covariate balancing propensity
15
+ score for a continuous treatment: Application to the efficacy of political
16
+ advertisements. *The Annals of Applied Statistics*, 12(1), 156-177.
17
+ """
18
+
19
+ from pathlib import Path
20
+ import pandas as pd
21
+
22
+
23
+ def load_npcbps_continuous_sim() -> pd.DataFrame:
24
+ """Load npCBPS continuous treatment simulation data.
25
+
26
+ Returns
27
+ -------
28
+ pd.DataFrame
29
+ Simulation data (500 rows × 7 columns).
30
+
31
+ Notes
32
+ -----
33
+ **Variables**
34
+
35
+ - ``Y``: Outcome variable (continuous)
36
+ - ``T``: Treatment variable (continuous)
37
+ - ``X1``-``X5``: Covariates (multivariate normal, correlation=0.5)
38
+
39
+ **Data Characteristics**
40
+
41
+ - Observations: 500
42
+ - Covariate structure: Multivariate normal with pairwise correlation 0.5
43
+
44
+ Raises
45
+ ------
46
+ FileNotFoundError
47
+ If simulation data file does not exist.
48
+
49
+ Examples
50
+ --------
51
+ >>> from cbps.datasets import load_npcbps_continuous_sim
52
+ >>> df = load_npcbps_continuous_sim()
53
+ >>> df.shape
54
+ (500, 7)
55
+ >>> list(df.columns)
56
+ ['Y', 'T', 'X1', 'X2', 'X3', 'X4', 'X5']
57
+ """
58
+ # Get data directory (supports both installed and development modes)
59
+ package_data_dir = Path(__file__).parent.parent / "data"
60
+ if package_data_dir.exists():
61
+ data_dir = package_data_dir
62
+ else:
63
+ # Fall back to project root data directory (development mode)
64
+ data_dir = Path(__file__).parent.parent.parent.parent / "data"
65
+
66
+ if not data_dir.exists():
67
+ raise FileNotFoundError(
68
+ f"Cannot find data directory. Tried:\n"
69
+ f" 1. Package data: {package_data_dir}\n"
70
+ f" 2. Project data: {data_dir}\n"
71
+ f"Please ensure the package is properly installed with data files."
72
+ )
73
+
74
+ filepath = data_dir / "npcbps_continuous_sim.csv"
75
+
76
+ if not filepath.exists():
77
+ raise FileNotFoundError(
78
+ f"Simulation data not found: {filepath}\n"
79
+ "Please ensure the package is properly installed with data files."
80
+ )
81
+
82
+ df = pd.read_csv(filepath)
83
+
84
+ # Validate data dimensions
85
+ expected_shape = (500, 7)
86
+ if df.shape != expected_shape:
87
+ raise ValueError(
88
+ f"Unexpected data shape: {df.shape}\n"
89
+ f"Expected: {expected_shape}"
90
+ )
91
+
92
+ # Validate column names
93
+ expected_columns = ['Y', 'T', 'X1', 'X2', 'X3', 'X4', 'X5']
94
+ if list(df.columns) != expected_columns:
95
+ raise ValueError(
96
+ f"Unexpected columns: {list(df.columns)}\n"
97
+ f"Expected: {expected_columns}"
98
+ )
99
+
100
+ return df
101
+
@@ -0,0 +1,101 @@
1
+ """
2
+ Diagnostics and Visualization Module
3
+ =====================================
4
+
5
+ This module provides tools for assessing covariate balance before and after
6
+ propensity score weighting, a critical step in evaluating causal inference
7
+ methodology.
8
+
9
+ Balance Assessment
10
+ ------------------
11
+ For binary and multi-valued treatments, balance is measured via **standardized
12
+ mean differences (SMD)**. Following Austin (2009) and Stuart (2010), an SMD
13
+ below 0.1 indicates acceptable balance.
14
+
15
+ For continuous treatments, balance is assessed via **weighted Pearson
16
+ correlations** between covariates and the treatment variable. An absolute
17
+ correlation near zero indicates that the treatment is conditionally independent
18
+ of covariates given the weights.
19
+
20
+ Functions
21
+ ---------
22
+ balance_cbps
23
+ Compute standardized mean differences for binary/multi-valued treatments.
24
+
25
+ balance_cbps_continuous
26
+ Compute weighted correlations for continuous treatments.
27
+
28
+ plot_cbps
29
+ Visualize SMD before and after weighting (requires matplotlib).
30
+
31
+ plot_cbps_continuous
32
+ Visualize correlation reduction for continuous treatments.
33
+
34
+ References
35
+ ----------
36
+ Imai, K. and Ratkovic, M. (2014). Covariate balancing propensity score.
37
+ Journal of the Royal Statistical Society, Series B, 76(1), 243-263.
38
+
39
+ Fong, C., Hazlett, C., and Imai, K. (2018). Covariate balancing propensity
40
+ score for a continuous treatment. The Annals of Applied Statistics, 12(1),
41
+ 156-177.
42
+
43
+ Austin, P.C. (2009). Balance diagnostics for comparing the distribution of
44
+ baseline covariates between treatment groups in propensity-score matched
45
+ samples. Statistics in Medicine, 28(25), 3083-3107.
46
+
47
+ Stuart, E.A. (2010). Matching methods for causal inference: A review and a
48
+ look forward. Statistical Science, 25(1), 1-21.
49
+ """
50
+
51
+ from .balance import balance_cbps, balance_cbps_continuous, omnibus_balance_test
52
+ from .continuous_diagnostics import (
53
+ diagnose_cbgps_quality,
54
+ print_balance_diagnosis,
55
+ compute_f_statistic as compute_f_statistic_continuous,
56
+ compute_weighted_correlations
57
+ )
58
+ from .weights_diag import weight_diagnostics
59
+ from .overlap import check_overlap
60
+ from .normality import test_treatment_normality
61
+ from .ocbps_conditions import verify_ocbps_conditions
62
+
63
+ # plots module is imported as optional dependency
64
+ try:
65
+ from .plots import (
66
+ plot_cbps, plot_cbps_continuous,
67
+ love_plot, plot_weight_distribution, plot_ps_overlap,
68
+ )
69
+ __all__ = [
70
+ 'balance_cbps',
71
+ 'balance_cbps_continuous',
72
+ 'omnibus_balance_test',
73
+ 'plot_cbps',
74
+ 'plot_cbps_continuous',
75
+ 'love_plot',
76
+ 'plot_weight_distribution',
77
+ 'plot_ps_overlap',
78
+ 'diagnose_cbgps_quality',
79
+ 'print_balance_diagnosis',
80
+ 'compute_f_statistic_continuous',
81
+ 'compute_weighted_correlations',
82
+ 'weight_diagnostics',
83
+ 'check_overlap',
84
+ 'test_treatment_normality',
85
+ 'verify_ocbps_conditions',
86
+ ]
87
+ except ImportError:
88
+ # When matplotlib is not installed, only export balance functions
89
+ __all__ = [
90
+ 'balance_cbps',
91
+ 'balance_cbps_continuous',
92
+ 'omnibus_balance_test',
93
+ 'diagnose_cbgps_quality',
94
+ 'print_balance_diagnosis',
95
+ 'compute_f_statistic_continuous',
96
+ 'compute_weighted_correlations',
97
+ 'weight_diagnostics',
98
+ 'check_overlap',
99
+ 'test_treatment_normality',
100
+ 'verify_ocbps_conditions',
101
+ ]