A linter for your data — detect mixed formats, misused fields, placeholder floods, and phantom duplicates in Excel and CSV files
Project description
Data Hygiene Auditor
A linter for your data. Point it at a spreadsheet, get back every inconsistency, placeholder, and hidden duplicate — with severity ratings, root causes, and fix code.
pip install data-hygiene-auditor
data-hygiene-audit --input customers.xlsx --output ./reports
Phone numbers stored seven different ways in the same column. "TBD" sitting in a status field for three years. A customer record that looks unique until you notice that whitespace and casing are the only things separating it from four others. These are the issues consultants inherit when they take over someone else's spreadsheet — and the ones nobody finds until they're already in production.
The Data Hygiene Auditor scans Excel, CSV, and TSV files for the specific real-world failure modes that show up in actual consulting engagements: mixed-format inconsistencies, fields used for the wrong purpose, placeholder values that escaped into production, and phantom duplicates hiding behind cosmetic differences.
A single run produces three reports tailored to three audiences: an HTML report for the stakeholder meeting, an Excel findings file for the person doing the cleanup, and a PDF for the deliverable folder.
What It Detects
Mixed Formats — Identifies dates, phone numbers, and currency values stored in inconsistent formats within the same column. For example, 2023-01-15 alongside Jan 15, 2023 and 01/15/2023 in one date field. The auditor recognizes 6 date patterns, 7 phone patterns, and 6 currency patterns.
Misused Fields — Flags data stored in the wrong column: reference codes in name fields, free text in currency columns, invalid email addresses, and mixed boolean representations (Y/N vs 1/0 vs Active/Inactive in the same field).
Placeholder Floods — Detects test values (Test, N/A, TBD, 000-000-0000) that persisted into production, as well as suspiciously repeated values that may indicate defaults that were never updated.
Phantom Duplicates — Finds records that appear different on the surface due to casing, whitespace, or punctuation but represent the same entity after normalization. ID columns are automatically excluded from matching, so two records with different surrogate keys but identical content are still caught.
Fuzzy Duplicates — Goes beyond normalization with fingerprint clustering (catches token reordering like "Smith John" vs "John Smith") and Levenshtein distance matching (catches typos like "Johnathan" vs "Jonathon"). Configurable similarity threshold.
Completeness Baseline — Every field receives a null/missing analysis with severity rating, including detection of whitespace-only values that look populated but carry no data.
Health Score — Every sheet and the overall file receive a 0-100 hygiene score. 90+ is clean, 70-89 needs attention, below 70 has significant issues. The score appears in all three reports and on the CLI.
Fix Suggestions — Every finding includes a copyable pandas code snippet: date normalization scripts, phone reformatting, placeholder replacement, dedup strategies. The HTML report has one-click copy buttons; the Excel report adds a "Suggested Fix" column.
Design Decisions
- Automatic field classification. Each column is inferred as one of: date, phone, currency, email, ID, name, categorical, freetext, or zipcode. The correct validation rules are applied based on the inferred type — date-format checks don't run against phone fields, and vice versa.
- Severity ratings on every finding. High, Medium, and Low thresholds are calibrated to the issue type (e.g., >30% format inconsistency = High; 10–30% = Medium).
- Plain-English impact statements. Every finding includes a "Why this matters" explanation written for non-technical stakeholders who need to understand the business consequence, not just the statistic.
- Multi-sheet support. All sheets in a workbook are audited independently.
Output
A single run produces three files:
| Output | Use Case |
|---|---|
*_audit_report.html |
Visual walkthrough with severity badges, format distribution tables, null-completeness bars, and impact explanations. Designed to present to a client or stakeholder. |
*_audit_findings.xlsx |
One row per issue. Frozen header, auto-filter, severity color-coding. Built for the person who needs to work through the fixes. |
*_audit_report.pdf |
Same substance as the HTML in a format suitable for email attachments and formal deliverables. |
See It In Action
Real outputs from a real run are committed to this repo so you can preview them without installing anything. They were produced from the deliberately-messy demo file under samples/ — a 30-row, 2-sheet workbook that exercises every detection category. The run reported 59 issues (23 High, 20 Medium, 16 Low).
| File | Description |
|---|---|
| samples/input/sample_messy_data.xlsx | Input — the messy workbook |
| samples/output/sample_messy_data_audit_report.html | HTML stakeholder report |
| samples/output/sample_messy_data_audit_findings.xlsx | Excel findings file |
| samples/output/sample_messy_data_audit_report.pdf | PDF deliverable |
Top of the HTML report. The four cards show total issue count and a breakdown by severity, generated from a 30-row, 2-sheet sample workbook designed to exercise every detection category. Below the summary, findings are organized by sheet and by column.
One column (FirstName) with multiple stacked issues at different severities. The yellow progress bar shows the missing-data rate (13.3%, with 1 whitespace-only value), and four findings are stacked underneath: a LOW high-missing-rate flag, a HIGH "code stuffed in name field" flag with a concrete example from row 16, a MEDIUM placeholder detection for "Test", and a LOW placeholder detection for "TBD".
The Phone column finding showing a HIGH severity Mixed Phone Formats issue: 17 of 26 values deviate from the dominant format. The embedded table breaks down the actual format distribution found in the data, so a reader can see exactly which variants are present and how often. Below it, additional findings on the same column flag a placeholder ("TBD") and a suspicious repetition ("(555) 123-4567" appearing 3 times).
To regenerate the outputs locally:
python audit.py --input samples/input/sample_messy_data.xlsx --output samples/output/
Installation
pip install data-hygiene-auditor
Or install from source:
pip install .
Usage
data-hygiene-audit --input <file> --output <report_directory>
Or run directly:
python audit.py --input <file> --output <report_directory>
Supports .xlsx, .xls, .csv, and .tsv files.
Options
| Flag | Description |
|---|---|
--input, -i |
Path to file, directory, or glob pattern (required) |
--output, -o |
Directory for generated reports (required) |
--json |
Also output the raw findings as structured JSON |
--threshold, -t |
Fuzzy duplicate similarity threshold, 0.0–1.0 (default: 0.85) |
--schema, -s |
Path to a schema JSON for type/completeness validation |
--generate-schema |
Infer types from the data and save a schema JSON to the given path |
--baseline, -b |
Path to a previous audit JSON for trend comparison (shows deltas) |
--rules, -r |
Path to custom rules JSON for additional checks |
--sarif |
Output findings in SARIF format (for GitHub Code Scanning) |
--export-fixes |
Export remediation plan as CSV (sorted by severity, with fix code) |
--fail-under |
Exit with code 1 if health score is below this threshold (0-100) |
--quiet, -q |
Suppress all terminal output (just write report files) |
--force |
Process files exceeding the 2M row safety limit |
--version, -V |
Print version and exit |
Example
python audit.py --input samples/input/sample_messy_data.xlsx --output ./reports
Data Hygiene Auditor
Auditing: samples/input/sample_messy_data.xlsx
[1/2] Analyzed sheet: Customers (score: 42)
[2/2] Analyzed sheet: Orders (score: 68)
Generating reports...
HTML -> ./reports/sample_messy_data_audit_report.html
Excel -> ./reports/sample_messy_data_audit_findings.xlsx
PDF -> ./reports/sample_messy_data_audit_report.pdf
Health Score: 55/100
59 issues found — High: 23 | Medium: 20 | Low: 16
Use as a Library
After pip install ., the auditor is importable as a Python library with typed results:
from data_hygiene_auditor import audit_file
result = audit_file("customers.xlsx")
print(f"Health Score: {result.overall_score}/100")
for sheet in result.sheets:
print(f"\n{sheet.name}: {sheet.health_score}/100")
for finding in sheet.findings:
print(f" [{finding.severity}] {finding.field}: {finding.description}")
if finding.fix:
print(f" Fix: {finding.fix.description}")
The audit_file() function returns an AuditResult with typed access to all findings:
result = audit_file("data.xlsx")
# Filter by severity
critical = result.high_issues
warnings = result.medium_issues
# Access raw dict for custom processing
raw = result.to_dict()
# Generate reports programmatically
result.generate_html("report.html")
result.generate_excel("findings.xlsx")
result.generate_pdf("report.pdf")
# Adjust fuzzy matching sensitivity
strict = audit_file("data.xlsx", fuzzy_threshold=0.95)
loose = audit_file("data.xlsx", fuzzy_threshold=0.70)
Works in Jupyter notebooks — call audit_file() in a cell and explore the typed results interactively.
Custom Rules
Define your own detection rules in a JSON file to enforce project-specific data standards alongside the built-in checks.
data-hygiene-audit --input data.xlsx --output ./reports --rules my_rules.json
Rule file format
{
"rules": [
{
"name": "Phone format (US)",
"description": "Phone numbers should match (XXX) XXX-XXXX format",
"severity": "High",
"condition": "regex_match",
"threshold": "^\\(\\d{3}\\) \\d{3}-\\d{4}$",
"column_pattern": "phone|tel"
}
]
}
Each rule requires: name, description, severity (High/Medium/Low), condition, and threshold.
Targeting columns
"column_pattern": "phone|tel"— regex matched against column names (case-insensitive)"columns": ["Status", "Type"]— explicit list of column names- Omit both to apply the rule to all columns
Available conditions
| Condition | Threshold | Fires when |
|---|---|---|
regex_match |
Regex string | Values don't match the pattern |
not_regex_match |
Regex string | Values match the disallowed pattern |
min_length |
Number | Values are shorter than threshold |
max_length |
Number | Values are longer than threshold |
allowed_values |
Array of strings | Values not in the allowed set (case-insensitive) |
disallowed_values |
Array of strings | Values found in the disallowed set (case-insensitive) |
max_missing_pct |
Number (0-100) | Missing percentage exceeds threshold |
See samples/rules_example.json for a working example with 4 rules.
Multi-file Mode
Pass a directory or glob pattern to audit multiple files at once:
data-hygiene-audit --input ./data/ --output ./reports
data-hygiene-audit --input "exports/*.csv" --output ./reports
Each file gets its own set of reports. The CLI shows a combined health score across all files.
CI / Pipeline Integration
Use --fail-under to gate CI pipelines on data quality:
data-hygiene-audit --input data.xlsx --output ./reports --fail-under 70
Exits with code 1 if the health score drops below the threshold.
GitHub Actions
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: ./.github/actions/audit
with:
file: data/customers.xlsx
fail-under: '70'
rules: rules.json
SARIF for Code Scanning
- name: Run audit with SARIF
run: |
pip install .
data-hygiene-audit --input data/ --output ./reports --sarif audit.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: audit.sarif
Findings appear as code scanning alerts in the GitHub Security tab.
Regenerating the Sample Data
generate_sample.py recreates the deliberately-messy demo workbook at samples/input/sample_messy_data.xlsx. Run it if you want to modify the demo data or verify that generation is reproducible. The committed outputs in samples/output/ can then be regenerated with the command shown in See It In Action.
python generate_sample.py
Requirements
- Python 3.9+
- pandas
- openpyxl
- reportlab
Releasing
To publish a new version to PyPI:
- Update
versioninpyproject.toml - Add a release entry to
CHANGELOG.md - Commit, tag, and push:
git tag v1.1.0 git push origin v1.1.0
The publish.yml workflow builds, tests, and uploads to PyPI automatically on version tags.
License
MIT — see LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file data_hygiene_auditor-1.0.0.tar.gz.
File metadata
- Download URL: data_hygiene_auditor-1.0.0.tar.gz
- Upload date:
- Size: 61.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2100466cf84a9588f32c02a61eacc3a3a89ab1ac35f93ff0041d9823365e9c97
|
|
| MD5 |
f456459626899f320543c2a14fdaf7ce
|
|
| BLAKE2b-256 |
6c2131abfae429f232a222845ad048cc6bcd7df09e200a4a7f21cbf98728b143
|
Provenance
The following attestation bundles were made for data_hygiene_auditor-1.0.0.tar.gz:
Publisher:
publish.yml on MsShawnP/data-hygiene-auditor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
data_hygiene_auditor-1.0.0.tar.gz -
Subject digest:
2100466cf84a9588f32c02a61eacc3a3a89ab1ac35f93ff0041d9823365e9c97 - Sigstore transparency entry: 1554068447
- Sigstore integration time:
-
Permalink:
MsShawnP/data-hygiene-auditor@307109d49cca90322664c063ce9a5040c0d94137 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/MsShawnP
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@307109d49cca90322664c063ce9a5040c0d94137 -
Trigger Event:
push
-
Statement type:
File details
Details for the file data_hygiene_auditor-1.0.0-py3-none-any.whl.
File metadata
- Download URL: data_hygiene_auditor-1.0.0-py3-none-any.whl
- Upload date:
- Size: 50.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adc17c0bd51568d074ab5d1c0b128ddb1900c674ed9168f5cff66b71c351bb98
|
|
| MD5 |
3d32acba87ef4ade869d4d05f7356bdf
|
|
| BLAKE2b-256 |
ba3ddcb34320ab065b771f78cb32bae24daa65f58cccef58231be1f4b1355985
|
Provenance
The following attestation bundles were made for data_hygiene_auditor-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on MsShawnP/data-hygiene-auditor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
data_hygiene_auditor-1.0.0-py3-none-any.whl -
Subject digest:
adc17c0bd51568d074ab5d1c0b128ddb1900c674ed9168f5cff66b71c351bb98 - Sigstore transparency entry: 1554068459
- Sigstore integration time:
-
Permalink:
MsShawnP/data-hygiene-auditor@307109d49cca90322664c063ce9a5040c0d94137 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/MsShawnP
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@307109d49cca90322664c063ce9a5040c0d94137 -
Trigger Event:
push
-
Statement type: