# Pandas data-quality lab

Original synthetic teaching data. No real customer records. Executed with Python
3.12.0 and pandas 3.0.2; installed NumPy is 2.4.4 and tzdata is 2026.1. Current
online documentation can describe a newer patch release, so the execution record
states the actual local version. No performance claim follows from this tiny fixture.

From this directory run:

```text
python build_and_verify.py
```

This regenerates the explicitly authored CSV fixtures and runs ten reference
checks. It overwrites only the named fixture CSVs and verification-results.json
inside this lab directory. pandas is an external dependency; this lab does not
install packages or change an environment automatically.

## Grain and controls

- `orders_raw.csv`: nine rows; eight unique order IDs; P03 is an identical replay.
- `amount_paise`: nonnegative integer minor units of INR, with one genuinely
  unknown amount on P08. Do not confuse these units with the separate Python
  reporting lab's decimal INR amounts.
- Seven unique Paid orders have a **known subtotal** of 57,000 paise (INR 570.00),
  with six observed amounts and one missing amount. The complete paid value is
  unknown. All statuses have a known subtotal of 65,000 paise.
- `customers.csv`: five unique customers. C03 exists but has no region. C99 is
  absent from the dimension. These are distinct quality conditions.
- `customers_conflict.csv`: intentionally adds C02/East alongside C02/West.
  A many-to-one merge must reject it. Never choose a winner by row order.
- Paid region known subtotals: North 15,000, West 35,000, missing 7,000 paise.
  North includes P08's unknown amount. Missing region includes P04's zero and
  P07's unmatched C99 amount. Default dropped-null grouping loses 7,000 paise.
- `mixed_dates.csv`: six records, three accepted dates. D2 is 1 February under
  its declared DMY source; D3 is 2 January under MDY. D4 has unknown source format,
  D5 an impossible date, D6 a missing date. Do not infer the ambiguous dates.
- `survey.csv`: three respondents, two questions, six possible answers; four
  observed scores total 14. Missing responses remain missing.
- `coverage.csv`: Jan 1–6; Jan 4 source coverage incomplete. Jan 3 has an observed
  paid zero. Jan 6 has a paid order whose amount is unknown. Those days require
  different labels from an observed, complete day with no paid orders.
- Snapshots: P04 added, P03 removed, P02 amount changes 5,000→5,500, P01 unchanged.

`load_orders()` removes identical full-row replays and fails on conflicting
order keys. It preserves IDs as text and amounts as nullable integers.
`paid_enriched()` validates the customer merge and retains an origin indicator.
`parse_mixed_dates()` retains every original date string with its parsing status.

The corresponding articles contain further executable assertions. The programme
checker runs each opted-in Python block from this directory in a fresh process.
The reference checks do not establish source completeness in a real business.

`quality.py` adds a reusable, nonmutating row-quality summary. Article NP-0134
checks its normal and injected-failure behavior. `export_bundle.py` writes
`example-export/paid_orders.csv`, `data_dictionary.json` and `run_manifest.json`;
run it explicitly to regenerate those files. Its checks cover CSV round trips and
the known subtotal. Article NP-0140 additionally verifies output checksums. Writes
are sequential, not an atomic multi-file publication protocol.
