Pandas data cleaning
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 reproducible cleaning pipeline before analysis or modeling.From its SKILL.md
npx -y skills add JayRHa/AgentSkills --skill pandas-data-cleaningAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.9 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
pandas Data Cleaning
Overview
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.
Workflow
-
Profile the raw data. Before changing anything, understand it. Run
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). Seereferences/cleaning-checklist.md. -
Fix structure. Standardize column names (snake_case, strip whitespace, dedupe). Set/verify the index. Drop fully-empty rows/columns and constant columns that carry no signal. Confirm one observation per row, one variable per column (tidy form). If not tidy, defer reshape to step 8.
-
Coerce dtypes. Convert numeric-looking strings (
"1,234","$5.00","12%") to numbers, parse dates with explicit formats, cast low-cardinality strings tocategory, and use nullable dtypes (Int64,boolean,string) where missing values must coexist with non-float types. Seereferences/dtype-conversion.md. -
Standardize text & categories. Trim whitespace, normalize case, collapse synonyms ("USA"/"U.S.A."/"United States"), fix encoding artifacts, and map free-text categories to a controlled vocabulary.
-
Handle missing values. First distinguish disguised missing (
"NA","-","unknown",-999, empty string) from real values and convert them toNaN/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. -
Remove duplicates. Detect exact duplicates and key-based duplicates (
subset=[...]). Decide keep policy (first/last/aggregate). Watch for near-duplicates from inconsistent text (handle those in step 4 first). -
Detect & treat outliers. Use IQR fences or z-scores to flag, then decide: keep (legitimate extreme), cap/winsorize, transform (log), or remove (data error). Never delete outliers reflexively. See
references/outliers-and-reshaping.md. -
Reshape to tidy. Use
meltto go wide→long,pivot/pivot_tablefor long→wide, andstr.split/explodeto split packed columns. Confirm the result satisfies the three tidy rules. -
Validate. Re-profile. Assert invariants: expected row count range, no unexpected nulls in required columns, dtypes correct, key uniqueness, value ranges/domains. Fail loudly if violated. See the validation section in
references/cleaning-checklist.md. -
Persist the recipe. Capture the ordered transforms in a single function/notebook cell so the same raw input always yields the same clean output. Use
templates/cleaning_report.mdto summarize what was done and why.
Missing-Value Decision Framework
| 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."
Outlier Decision Framework
- Flag, don't auto-delete. Compute IQR fences (
Q1 - 1.5*IQR,Q3 + 1.5*IQR) or |z| > 3. - Investigate. Is it a data-entry error (age = 999), a unit mistake (cm vs m), or a real rare event?
- Treat by cause: error → fix or remove; legitimate extreme but distorting → winsorize/cap at the fence or log-transform; legitimate and meaningful → keep.
- Use robust methods (IQR, median) over mean/std on skewed data, since mean/std are themselves dragged by outliers.
Worked Example (condensed)
Given a column price of strings like "$1,299.00", "N/A", "":
df["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.
Best Practices
- Profile before and after. You cannot clean what you have not measured, and you cannot trust a clean you did not verify.
- Prefer non-mutating chains. Build the cleaned frame with
.assign/.pipeso the whole recipe is one readable, rerunnable block. Avoid sprinklinginplace=True. - Coerce with
errors="coerce", then inspect the new NaNs — they reveal unparseable values you'd otherwise miss. - Use explicit date formats (
pd.to_datetime(s, format="%Y-%m-%d")) to avoid silent misparsing of ambiguous01/02/03. - Use nullable dtypes (
Int64,boolean,string) instead of forcing floats just to hold NaN. - Document every destructive choice (drop/fill/cap) in the cleaning report so reviewers can challenge it.
- Validate with assertions at the end; treat a cleaning script that produces silently-wrong data as a bug.
- Keep the raw file immutable. Always read raw, write cleaned to a new path.
Common Pitfalls
- Disguised missing values (
"unknown",-999,"-", whitespace) left as real data, poisoning means and joins. Always normalize these to NaN first. inplace=Truechaining bugs and accidentalSettingWithCopyWarningfrom chained indexing — use.locand reassignment.- Auto-deleting outliers before checking whether they're legitimate, throwing away the most interesting rows.
- Mean-imputing skewed columns, dragging the central tendency and shrinking variance.
float64columns full of1.0/2.0because NaN forced float — cast toInt64after cleaning.- Ambiguous date parsing (
dayfirstvs default) silently swapping day/month. - Dropping duplicates before standardizing text, so
"Acme "and"acme"survive as distinct. - Pivot collisions — using
pivotwhen index/column pairs aren't unique; usepivot_tablewith an explicitaggfunc. - No post-clean validation, shipping a dataset whose row count or domain quietly broke.
What ships with it: 7 files
25.7 KB alongside SKILL.md, 2 of them executable
examples/
- clean_messy_sales.md4.0 KB
references/
- cleaning-checklist.md3.7 KB
- dtype-conversion.md2.9 KB
- outliers-and-reshaping.md2.9 KB
scripts/
- clean_pipeline.pyruns5.7 KB
- profile_data.pyruns4.2 KB
templates/
- cleaning_report.md2.2 KB