microimpute 3.1.0__tar.gz → 3.1.2__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.
- {microimpute-3.1.0 → microimpute-3.1.2}/PKG-INFO +2 -2
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/qrf.py +31 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/zero_inflated.py +37 -7
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute.egg-info/PKG-INFO +2 -2
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute.egg-info/requires.txt +1 -1
- {microimpute-3.1.0 → microimpute-3.1.2}/pyproject.toml +8 -2
- {microimpute-3.1.0 → microimpute-3.1.2}/README.md +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/autoimpute.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/autoimpute_helpers.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/imputations.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/metrics.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/comparisons/validation.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/config.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/evaluations/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/evaluations/cross_validation.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/evaluations/predictor_analysis.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/imputer.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/matching.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/mdn.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/ols.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/models/quantreg.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/utils/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/utils/dashboard_formatter.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/utils/data.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/utils/statmatch_hotdeck.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/utils/type_handling.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/visualizations/__init__.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/visualizations/comparison_plots.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute/visualizations/performance_plots.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute.egg-info/SOURCES.txt +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute.egg-info/dependency_links.txt +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/microimpute.egg-info/top_level.txt +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/setup.cfg +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_autoimpute.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_basic.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_dashboard_formatter.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_data_preprocessing.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_metrics.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_predictor_analysis.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_quantile_comparison.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_smoke_qrf.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_type_handling.py +0 -0
- {microimpute-3.1.0 → microimpute-3.1.2}/tests/test_visualizations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microimpute
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.2
|
|
4
4
|
Summary: Benchmarking imputation methods for microdata
|
|
5
5
|
Author-email: María Juaristi <juaristi@uni.minerva.edu>, Nikhil Woodruff <nikhil.woodruff@outlook.com>
|
|
6
6
|
Requires-Python: <3.15,>=3.12
|
|
@@ -21,7 +21,7 @@ Requires-Dist: psutil
|
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == "dev"
|
|
23
23
|
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == "dev"
|
|
24
|
-
Requires-Dist: ruff
|
|
24
|
+
Requires-Dist: ruff<0.17.0,>=0.16.0; extra == "dev"
|
|
25
25
|
Requires-Dist: mypy<2.0.0,>=1.2.3; extra == "dev"
|
|
26
26
|
Requires-Dist: build<2.0.0,>=1.2.0; extra == "dev"
|
|
27
27
|
Requires-Dist: towncrier>=24.8.0; extra == "dev"
|
|
@@ -50,6 +50,7 @@ class _RandomForestClassifierModel:
|
|
|
50
50
|
self.var_type = None
|
|
51
51
|
self.categories = None
|
|
52
52
|
self.label_map = None
|
|
53
|
+
self.feature_columns: List[str] = []
|
|
53
54
|
|
|
54
55
|
def fit(
|
|
55
56
|
self,
|
|
@@ -99,10 +100,24 @@ class _RandomForestClassifierModel:
|
|
|
99
100
|
fit_kwargs = {}
|
|
100
101
|
if sample_weight is not None:
|
|
101
102
|
fit_kwargs["sample_weight"] = np.asarray(sample_weight, dtype=float)
|
|
103
|
+
self.feature_columns = list(X.columns)
|
|
102
104
|
self.classifier.fit(X, y_encoded, **fit_kwargs)
|
|
103
105
|
|
|
106
|
+
def _align_features(self, X: pd.DataFrame) -> pd.DataFrame:
|
|
107
|
+
"""Reorder prediction features to the fitted sklearn column contract."""
|
|
108
|
+
if not self.feature_columns:
|
|
109
|
+
return X
|
|
110
|
+
missing = [column for column in self.feature_columns if column not in X.columns]
|
|
111
|
+
if missing:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"Prediction data is missing fitted feature columns for "
|
|
114
|
+
f"{self.output_column}: {missing}"
|
|
115
|
+
)
|
|
116
|
+
return X.loc[:, self.feature_columns]
|
|
117
|
+
|
|
104
118
|
def predict(self, X: pd.DataFrame, return_probs: bool = False) -> pd.Series:
|
|
105
119
|
"""Predict classes or probabilities."""
|
|
120
|
+
X = self._align_features(X)
|
|
106
121
|
if return_probs:
|
|
107
122
|
probs = self.classifier.predict_proba(X)
|
|
108
123
|
# Return both probabilities and the original category labels
|
|
@@ -151,6 +166,7 @@ class _QRFModel:
|
|
|
151
166
|
self.logger = logger
|
|
152
167
|
self.qrf = None
|
|
153
168
|
self.output_column = None
|
|
169
|
+
self.feature_columns: List[str] = []
|
|
154
170
|
# Create the RNG once at construction so that repeated predict()
|
|
155
171
|
# calls consume state progressively and return different draws.
|
|
156
172
|
self._rng = np.random.default_rng(self.seed)
|
|
@@ -192,8 +208,21 @@ class _QRFModel:
|
|
|
192
208
|
fit_kwargs = {}
|
|
193
209
|
if sample_weight is not None:
|
|
194
210
|
fit_kwargs["sample_weight"] = np.asarray(sample_weight, dtype=float)
|
|
211
|
+
self.feature_columns = list(X.columns)
|
|
195
212
|
self.qrf.fit(X, y.values.ravel(), **fit_kwargs)
|
|
196
213
|
|
|
214
|
+
def _align_features(self, X: pd.DataFrame) -> pd.DataFrame:
|
|
215
|
+
"""Reorder prediction features to the fitted QRF column contract."""
|
|
216
|
+
if not self.feature_columns:
|
|
217
|
+
return X
|
|
218
|
+
missing = [column for column in self.feature_columns if column not in X.columns]
|
|
219
|
+
if missing:
|
|
220
|
+
raise ValueError(
|
|
221
|
+
"Prediction data is missing fitted feature columns for "
|
|
222
|
+
f"{self.output_column}: {missing}"
|
|
223
|
+
)
|
|
224
|
+
return X.loc[:, self.feature_columns]
|
|
225
|
+
|
|
197
226
|
def predict(
|
|
198
227
|
self,
|
|
199
228
|
X: pd.DataFrame,
|
|
@@ -218,6 +247,8 @@ class _QRFModel:
|
|
|
218
247
|
across quantiles for a given row and is used when the caller
|
|
219
248
|
supplies an explicit ``quantiles`` list.
|
|
220
249
|
"""
|
|
250
|
+
X = self._align_features(X)
|
|
251
|
+
|
|
221
252
|
# Deterministic path: user asked for a specific quantile — query the
|
|
222
253
|
# QRF directly so that for any row i,
|
|
223
254
|
# prediction(q_low) <= prediction(q_mid) <= prediction(q_high).
|
|
@@ -232,6 +232,9 @@ class ZeroInflatedImputer(Imputer):
|
|
|
232
232
|
for v in imputed_variables
|
|
233
233
|
if v in self.numeric_targets or v in constant_numeric_targets
|
|
234
234
|
]
|
|
235
|
+
nested_not_numeric_categorical = list(
|
|
236
|
+
dict.fromkeys([*(not_numeric_categorical or []), *numeric_targets])
|
|
237
|
+
)
|
|
235
238
|
# Sequential (chained-equations) imputation: condition each numeric
|
|
236
239
|
# target on the original predictors plus the previously-fit numeric
|
|
237
240
|
# targets, so the imputed vector preserves cross-variable joint
|
|
@@ -255,6 +258,7 @@ class ZeroInflatedImputer(Imputer):
|
|
|
255
258
|
variable=var,
|
|
256
259
|
regime=regime,
|
|
257
260
|
y=y,
|
|
261
|
+
not_numeric_categorical=nested_not_numeric_categorical,
|
|
258
262
|
)
|
|
259
263
|
bundle["predictors"] = list(seq_predictors)
|
|
260
264
|
self._per_variable[var] = bundle
|
|
@@ -302,6 +306,7 @@ class ZeroInflatedImputer(Imputer):
|
|
|
302
306
|
variable: str,
|
|
303
307
|
regime: str,
|
|
304
308
|
y: np.ndarray,
|
|
309
|
+
not_numeric_categorical: Optional[List[str]] = None,
|
|
305
310
|
) -> Dict[str, Any]:
|
|
306
311
|
"""Fit the gate and base imputer(s) for one numeric target.
|
|
307
312
|
|
|
@@ -317,7 +322,12 @@ class ZeroInflatedImputer(Imputer):
|
|
|
317
322
|
# No gate; single base imputer on the full training set.
|
|
318
323
|
return {
|
|
319
324
|
"kind": "single",
|
|
320
|
-
"base": self._fit_base_single(
|
|
325
|
+
"base": self._fit_base_single(
|
|
326
|
+
X_train,
|
|
327
|
+
predictors,
|
|
328
|
+
variable,
|
|
329
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
330
|
+
),
|
|
321
331
|
}
|
|
322
332
|
|
|
323
333
|
if regime == REGIME_ZI_POSITIVE:
|
|
@@ -326,7 +336,10 @@ class ZeroInflatedImputer(Imputer):
|
|
|
326
336
|
clf.fit(X_pred, labels)
|
|
327
337
|
pos_mask = y > self.zero_atol
|
|
328
338
|
pos_base = self._fit_base_single(
|
|
329
|
-
X_train.loc[pos_mask],
|
|
339
|
+
X_train.loc[pos_mask],
|
|
340
|
+
predictors,
|
|
341
|
+
variable,
|
|
342
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
330
343
|
)
|
|
331
344
|
return {
|
|
332
345
|
"kind": "zi_positive",
|
|
@@ -340,7 +353,10 @@ class ZeroInflatedImputer(Imputer):
|
|
|
340
353
|
clf.fit(X_pred, labels)
|
|
341
354
|
neg_mask = y < -self.zero_atol
|
|
342
355
|
neg_base = self._fit_base_single(
|
|
343
|
-
X_train.loc[neg_mask],
|
|
356
|
+
X_train.loc[neg_mask],
|
|
357
|
+
predictors,
|
|
358
|
+
variable,
|
|
359
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
344
360
|
)
|
|
345
361
|
return {
|
|
346
362
|
"kind": "zi_negative",
|
|
@@ -360,10 +376,16 @@ class ZeroInflatedImputer(Imputer):
|
|
|
360
376
|
"kind": "sign_only",
|
|
361
377
|
"classifier": clf,
|
|
362
378
|
"positive_base": self._fit_base_single(
|
|
363
|
-
X_train.loc[pos_mask],
|
|
379
|
+
X_train.loc[pos_mask],
|
|
380
|
+
predictors,
|
|
381
|
+
variable,
|
|
382
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
364
383
|
),
|
|
365
384
|
"negative_base": self._fit_base_single(
|
|
366
|
-
X_train.loc[neg_mask],
|
|
385
|
+
X_train.loc[neg_mask],
|
|
386
|
+
predictors,
|
|
387
|
+
variable,
|
|
388
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
367
389
|
),
|
|
368
390
|
}
|
|
369
391
|
|
|
@@ -382,10 +404,16 @@ class ZeroInflatedImputer(Imputer):
|
|
|
382
404
|
"kind": "three_sign",
|
|
383
405
|
"classifier": clf,
|
|
384
406
|
"positive_base": self._fit_base_single(
|
|
385
|
-
X_train.loc[pos_mask],
|
|
407
|
+
X_train.loc[pos_mask],
|
|
408
|
+
predictors,
|
|
409
|
+
variable,
|
|
410
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
386
411
|
),
|
|
387
412
|
"negative_base": self._fit_base_single(
|
|
388
|
-
X_train.loc[neg_mask],
|
|
413
|
+
X_train.loc[neg_mask],
|
|
414
|
+
predictors,
|
|
415
|
+
variable,
|
|
416
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
389
417
|
),
|
|
390
418
|
}
|
|
391
419
|
|
|
@@ -396,6 +424,7 @@ class ZeroInflatedImputer(Imputer):
|
|
|
396
424
|
X_train: pd.DataFrame,
|
|
397
425
|
predictors: List[str],
|
|
398
426
|
variable: str,
|
|
427
|
+
not_numeric_categorical: Optional[List[str]] = None,
|
|
399
428
|
) -> ImputerResults:
|
|
400
429
|
"""Fit a single base Imputer on a (possibly filtered) slice."""
|
|
401
430
|
imputer = self.base_imputer_class(
|
|
@@ -406,6 +435,7 @@ class ZeroInflatedImputer(Imputer):
|
|
|
406
435
|
X_train=X_train,
|
|
407
436
|
predictors=predictors,
|
|
408
437
|
imputed_variables=[variable],
|
|
438
|
+
not_numeric_categorical=not_numeric_categorical,
|
|
409
439
|
)
|
|
410
440
|
|
|
411
441
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microimpute
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.2
|
|
4
4
|
Summary: Benchmarking imputation methods for microdata
|
|
5
5
|
Author-email: María Juaristi <juaristi@uni.minerva.edu>, Nikhil Woodruff <nikhil.woodruff@outlook.com>
|
|
6
6
|
Requires-Python: <3.15,>=3.12
|
|
@@ -21,7 +21,7 @@ Requires-Dist: psutil
|
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == "dev"
|
|
23
23
|
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == "dev"
|
|
24
|
-
Requires-Dist: ruff
|
|
24
|
+
Requires-Dist: ruff<0.17.0,>=0.16.0; extra == "dev"
|
|
25
25
|
Requires-Dist: mypy<2.0.0,>=1.2.3; extra == "dev"
|
|
26
26
|
Requires-Dist: build<2.0.0,>=1.2.0; extra == "dev"
|
|
27
27
|
Requires-Dist: towncrier>=24.8.0; extra == "dev"
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "microimpute"
|
|
10
|
-
version = "3.1.
|
|
10
|
+
version = "3.1.2"
|
|
11
11
|
description = "Benchmarking imputation methods for microdata"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
authors = [
|
|
@@ -35,7 +35,7 @@ dependencies = [
|
|
|
35
35
|
dev = [
|
|
36
36
|
"pytest>=8.0.0,<10.0.0",
|
|
37
37
|
"pytest-cov>=6.0.0,<8.0.0",
|
|
38
|
-
"ruff>=0.
|
|
38
|
+
"ruff>=0.16.0,<0.17.0",
|
|
39
39
|
"mypy>=1.2.3,<2.0.0",
|
|
40
40
|
"build>=1.2.0,<2.0.0",
|
|
41
41
|
"towncrier>=24.8.0",
|
|
@@ -101,3 +101,9 @@ showcontent = true
|
|
|
101
101
|
directory = "removed"
|
|
102
102
|
name = "Removed"
|
|
103
103
|
showcontent = true
|
|
104
|
+
|
|
105
|
+
[tool.ruff]
|
|
106
|
+
# Documentation is prose, not a lint surface. ruff 0.16.0 began formatting
|
|
107
|
+
# Python inside markdown code blocks, which reformatted five docs files that
|
|
108
|
+
# nobody had edited and turned the Lint job red on an untouched checkout.
|
|
109
|
+
extend-exclude = ["docs/**/*.md"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|