Data Science Foundations with Python & SQL

Make invalid records visible

Parse a small dataset into validated records without hiding rejected observations.

In this chapter

  • Parse a small dataset into validated records without hiding rejected observations.
  • Produce and check the chapter's practical artifact.
From evidence to decision
  1. Raw rows
  2. Typed values
  3. Rule checks
  4. Accepted + rejected

Each transition should preserve enough information to explain and reproduce the result.

Text is not yet a dataset

CSV is an exchange format, not a schema. A field containing the characters 12 is still text until the program interprets it. Empty values, extra spaces and malformed dates are common integration problems. Decide which transformations are legitimate: trimming surrounding whitespace is usually different from guessing an absent count. Keep the original row number so a rejected record can be located again.

For Community Meter, accept a row only when hours is a positive number and loans is a nonnegative integer. Do not use a broad 'except: pass' block. It produces a neat result by making unexplained records disappear. A small rejection ledger turns a silent data-quality failure into a reviewable artifact.

Work through a parser

The sample keeps data inside the script so the exercise does not depend on downloads or private records. DictReader associates each field with its header. The conversion rules then produce Python numbers. A bad count triggers a ValueError, which is recorded alongside the source line. Production importers also need limits on file size, encodings and unexpected columns; this toy parser demonstrates only the validation contract.

python
import csv
from io import StringIO
source = "day,hours,loans\nMon,10,60\nTue,8,\nSat,6,48\n"
accepted, rejected = [], []
rows = list(csv.DictReader(StringIO(source)))
for line, row in enumerate(rows, start=2):
    try:
        hours, loans = float(row["hours"]), int(row["loans"])
        if not (0 < hours <= 24) or loans < 0:
            raise ValueError("outside range")
        accepted.append((row["day"].strip(), hours, loans))
    except (ValueError, KeyError):
        rejected.append({"line": line, "reason": "invalid hours or loans"})
assert len(accepted) + len(rejected) == len(rows)
print(accepted, rejected)

Reconcile before improving

The reconciliation assertion establishes a simple invariant: every input record is either accepted or rejected. It does not mean every accepted value is true. A mistaken count of 600 instead of 60 can satisfy the type and range checks. Add a plausibility review without silently replacing unusual observations.

Do not log full private rows when a line number and rejection category are enough. In a real workplace, logs can outlive the source file and have broader access. Use synthetic fixtures for development and document retention separately. Finally, avoid overwriting the source: a corrected output and a transformation record make later review possible.

Workbench

  • Run the sample; inspect the rejection for line three.
  • Add a negative-loans row and a row with surrounding day whitespace.
  • Write a data dictionary explaining accepted types, range checks and unknown values.

Expected checks

  • Two original records are accepted and one rejected.
  • Input count always equals accepted plus rejected.

Check your understanding

What should happen to a missing loan count?

  • Assume zero silently
  • Record a rejection or a documented missing value
  • Delete the whole dataset
Answer explanation

Missing and zero have different meanings; record the choice explicitly.

Official tools & further reading

The curriculum

  1. Ask a question the data can answer — Free preview

    Define a decision, an observation and a useful denominator before writing code.

  2. Make invalid records visible — Free preview

    Parse a small dataset into validated records without hiding rejected observations.

  3. Turn an analysis into a reusable function — Sign-in access

    Separate a calculation from input/output and check its boundaries.

  4. Join tables without multiplying the story — Sign-in access

    Use keys, grouped queries and reconciliation checks to make SQL results trustworthy.

  5. Describe variation before choosing a headline — Free preview

    Distinguish typical values, unusual observations and uncertainty about the future.

  6. Design a chart that survives questions — Sign-in access

    Choose clear units, honest scales and a comparison that answers the decision.

  7. Build a pipeline that fails informatively — Sign-in access

    Connect validation, aggregation and a deterministic output with explicit failure behavior.

  8. Deliver the Community Meter decision pack — Sign-in access

    Combine code, checks and a careful recommendation in a complete local project.