Michael 438bc0f84d perf: 1 GB-class file efficiency for the analyzer + gate pipeline
Six targeted changes that drop the user-visible analyzer scan time from
"go for coffee" to sub-second on 1 GB inputs and reduce peak RSS by ~10×.

src/core/io.py
  - detect_encoding: open + read sample bytes instead of read_bytes()[:N].
    Was allocating the full file in memory just to slice the head; on a
    1 GB input this saves a 1 GB intermediate allocation.
  - repair_bytes: byte-level smart-quote fold via bytes.replace when the
    input is UTF-8. The probe (b"\\xe2\\x80" / b"\\xc2\\xab" / b"\\xc2\\xbb")
    is a single C-implemented contains check that skips the entire fold
    stage on files with no smart quotes — most of them.
  - repair_bytes: skip the per-row csv.reader walk unless a cheap byte
    scan finds a currency sigil ($/€/£), the delimiter is non-comma, the
    decoder substituted U+FFFD, or _has_field_count_mismatch detects an
    unquoted-delimiter row. csv.reader was the dominant cost in
    repair_bytes on big files (materializes a list of every row).
  - _has_field_count_mismatch: hand-rolled quote-state walker; one pass,
    no allocation, returns True at first mismatch. False positives just
    fall through to the slower _repair_rows pass.

src/core/analyze.py
  - _load_for_analysis: read only ~max(4KB, sample_rows × 256B × 2) head
    bytes for the analyzer's sample-mode scan. Drops analyze(sample_rows
    =1000) from "read + repair full file" to "read + repair 500KB" —
    150× faster on a 1.25 GB file. Falls back to a single full-file
    retry if pandas reports fewer rows than the cap.
  - Compiled regex character classes for hot-path detectors and a
    _vec_match_count helper that runs Series.str.contains in C instead
    of Python per-cell loops. Detectors converted: smart_punctuation,
    invisible_chars (NBSP + zero-width), whitespace_padding,
    null_like_sentinels, mojibake, encoding_uncertainty,
    mixed_case_email, leading_zero_ids.

src/core/fixes.py
  - _vectorized_translate / _vectorized_regex_sub: pandas-native string
    transforms for the fixes that are pure character maps (strip_nbsp,
    fold_smart_punctuation, strip_zero_width). Series.str.translate
    runs in C — 10-50× faster than per-cell Python.
  - _apply_to_strings: replaced inner per-cell loops with Series.map +
    boolean-mask diff for the count.
  - All fix entry points read an "inplace" flag from payload and thread
    it through the helpers.

src/core/normalize.py
  - apply_decisions: takes a single working copy at the top, then sets
    payload["inplace"] = True so each chained fix mutates that copy.
    Previously every fix did df.copy(); N fixes × 6 GB DataFrame =
    30+ GB peak. Now: one 6 GB allocation.

Validation: 765 passed, 17 xfailed (no regressions). 100 MB benchmark:

  stage                              before       after
  ------------------------------     -------      --------
  detect_encoding                    0.97s+1.3GB  ~0s + 0 MB
  analyze (sample_rows=1000)         235.76s      0.08s
  _load_for_analysis (1000 rows)     148.17s      0.01s
  repair_bytes (full file)           150s/1.25GB  2.91s/100MB

The user-visible analyzer scan dropped from minutes to sub-second on
1 GB-class files. Full-DataFrame analyze + auto_fix improvements are
more modest (~25%) because trim_whitespace and replace_null_sentinels
still need per-cell Python for the structural-shape checks, but the
hot path through these is now bounded by pandas' .map rather than a
manual for loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:13:47 +00:00

DataTools

A bundle of Python data-cleaning tools for CSV and Excel files. Two scripts ship today; more are in build.

# Tool What it does
01 Deduplicator Find and remove duplicate rows with exact + fuzzy matching, smart normalization, and interactive review.
02 Text Cleaner Trim whitespace, fold smart quotes, strip invisible / control characters, normalize Unicode, normalize line endings, optional case conversion.

Deduplicator

Features

  • Zero-config start — auto-detects encoding, delimiters, headers, and match columns
  • Fuzzy matching — Jaro-Winkler, Levenshtein, and token set ratio algorithms
  • 5 built-in normalizers — email (Gmail dot/plus), phone (E.164), name (titles/suffixes), address (USPS), string (whitespace/case)
  • Merge mode — fill missing fields in the surviving row from removed duplicates
  • 4 survivor rules — keep first, last, most complete, or most recent row per group
  • Interactive review — inspect match groups with inline checkboxes and column dropdowns, cherry-pick values, preview surviving rows live
  • Config profiles — save and reload your settings as JSON for repeatable runs
  • Dual interface — full CLI for automation, Streamlit GUI for visual review
  • Dry-run by default — preview what would change before writing anything
  • Audit trail — every run produces a match groups report and timestamped log

Quick Start

Install

pip install -r requirements.txt

CLI

# Preview duplicates (dry run — no files written)
python -m src.cli customers.csv

# Remove duplicates and save the result
python -m src.cli customers.csv --apply

# Fuzzy-match names at 80% similarity, merge missing fields
python -m src.cli customers.csv --fuzzy name --threshold 80 --merge --apply

# Interactively review each match group
python -m src.cli customers.csv --review --apply

GUI

streamlit run src/gui/app.py

Upload a file, click Find Duplicates, review match groups side-by-side, then download the cleaned result.

CLI Usage Summary

python -m src.cli INPUT_FILE [OPTIONS]

Options:
  --apply                  Write output files (default: preview only)
  --output, -o PATH        Output file path
  --subset, -s COLS        Columns to match on (comma-separated)
  --key, -k COLS           Strong-key columns for exact matching
  --fuzzy COLS             Columns to fuzzy-match
  --algorithm, -a ALG      levenshtein | jaro_winkler | token_set_ratio
  --threshold, -t N        Similarity threshold 0-100 (default: 85)
  --normalize COL:TYPE     Per-column normalizers (e.g., email:email,phone:phone)
  --survivor RULE          first | last | most-complete | most-recent
  --merge                  Fill missing fields from removed duplicates
  --review                 Interactively review each match group
  --config PATH            Load settings from a JSON config file
  --save-config PATH       Save current settings to JSON
  --sheet NAME             Excel sheet name or 0-based index
  --encoding ENC           Override auto-detected encoding
  --header-row N           0-based header row index
  --help                   Show full help

Sample Output

$ python -m src.cli samples/messy_sales.csv

Reading messy_sales.csv...
  50 rows, 8 columns
Finding duplicates...

──────────────────────────────────────────────────
  File:      messy_sales.csv
  Rows in:   50
  Rows out:  28
  Removed:   22
  Groups:    22
──────────────────────────────────────────────────

Match groups:
  Group 1: rows [1, 2] → keep row 1 (confidence: 100.0%, matched on: email)
  Group 2: rows [3, 4] → keep row 3 (confidence: 92.3%, matched on: name, phone)
  ...

This was a preview. Add --apply to write the output files.

Output Files

When --apply is used, three files are produced:

File Contents
{input}_deduplicated.csv Cleaned data with duplicates removed
{input}_removed.csv Rows that were removed
{input}_match_groups.csv Audit trail: group ID, confidence, matched columns, survivor flag

Text Cleaner

Character-level hygiene for messy CSV / Excel input. Solves the dirty-data failure modes that silently break VLOOKUPs, dedup runs, and downstream imports:

  • Trailing / leading whitespace and tabs in cells
  • Non-breaking spaces (U+00A0) hiding inside text where regular spaces should be
  • Smart quotes pasted from Word (" " ' '" " ' ')
  • Em / en dashes, ellipsis, other typographic Unicode
  • Zero-width and bidi-mark characters (U+200B, U+200C, U+200D, etc.)
  • BOMs from Excel "Save As CSV UTF-8"
  • Mixed line endings (\r\n, bare \r) inside multi-line cells
  • Control characters (U+0000-U+001F minus \t \n \r)
  • Optional Unicode NFC / NFKC normalization
  • Optional per-column case conversion (UPPER / lower / smart Title / Sentence)
# Preview what would change (dry-run)
python -m src.cli_text_clean samples/messy_text.csv

# Apply the safe defaults
python -m src.cli_text_clean samples/messy_text.csv --apply

# Title-case the name column, upper-case the SKU column
python -m src.cli_text_clean products.csv --case title:name,upper:sku --apply

# Just trim and collapse — nothing fancy
python -m src.cli_text_clean messy.csv --preset minimal --apply

Three presets: minimal (trim + collapse only), excel-hygiene (default; everything safe ON), paranoid (adds lossy NFKC fold).

Outputs {input}_cleaned.csv plus a per-cell {input}_changes.csv audit (row, column, old, new, ops applied).

See docs/CLI-REFERENCE.md for every flag.

Review & Normalize gate

Every uploaded file passes through a CSV-normalization gate before any tool page sees it. The analyzer scans for ~15 issue types — whitespace pollution, NBSP / zero-width chars, mixed line endings, BOM artifacts, encoding misdetections, smart punctuation, dirty headers, null sentinels, mojibake, and more — and tags each finding by confidence (high / medium / low) and fix action (the algorithm in src/core/fixes.py that resolves it).

In the GUI, the Review & Normalize page renders one expandable card per finding with a decision control (Auto-fix / Skip / Customize), a live before-and-after preview, an encoding-override picker for misdetected codepages, and an Advanced output options block (encoding, delimiter, line terminator) for the download. Tool pages refuse to load until the gate passes.

See docs/USER-GUIDE.md §3.3 for the user-facing walkthrough and docs/TECHNICAL.md §10.2.110.2.4 for the developer-facing API.

Documentation

  • User Guide — installation, GUI workflow, the Review & Normalize gate
  • CLI Reference — every flag with examples and recipe sections
  • Technical — architecture, gate internals, finding schema, fix registry
  • Developer Guide — extending the bundle, adding fixes / detectors

Requirements

  • Python 3.10+
  • Dependencies: pandas, openpyxl, rapidfuzz, typer, phonenumbers, loguru, tqdm, charset-normalizer

License

Proprietary. All rights reserved.

Description
Data tools development
Readme 7.7 MiB
Languages
Python 87.3%
HTML 10%
CSS 1.8%
Shell 0.4%
JavaScript 0.2%
Other 0.2%