Data quality audit
Skills library for product data scientists working with Claude
npx -y skills add vermapragya/analytics-skill --skill data-quality-auditAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 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 author says it does
Copied from the file, not written here
Runs a structured audit on a table covering nulls, duplicates, freshness, schema drift, primary key uniqueness, value distributions, and referential integrity. Use when the user mentions data quality, DQ check, audit this table, "can I trust this data", null check, dedup, freshness, or before relying on a new source.
SKILL.md
6.3 KB, as published. Nobody here has run it
Data Quality Audit
When to use this skill
Use before trusting data for analysis, modeling, or reporting. Triggers:
- "Audit this table"
- "Check the data quality of…"
- "Can I trust this data?"
- "Is fct_orders fresh?"
- "Are there duplicates in…"
- Before kicking off any modeling skill (
logistic-regression,survival-analysis, etc.)
Required inputs
| Input | Why it matters |
|---|---|
| Table name | What to audit |
| Stated primary key | What grain rows should be at |
| Expected freshness | How recent data should be |
| Critical columns | Columns where null/garbage breaks downstream |
| Foreign key columns | For referential integrity |
Workflow
Run these in order. Stop and report if any check FAILS critically.
1. Row count and time range
select
count(*) as row_count,
min(<time_col>) as earliest,
max(<time_col>) as latest,
datediff('hour', max(<time_col>), current_timestamp) as hours_since_latest
from <table>;
Expected: non-zero rows, latest timestamp within freshness SLA.
Fail if: row count = 0, or hours_since_latest > freshness SLA × 1.5.
2. Primary key uniqueness
select <pk_cols>, count(*) as dupes
from <table>
group by <pk_cols>
having count(*) > 1
limit 100;
Expected: zero rows.
Fail if: any duplicates. Investigate before proceeding.
3. Null check on critical columns
select
sum(case when <col_1> is null then 1 else 0 end) as nulls_col_1,
sum(case when <col_2> is null then 1 else 0 end) as nulls_col_2,
...
from <table>;
Expected: zero nulls in PK and critical columns.
Warn if: > 1% nulls in non-critical columns.
4. Schema check
-- Snowflake
describe table <table>;
-- or
select column_name, data_type
from information_schema.columns
where table_name = '<table_upper>';
Expected: columns and types match documentation.
Fail if: columns added/removed/retyped since last documented schema.
5. Value distribution sanity
-- For categorical columns
select <col>, count(*), count(*) * 1.0 / sum(count(*)) over () as share
from <table>
group by 1
order by 2 desc
limit 20;
-- For numeric columns
select
min(<col>),
percentile_cont(0.25) within group (order by <col>) as p25,
median(<col>) as p50,
percentile_cont(0.75) within group (order by <col>) as p75,
percentile_cont(0.99) within group (order by <col>) as p99,
max(<col>),
avg(<col>),
stddev(<col>)
from <table>;
Expected: distributions match prior periods / business expectations.
Warn if: new category dominates, or numeric tail spans multiple orders of magnitude unexpectedly.
6. Referential integrity
select count(*) as orphan_rows
from <child_table> c
left join <parent_table> p on c.<fk_col> = p.<pk_col>
where p.<pk_col> is null;
Expected: zero orphans.
Warn if: > 0.1% orphans (may indicate join logic issues downstream).
7. Time-series gaps (for event tables)
with daily_counts as (
select date_trunc('day', <time_col>) as d, count(*) as cnt
from <table>
where <time_col> >= dateadd('day', -30, current_date)
group by 1
)
select d, cnt,
lag(cnt) over (order by d) as prev_cnt,
cnt * 1.0 / lag(cnt) over (order by d) as pct_change
from daily_counts
order by d;
Warn if: any day's count is < 50% or > 200% of prior day's count.
Fail if: any day has 0 rows when prior days have data (pipeline failure).
Output format
# Data Quality Audit: <schema.table>
## TL;DR
**Status:** <PASS | WARN | FAIL>
<one-line summary of any issues>
## Checks
| Check | Result | Status | Notes |
|---|---|---|---|
| Row count | 4,872,109 | PASS | |
| Freshness | 2.1h since latest | PASS | SLA: 24h |
| PK uniqueness | 0 dupes on (user_id, event_date) | PASS | |
| Critical nulls | 0 nulls in user_id, event_at | PASS | |
| Schema | 14 cols, matches docs | PASS | |
| Value distributions | event_name top 3 = login (62%), view (28%), purchase (8%) | PASS | normal |
| Referential integrity | 0 orphan user_id vs dim_users | PASS | |
| Time-series gaps | No missing days; max daily delta +18% | PASS | |
## Issues found
<none, or:>
- **FAIL: 142 duplicate PK rows.** Pipeline likely double-loaded 2026-04-12. Investigate `dbt_upload_log` for that date.
- **WARN: 5.2% nulls in `device_type`.** Recent mobile SDK update may have changed event schema.
## Recommended actions
- <e.g., "Block analysis until dedup is fixed">
- <e.g., "Coerce null device_type to 'unknown' downstream">
## Caveats
- <e.g., "Last 24h of data has pipeline-lag and shouldn't be trusted for freshness check">
Validation checks (meta — does the audit itself work)
- All seven checks ran without query errors
- Each check has an explicit pass/warn/fail status
- Status reasoning is reproducible (numbers + thresholds, not vibes)
Edge cases & failure modes
- Slowly-changing-dimension tables: PK may not be uniqueness check candidate. Audit on
(pk, valid_from)instead. - Append-only event tables: late-arriving events are normal; treat as "data delay" not "data missing" if within reasonable bounds.
- Sampled tables: row count will be low by design. Document the sample rate and adjust expectations.
- Test/internal data mixed in: include an
is_test = falsefilter in the audit if applicable. - Sparse columns: high null % may be expected (e.g.,
referrer_urlfor organic users). Warn but don't fail.
Scripts
scripts/audit_table.sql— Parameterized Snowflake audit scriptscripts/audit_table.py— Run audit against any pandas DataFrame
python scripts/audit_table.py --input data.csv --pk user_id,event_date --critical user_id,event_at --time-col event_at
Related skills
modular-sql-ctes— fix sources structurally rather than band-aid downstreammetric-definition— pin down expected values so the audit has thresholds- Any modeling skill — always audit inputs first