Universal Data Cleaner
A small, production-oriented tabular cleaning transformer for pandas + scikit-learn.
It is designed for the part of an ML pipeline that is easy to get wrong: cleaning training data and inference data with exactly the same learned rules.
Why this exists
A common notebook pattern is:
df["Age"] = df["Age"].fillna(df["Age"].median())
That leaks information when it is run before the train/test split, and it silently breaks at inference time because the median is recomputed on the new batch.
DataCleaner fits on training data, stores the learned values, and reuses them in
transform() and in saved artifacts.
What it handles
- numeric coercion and median / mean / constant imputation
- optional IQR outlier capping (learned from training data only)
- categorical mode / constant imputation
- optional unseen-category policy: keep or replace
- boolean coercion and imputation
- datetime parsing plus calendar feature extraction
- optional missing-value indicator columns
- optional constant-column dropping
- stable output schema and column order
- joblib save / load with artifact versioning
- scikit-learn
Pipeline,clone, andget_feature_names_outcompatibility
The cleaner never mutates the input DataFrame and never drops inference rows.
Install from PyPI
pip install universal-data-cleaner
The distribution name is universal-data-cleaner; the import name is
data_cleaner.
from data_cleaner import CleanerConfig, DataCleaner
Development setup with uv
git clone https://github.com/devlprkhan/universal-data-cleaner.git
cd universal-data-cleaner
uv sync
Run the test suite:
uv run pytest -q
Open the reference notebook:
uv run jupyter lab Universal_Data_Cleaner.ipynb
The notebook is also verified headlessly in CI-style environments with
nbclient; both tools are in the dev dependency group.
Install directly from GitHub
uv add "git+https://github.com/devlprkhan/universal-data-cleaner.git"
# or
pip install "git+https://github.com/devlprkhan/universal-data-cleaner.git"
Quick start
import pandas as pd
from data_cleaner import CleanerConfig, DataCleaner
train = pd.DataFrame(
{
"age": [34, 41, None, 29],
"salary": [70_000, 82_000, 91_000, 55_000],
"dept": ["IT", "HR", "IT", None],
"joined": ["2022-01-03", "2021-11-20", "2023-04-01", "bad-date"],
}
)
config = CleanerConfig(
datetime_columns=("joined",),
add_missing_indicators=True,
drop_constant_columns=True,
)
cleaner = DataCleaner(config).fit(train)
train_clean = cleaner.transform(train)
print(cleaner.cleaning_summary())
print(cleaner.outlier_summary())
For a column that should be numeric even though it arrived as dirty strings:
config = CleanerConfig(
numeric_columns=("salary",), # force numeric parsing
datetime_columns=("joined",), # parse dates instead of treating them as categories
unseen_category="keep", # or "fill" to replace unseen labels
)
Leak-safe workflow
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
cleaner = DataCleaner(config).fit(X_train)
X_train_clean = cleaner.transform(X_train)
X_test_clean = cleaner.transform(X_test) # uses train statistics only
Save the fitted cleaner with the model artifact:
cleaner.save("artifacts/data_cleaner.joblib")
restored = DataCleaner.load("artifacts/data_cleaner.joblib")
scikit-learn Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric = ["age", "salary", "joined__year"]
categorical = ["dept"]
boolean = []
preprocess = ColumnTransformer(
[
("num", StandardScaler(), numeric),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical),
("bool", "passthrough", boolean),
],
remainder="drop",
)
pipeline = Pipeline(
[
("cleaner", DataCleaner(config)),
("features", preprocess),
("model", Ridge(alpha=1.0)),
]
)
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
Public API
| Name | Purpose |
|---|---|
CleanerConfig |
Validated cleaning configuration |
DataCleaner |
Fit/transform cleaner with save/load |
profile_dataframe |
Column-level missing/type/cardinality profile |
dataset_summary |
Row/column/missing/duplicate summary |
drop_duplicate_rows |
Explicit duplicate removal helper |
assert_frame_clean |
Guard against missing/infinite output values |
Important production notes
- Fit the cleaner after splitting and only on training data.
- Do not pass IDs, free-text notes, or the target column to the cleaner.
- Keep
DataSource-style constant columns out of the model, or letdrop_constant_columns=Trueremove them. unseen_category="keep"is the default because one-hot encoders withhandle_unknown="ignore"can handle an unseen label safely.- Datetime columns are replaced by calendar features. Their missing values are represented by the generated feature NaNs plus an optional missing indicator.
- IQR capping is skipped automatically when the IQR is zero, so constant or near-constant numeric columns are not accidentally destroyed.
Project layout
.
├── data_cleaner.py # reusable library module
├── Universal_Data_Cleaner.ipynb # end-to-end reference workflow
├── tests/test_data_cleaner.py # unit and integration tests
├── pyproject.toml # package metadata and dev tooling
├── uv.lock # reproducible dependency lock
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── SECURITY.md
└── LICENSE
Contributing
Contributions are welcome. See CONTRIBUTING.md for the local setup, test command, and pull-request expectations.
License
Released under the MIT License.
Metadata
Release files for universal-data-cleaner 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| universal_data_cleaner-2.0.0.tar.gz | 16.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| universal_data_cleaner-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.5 kB
Release files / universal_data_cleaner-2.0.0.tar.gz
| Download URL | universal_data_cleaner-2.0.0.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
05a0f720d02194ba07451d773fd18f75c717e85a3e80abc6f8afec1cce72ea93
|
|
BLAKE2b-256 checksum How to use checksums |
dae9fda65a221d9669b65c188e402b52ebfee8462647695a4bb3260c9aae464d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / universal_data_cleaner-2.0.0-py3-none-any.whl
| Download URL | universal_data_cleaner-2.0.0-py3-none-any.whl |
|---|---|
| Size | 12.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a60b1b30d4d15b1c4031a63dd606fd7b50d6df32b61e5e1489da229b19ed8e0b
|
|
BLAKE2b-256 checksum How to use checksums |
9aa6f7a9dedd2009aee8c56609e8e5b2bd2e6357030968a7c1c4c3004c494111
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for universal-data-cleaner 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.