From d6ca1d5d31bcee4b9c35961ea6cdd220ecdbef03 Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Wed, 16 Sep 2026 13:06:12 +0100 Subject: [PATCH 1/3] Declare subpackages instead of shipping the source tree as package data packages = ["microimpute"] with package-data "**/*" meant the subpackages reached the wheel only as package data, swept in by a glob that also collected whatever else was in the working tree. A wheel built from a tree with compiled bytecode present carried 27 __pycache__ entries and around 500 KB of build-host bytecode, so wheel contents were a function of the builder's working directory rather than the source. setuptools.packages.find declares them properly. Verified: with 54 .pyc files present in the tree, the built wheel now contains none, and every subpackage imports from the installed wheel. Also adds py.typed, so the annotations become visible to downstream consumers; the two missing authors, which left the paper's corresponding author out of the PyPI metadata; and classifiers and project URLs, with no repository link previously on the PyPI page. The absent LICENSE file is #197 and is not addressed here. Fixes #211 --- changelog.d/211.fixed.md | 1 + microimpute/py.typed | 0 pyproject.toml | 19 +++++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 changelog.d/211.fixed.md create mode 100644 microimpute/py.typed diff --git a/changelog.d/211.fixed.md b/changelog.d/211.fixed.md new file mode 100644 index 00000000..c08e656c --- /dev/null +++ b/changelog.d/211.fixed.md @@ -0,0 +1 @@ +The wheel now declares its subpackages properly instead of sweeping the source tree in as package data, so stale `__pycache__` and other working-tree files no longer ship. Adds `py.typed`, the full author list, classifiers and project URLs. diff --git a/microimpute/py.typed b/microimpute/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pyproject.toml b/pyproject.toml index 9b99f85d..229a970f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,9 +11,16 @@ version = "3.1.2" description = "Benchmarking imputation methods for microdata" readme = "README.md" authors = [ + { name = "Vahid Ahmadi" }, + { name = "Max Ghenis", email = "max@policyengine.org" }, { name = "María Juaristi", email = "juaristi@uni.minerva.edu" }, { name = "Nikhil Woodruff", email = "nikhil.woodruff@outlook.com" } ] +classifiers = [ + "Intended Audience :: Science/Research", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Information Analysis", +] requires-python = ">=3.12,<3.15" dependencies = [ "numpy>=2.0.0,<3.0.0", @@ -62,12 +69,16 @@ images = [ "kaleido>=0.2.1,<0.3.0", # For exporting plots as PNG/JPG ] -[tool.setuptools] -packages = ["microimpute"] -include-package-data = true +[project.urls] +Repository = "https://github.com/PolicyEngine/microimpute" +Documentation = "https://policyengine.github.io/microimpute" +Issues = "https://github.com/PolicyEngine/microimpute/issues" + +[tool.setuptools.packages.find] +include = ["microimpute*"] [tool.setuptools.package-data] -"microimpute" = ["**/*"] +"microimpute" = ["py.typed"] [tool.towncrier] package = "microimpute" From 60f822a5a698f9b1d54dc1788931e011313f67e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:27:26 +0100 Subject: [PATCH 2/3] Fix issues from review: format documentation examples --- docs/imputation-benchmarking/cross-validation.md | 12 ++++++------ docs/imputation-benchmarking/preprocessing.md | 8 ++++---- docs/imputation-benchmarking/visualizations.md | 8 ++------ docs/models/imputer/implement-new-model.md | 4 +--- docs/use_cases/index.md | 12 ++++++------ 5 files changed, 19 insertions(+), 25 deletions(-) diff --git a/docs/imputation-benchmarking/cross-validation.md b/docs/imputation-benchmarking/cross-validation.md index 8da745ff..c1b6f93e 100644 --- a/docs/imputation-benchmarking/cross-validation.md +++ b/docs/imputation-benchmarking/cross-validation.md @@ -41,23 +41,23 @@ Returns a dictionary containing separate results for each metric type: ```python { "quantile_loss": { - "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (mean across folds) + "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (mean across folds) "results_std": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (std across folds) "mean_train": float, "mean_test": float, "std_train": float, "std_test": float, - "variables": List[str] # numerical variables evaluated + "variables": List[str], # numerical variables evaluated }, "log_loss": { - "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles + "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles "results_std": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (std across folds) "mean_train": float, "mean_test": float, "std_train": float, "std_test": float, - "variables": List[str] # categorical variables evaluated - } + "variables": List[str], # categorical variables evaluated + }, } ``` @@ -77,7 +77,7 @@ results = cross_validate_model( data=diabetes_df, predictors=["age", "sex", "bmi", "bp"], imputed_variables=["s1", "s4"], - n_splits=5 + n_splits=5, ) # Check performance for numerical variables diff --git a/docs/imputation-benchmarking/preprocessing.md b/docs/imputation-benchmarking/preprocessing.md index 736d73ed..05693943 100644 --- a/docs/imputation-benchmarking/preprocessing.md +++ b/docs/imputation-benchmarking/preprocessing.md @@ -117,10 +117,10 @@ result = autoimpute( predictors=["age", "education"], imputed_variables=["income", "wealth"], preprocessing={ - "income": "log", # Log transform (positive values only) - "wealth": "asinh", # Asinh transform (handles zeros/negatives) - "age": "normalize" # Z-score normalization - } + "income": "log", # Log transform (positive values only) + "wealth": "asinh", # Asinh transform (handles zeros/negatives) + "age": "normalize", # Z-score normalization + }, ) ``` diff --git a/docs/imputation-benchmarking/visualizations.md b/docs/imputation-benchmarking/visualizations.md index 21dde021..fe8f87b8 100644 --- a/docs/imputation-benchmarking/visualizations.md +++ b/docs/imputation-benchmarking/visualizations.md @@ -83,11 +83,7 @@ comparison_viz = method_comparison_results( ) # Generate plot -fig = comparison_viz.plot( - title="Method comparison", - show_mean=True, - plot_type="bar" -) +fig = comparison_viz.plot(title="Method comparison", show_mean=True, plot_type="bar") fig.show() # Get summary statistics @@ -165,7 +161,7 @@ perf_viz = model_performance_results( results=cv_results, model_name="QRF", method_name="Cross-validation", - metric="quantile_loss" + metric="quantile_loss", ) fig = perf_viz.plot(title="QRF performance") diff --git a/docs/models/imputer/implement-new-model.md b/docs/models/imputer/implement-new-model.md index edf21254..794d6193 100644 --- a/docs/models/imputer/implement-new-model.md +++ b/docs/models/imputer/implement-new-model.md @@ -73,9 +73,7 @@ class NewModelResults(ImputerResults): except Exception as e: self.logger.error(f"Error during Model prediction: {str(e)}") - raise RuntimeError( - f"Failed to predict with Model: {str(e)}" - ) from e + raise RuntimeError(f"Failed to predict with Model: {str(e)}") from e ``` ## Implementing the main model class diff --git a/docs/use_cases/index.md b/docs/use_cases/index.md index 32c7af96..2c9ff07c 100644 --- a/docs/use_cases/index.md +++ b/docs/use_cases/index.md @@ -23,7 +23,7 @@ Before imputation, make sure both datasets have compatible variables. Identify c ```python # Identify common variables -common_variables = ['age', 'income', 'education', 'marital_status', 'region'] +common_variables = ["age", "income", "education", "marital_status", "region"] # Ensure variable formats match (example: education coding) education_mapping = { @@ -31,19 +31,19 @@ education_mapping = { 2: "high_school", 3: "some_college", 4: "bachelor", - 5: "graduate" + 5: "graduate", } # Apply standardization to both datasets for dataset in [scf_data, cps_data]: - dataset['education'] = dataset['education'].map(education_mapping) + dataset["education"] = dataset["education"].map(education_mapping) # Convert income to same units (thousands) - if 'income' in dataset.columns: - dataset['income'] = dataset['income'] / 1000 + if "income" in dataset.columns: + dataset["income"] = dataset["income"] / 1000 # Identify target variable in donor dataset -target_variable = ['networth'] +target_variable = ["networth"] ``` ## Performing imputation From 6a0e9b79cf84f61c0997d6a4b36fca8104239993 Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Mon, 21 Sep 2026 10:39:05 +0100 Subject: [PATCH 3/3] Add per-version classifiers, an author email, and a find exclude Per review. Adds Python 3.12/3.13/3.14 classifiers to match requires-python, an email for Vahid so all four authors render in Author-email rather than one splitting into the legacy Author field, and an explicit exclude alongside the include as belt and braces. Rewords the changelog fragment: the bloat is latent rather than shipped. CI builds from a fresh checkout with no imports, so published wheels have been clean - the problem appears when anyone builds from a tree that has been tested in. The previous wording implied released wheels were affected. Also drops the docs reformatting, which belongs to #218. --- changelog.d/211.fixed.md | 2 +- docs/imputation-benchmarking/cross-validation.md | 12 ++++++------ docs/imputation-benchmarking/preprocessing.md | 8 ++++---- docs/imputation-benchmarking/visualizations.md | 8 ++++++-- docs/models/imputer/implement-new-model.md | 4 +++- docs/use_cases/index.md | 12 ++++++------ pyproject.toml | 6 +++++- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/changelog.d/211.fixed.md b/changelog.d/211.fixed.md index c08e656c..d95aace9 100644 --- a/changelog.d/211.fixed.md +++ b/changelog.d/211.fixed.md @@ -1 +1 @@ -The wheel now declares its subpackages properly instead of sweeping the source tree in as package data, so stale `__pycache__` and other working-tree files no longer ship. Adds `py.typed`, the full author list, classifiers and project URLs. +The wheel now declares its subpackages properly instead of sweeping the source tree in as package data, so a build from a working tree containing compiled bytecode or stray files no longer ships them. Adds `py.typed`, the full author list, per-version classifiers and project URLs. diff --git a/docs/imputation-benchmarking/cross-validation.md b/docs/imputation-benchmarking/cross-validation.md index c1b6f93e..8da745ff 100644 --- a/docs/imputation-benchmarking/cross-validation.md +++ b/docs/imputation-benchmarking/cross-validation.md @@ -41,23 +41,23 @@ Returns a dictionary containing separate results for each metric type: ```python { "quantile_loss": { - "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (mean across folds) + "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (mean across folds) "results_std": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (std across folds) "mean_train": float, "mean_test": float, "std_train": float, "std_test": float, - "variables": List[str], # numerical variables evaluated + "variables": List[str] # numerical variables evaluated }, "log_loss": { - "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles + "results": pd.DataFrame, # rows: ["train", "test"], cols: quantiles "results_std": pd.DataFrame, # rows: ["train", "test"], cols: quantiles (std across folds) "mean_train": float, "mean_test": float, "std_train": float, "std_test": float, - "variables": List[str], # categorical variables evaluated - }, + "variables": List[str] # categorical variables evaluated + } } ``` @@ -77,7 +77,7 @@ results = cross_validate_model( data=diabetes_df, predictors=["age", "sex", "bmi", "bp"], imputed_variables=["s1", "s4"], - n_splits=5, + n_splits=5 ) # Check performance for numerical variables diff --git a/docs/imputation-benchmarking/preprocessing.md b/docs/imputation-benchmarking/preprocessing.md index 05693943..736d73ed 100644 --- a/docs/imputation-benchmarking/preprocessing.md +++ b/docs/imputation-benchmarking/preprocessing.md @@ -117,10 +117,10 @@ result = autoimpute( predictors=["age", "education"], imputed_variables=["income", "wealth"], preprocessing={ - "income": "log", # Log transform (positive values only) - "wealth": "asinh", # Asinh transform (handles zeros/negatives) - "age": "normalize", # Z-score normalization - }, + "income": "log", # Log transform (positive values only) + "wealth": "asinh", # Asinh transform (handles zeros/negatives) + "age": "normalize" # Z-score normalization + } ) ``` diff --git a/docs/imputation-benchmarking/visualizations.md b/docs/imputation-benchmarking/visualizations.md index fe8f87b8..21dde021 100644 --- a/docs/imputation-benchmarking/visualizations.md +++ b/docs/imputation-benchmarking/visualizations.md @@ -83,7 +83,11 @@ comparison_viz = method_comparison_results( ) # Generate plot -fig = comparison_viz.plot(title="Method comparison", show_mean=True, plot_type="bar") +fig = comparison_viz.plot( + title="Method comparison", + show_mean=True, + plot_type="bar" +) fig.show() # Get summary statistics @@ -161,7 +165,7 @@ perf_viz = model_performance_results( results=cv_results, model_name="QRF", method_name="Cross-validation", - metric="quantile_loss", + metric="quantile_loss" ) fig = perf_viz.plot(title="QRF performance") diff --git a/docs/models/imputer/implement-new-model.md b/docs/models/imputer/implement-new-model.md index 794d6193..edf21254 100644 --- a/docs/models/imputer/implement-new-model.md +++ b/docs/models/imputer/implement-new-model.md @@ -73,7 +73,9 @@ class NewModelResults(ImputerResults): except Exception as e: self.logger.error(f"Error during Model prediction: {str(e)}") - raise RuntimeError(f"Failed to predict with Model: {str(e)}") from e + raise RuntimeError( + f"Failed to predict with Model: {str(e)}" + ) from e ``` ## Implementing the main model class diff --git a/docs/use_cases/index.md b/docs/use_cases/index.md index 2c9ff07c..32c7af96 100644 --- a/docs/use_cases/index.md +++ b/docs/use_cases/index.md @@ -23,7 +23,7 @@ Before imputation, make sure both datasets have compatible variables. Identify c ```python # Identify common variables -common_variables = ["age", "income", "education", "marital_status", "region"] +common_variables = ['age', 'income', 'education', 'marital_status', 'region'] # Ensure variable formats match (example: education coding) education_mapping = { @@ -31,19 +31,19 @@ education_mapping = { 2: "high_school", 3: "some_college", 4: "bachelor", - 5: "graduate", + 5: "graduate" } # Apply standardization to both datasets for dataset in [scf_data, cps_data]: - dataset["education"] = dataset["education"].map(education_mapping) + dataset['education'] = dataset['education'].map(education_mapping) # Convert income to same units (thousands) - if "income" in dataset.columns: - dataset["income"] = dataset["income"] / 1000 + if 'income' in dataset.columns: + dataset['income'] = dataset['income'] / 1000 # Identify target variable in donor dataset -target_variable = ["networth"] +target_variable = ['networth'] ``` ## Performing imputation diff --git a/pyproject.toml b/pyproject.toml index 229a970f..088db6c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ version = "3.1.2" description = "Benchmarking imputation methods for microdata" readme = "README.md" authors = [ - { name = "Vahid Ahmadi" }, + { name = "Vahid Ahmadi", email = "vahid@policyengine.org" }, { name = "Max Ghenis", email = "max@policyengine.org" }, { name = "María Juaristi", email = "juaristi@uni.minerva.edu" }, { name = "Nikhil Woodruff", email = "nikhil.woodruff@outlook.com" } @@ -19,6 +19,9 @@ authors = [ classifiers = [ "Intended Audience :: Science/Research", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering :: Information Analysis", ] requires-python = ">=3.12,<3.15" @@ -76,6 +79,7 @@ Issues = "https://github.com/PolicyEngine/microimpute/issues" [tool.setuptools.packages.find] include = ["microimpute*"] +exclude = ["tests*", "docs*", "examples*", "experiments*"] [tool.setuptools.package-data] "microimpute" = ["py.typed"]