Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Cleans messy tabular datasets in pandas end-to-end — fixing dtypes, parsing dates and numbers, standardizing text, handling missing values, removing duplicates, detecting and treating outliers, and reshaping wide/long into tidy data. Use this skill when the user asks to "clean this CSV/Excel/dataframe", "fix data types", "handle missing values / NaNs", "remove duplicates", "deal with outliers", "standardize column names or categories", "parse dates", "melt/pivot/reshape", or to build a reproduci
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-16 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-23 | ✓→✗ | ▼ Worse | 55% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 63% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 84% | 0% |
| case-14 | ✓→✓ | = Same ✓ | 111% | 0% |
Keywords: pandas, data cleaning, dtypes, missing values, NaN, imputation, duplicates, outliers, IQR, z-score, tidy data, melt, pivot, normalize, standardize, parse dates, categorical, data quality, ETL preprocessing.
This skill turns a messy DataFrame into a tidy, correctly-typed, analysis-ready dataset using a repeatable, auditable workflow. The core principle: profile first, decide explicitly, transform with logging, validate after. Never mutate data silently — every fill, drop, or cast should be a deliberate, documented choice you can defend.
Treat cleaning as a pipeline that produces (1) the cleaned DataFrame and (2) a record of decisions. Prefer chained, non-mutating transforms (df.assign(...), .pipe(...)) over scattered in-place edits so the pipeline is reproducible top-to-bottom.
scripts/profile_data.py <path> (or replicate inline) to get shape, dtypes, per-column null counts/percentages, unique counts, sample values, and candidate problems (mixed types, high-cardinality strings, numeric-looking objects, constant columns). See references/cleaning-checklist.md."1,234", "$5.00", "12%") to numbers, parse dates with explicit formats, cast low-cardinality strings to category, and use nullable dtypes (Int64, boolean, string) where missing values must coexist with non-float types. See references/dtype-conversion.md."NA", "-", "unknown", -999, empty string) from real values and convert them to NaN/pd.NA. Then choose a strategy per column — drop, constant fill, statistical impute (mean/median/mode), forward/backfill for time series, or model-based — and document why. Use the decision framework below.subset=[...]). Decide keep policy (first/last/aggregate). Watch for near-duplicates from inconsistent text (handle those in step 4 first).references/outliers-and-reshaping.md.melt to go wide→long, pivot/pivot_table for long→wide, and str.split/explode to split packed columns. Confirm the result satisfies the three tidy rules.references/cleaning-checklist.md.templates/cleaning_report.md to summarize what was done and why.| Situation | Recommended strategy | |---|---| | Column >50–60% missing, not critical | Drop the column | | A few rows missing in a required key/target | Drop those rows | | Numeric, missing-at-random, skewed | Impute median | | Numeric, roughly symmetric | Impute mean (or median for robustness) | | Categorical | Impute mode, or add explicit "Missing" category | | Time series / ordered | ffill/bfill, or interpolate (.interpolate()) | | Missingness is itself informative | Keep NaN + add boolean was_missing flag | | Need non-float ints with NaN | Cast to nullable Int64, don't fill |
Rule of thumb: imputing changes the distribution. For modeling, prefer adding a missingness indicator alongside the imputed value so the model can learn from "was missing."
Q1 - 1.5*IQR, Q3 + 1.5*IQR) or |z| > 3.Given a column price of strings like "$1,299.00", "N/A", "":
pythondf["price"] = ( df["price"] .replace({"N/A": pd.NA, "": pd.NA}) .str.replace(r"[$,]", "", regex=True) .pipe(pd.to_numeric, errors="coerce") # bad parses -> NaN ) df["price"] = df["price"].fillna(df["price"].median()) # documented: skewed
See examples/clean_messy_sales.md for a full raw→clean walkthrough with a 12-column messy sales file, and run scripts/clean_pipeline.py --help for a configurable end-to-end cleaner.
.assign/.pipe so the whole recipe is one readable, rerunnable block. Avoid sprinkling inplace=True.errors="coerce", then inspect the new NaNs — they reveal unparseable values you'd otherwise miss.pd.to_datetime(s, format="%Y-%m-%d")) to avoid silent misparsing of ambiguous 01/02/03.Int64, boolean, string) instead of forcing floats just to hold NaN."unknown", -999, "-", whitespace) left as real data, poisoning means and joins. Always normalize these to NaN first.inplace=True chaining bugs and accidental SettingWithCopyWarning from chained indexing — use .loc and reassignment.float64 columns full of 1.0/2.0 because NaN forced float — cast to Int64 after cleaning.dayfirst vs default) silently swapping day/month."Acme " and "acme" survive as distinct.pivot when index/column pairs aren't unique; use pivot_table with an explicit aggfunc.Other measured skills in the registry, with their headline benchmark lift.