-
-
Notifications
You must be signed in to change notification settings - Fork 0
Concepts
A recipe is a versionable YAML document that describes exactly how to clean a table: column renames, ordered ops, frame-level ops (dedup), and validation rules.
version: 1
columns:
"Customer Name":
rename_to: customer_name
ops: [strip_whitespace, title_case]
validate:
- {column: email, check: valid_email, on_fail: quarantine}- Generated by CleanFrame (rules or LLM), edited by you, owned by git.
- Replayed by the executor with zero AI calls.
- Round-trips losslessly:
Recipe.from_yaml(r.to_yaml()) == r.
See Recipe specification.
DataFrame / file
→ profile (semantic types, stats)
→ detect (Issues + proposed ops)
→ plan (RulesPlanner or LLMPlanner → Recipe)
→ execute (pure pandas, 4 phases)
→ validate (quarantine / error / warn / drop / null)
→ diff (cell-level lineage)
cf.report() stops after detect. cf.apply_recipe() skips profile/detect/plan
and only executes (+ optional drift check).
Modes gate which detector proposals enter the recipe (by confidence) and how strict execution/validation is:
| Mode | Min confidence | Drift / missing columns |
|---|---|---|
review |
0.50 | Warn / skip missing columns |
auto |
0.65 | Same as review for missing cols |
strict |
0.85 | Raise on drift, missing columns, validation failures |
Failed validation rows go to result.quarantine with a _cf_quarantine_reason
column by default (on_fail: quarantine). CleanFrame never silently drops
bad rows unless you explicitly set on_fail: drop.
Every changed cell is attributed: row_id, column, before, after. The executor
keeps a stable positional row id and column lineage through renames and derived
columns (extract_currency).
On very large dirty frames, detail storage is capped (default 100k cells) so memory stays bounded; the count of changed cells remains exact. See Production guide.
When you replay a recipe, CleanFrame fingerprints the incoming frame and compares
it to source_fingerprint in the recipe. Renamed columns, new formats, and type
shifts raise DriftError by default (on_drift="error").
cf.apply_recipe(df, recipe, on_drift="error") # default
cf.apply_recipe(df, recipe, on_drift="warn")
cf.apply_recipe(df, recipe, on_drift="ignore") # dangerous; CLI --forcecf.suggest_update() applies safe mechanical patches (repoint renamed sources,
extend parse_date formats).
Optional. When provided, drives:
- Column mapping (fuzzy name + type)
- Validation synthesis (
not_null,>= min,valid_email, …)
Infer a draft with cf.infer_schema(df) or cleanframe infer-schema FILE.
- Determinism — same input → same recipe / frame / diff
- LLM never touches rows — metadata (or approved sample) only
- Recipes round-trip — YAML ↔ object lossless
- Nothing silently imputed or dropped
- Every changed cell is tracked
Details in CONTRIBUTING.md.