Announcing freshdata 1.0 — data cleaning that shows its work¶
2026-07-05 · Johnny Wilson Dougherty
Most data-cleaning code is a pile of fillna, dropna, and astype calls that
nobody can explain three weeks later. freshdata started as a reaction to that:
a cleaner that makes a defensible decision for every column and tells you
exactly what it did and why. Today it hits 1.0.
import pandas as pd
import freshdata as fd
df = pd.read_csv("export.csv")
cleaned, report = fd.clean(df, return_report=True)
print(report.summary())
That report is the whole point. Every decision carries a rationale, a risk
level, and a confidence score. If a NaN survives the clean, the report tells
you why it was safer to leave it.
What 1.0 actually gives you¶
A decision engine, not a fillna wrapper. freshdata profiles every column —
missing ratio, dtype, skew, cardinality, inferred role — and picks the right
action per column. It will not impute an identifier, overwrite a target/label,
force-fill free text, or drop outliers blindly.
Rules in plain English. You can hand freshdata the domain knowledge that lives in your head:
The context compiler is deterministic and offline — no LLM, no network. It understands a documented set of sentence patterns (uniqueness, protection, formats, allowed values, ranges, dedup keys). Anything it doesn't understand is surfaced as unparsed, never guessed at. Protected columns get a hard byte-identity guarantee: freshdata verifies they came out untouched.
Scale when you need it, honesty about when you don't. The accuracy-first
engine is pandas, but the safe representation steps run natively on Polars,
DuckDB, or Spark, and StreamingCleaner cleans an unbounded stream in flat
memory. When a step genuinely needs the pandas engine, the native backends fall
back — and the report says so. As of 1.0 the streaming path honors context
policies too: the policy is compiled once against the first batch and protects
those columns across the entire stream.
A trust score you can gate on. Every clean produces a 0–100 trust score
that decreases monotonically as data gets dirtier — so you can fail a pipeline
(fail_under_trust=...) instead of shipping garbage downstream.
Explainable output for ML. Typed, leakage-aware frames that drop straight into scikit-learn or XGBoost, with the decision log attached.
The part most launches skip: what it doesn't do¶
freshdata's one rule is that it never claims more than it does, so the Honest limitations page ships as a first-class document. A couple of things worth calling out on launch day:
- Calibration is honest, not magical. Out of the box, freshdata's confidence
calibration reaches an expected calibration error around 0.038 on CleanBench —
good enough to clear our 0.05 target, but not the stricter 0.03 tier. That
stricter tier needs a trained
calib-v1artifact that isn't published yet, and we say so in the docs and the benchmark output rather than hiding the gap. - Ambiguity is never resolved silently. A malformed email, a phone number
with the wrong digit count, an ambiguous date — these become suggestions or
flags, not quiet edits.
strict=Trueturns ambiguity into a hard error.
The CleanBench results are committed to the repo and reproducible with a single command. If a number looks too good, reproduce it.
Get started¶
Then read the Quickstart, skim the Honest limitations, and clean something messy. If freshdata makes a call you disagree with, the report will tell you exactly which decision to override — and issues and PRs are very welcome.
Thanks to everyone who filed bugs, argued about defaults, and pushed on the "show your work" principle that got us here.
— Johnny