Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Analysis with Python (pandas, polars, DuckDB)  Module 15 – Data-quality checks you can automate

Reconciliation, audit trails and reproducibility

Reconcile rows, keys and totals between pipeline stages, keep an audit trail of every removed and changed row, and make each run reproducible with a run log.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, pandas 3.0.6, pyarrow 25.0.1 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 9 of 30 minutes free
Free: Each stage accounts for the rows it changes · Counts, row history and repeatability provide different evidence · The exercise reports unexplained losses and additions. With a pass: 3 in-depth parts and 3 interview answers, about 21 minutes.

What you will learn

  • Compare row counts, distinct keys and totals between source, intermediate and final tables
  • Record an audit trail of removed and changed rows with the step and the reason
  • Build a reproducible analysis with seeds, pinned versions, input hashes and a run log
  • Check a pipeline by running it again from a clean start and comparing the outputs

Before you start

The original sixteen-order pipeline loses six rows, but its named rules initially explain only four. Reconciliation records rows, keys and totals at each stage; a row audit trail explains specific exclusions and changes. This lesson keeps that original pipeline and the real-month source-overlap analysis, then compares count accounting, identity, run logs and the declared fingerprint policy used for repeat runs.

Each stage accounts for the rows it changes

Reconciliation proves that the numbers in a final table can be traced back to their source. For every stage of a pipeline you count the rows, the distinct keys and the total of an additive column such as revenue, and every difference between two stages is explained by a named rule with a count. An audit trail keeps the rows each rule removed and the values each step changed, with the step and the reason, so that any single row can be accounted for. Reproducibility means that the same inputs, code and versions give the same outputs, and a run log records which inputs, code and versions a run used. Together they answer the three questions every report meets sooner or later: do the numbers add up, where did this row go, and would we get the same answer again?

The habit is small: after every step, record the row count, the number of distinct keys and the total, and have each rule count the rows it removes. A step’s unexplained delta: A signed stage change remaining after the declared rule's accounted change is subtracted under a fixed reconciliation convention. is its dropped rows minus the rows its rule removed. That delta should be zero under the stated removal contract. Anything else is unexplained, and an unexplained difference is a bug until someone explains it. Here a week of 16 orders goes through four steps, chained with DataFrame.pipe:

Four steps, and a table that explains every row that leaves

week/reconcile_week.py

"""A week of orders through four steps, with a reconciliation table that explains every row that leaves."""
import pandas as pd

orders = pd.read_csv("orders.csv")
stores = pd.read_csv("stores.csv")


def drop_repeats(df):
    repeat = df.duplicated()
    return df[~repeat], int(repeat.sum())  # the rows a rule removes, counted by the rule itself


def exclude_cancelled(df):
    cancelled = df["status"].eq("cancelled")
    return df[~cancelled], int(cancelled.sum())


def add_region(df):
    return df.merge(stores, on="store"), 0  # adds a column; no rule removes rows here


def keep_sales(df):
    returned = df["status"].eq("returned")
    return df[~returned], int(returned.sum())


def stage(df, name, by_rule=0):
    return {"stage": name, "rows": len(df), "orders": df["order_id"].nunique(), "amount": df["amount"].sum(), "by rule": by_rule}


table = [stage(orders, "source")]
df = orders
for step in [drop_repeats, exclude_cancelled, add_region, keep_sales]:
    df, by_rule = df.pipe(step)
    table.append(stage(df, step.__name__, by_rule))

recon = pd.DataFrame(table)
recon["dropped"] = (-recon["rows"].diff()).fillna(0).astype(int)
recon["unexplained"] = recon["dropped"] - recon["by rule"]
print(recon.to_string(index=False))
lost = recon[recon["unexplained"] != 0]
for row in lost.itertuples():
    print(f"\n{row.stage} lost {row.unexplained} rows that no rule removed")

Output

            stage  rows  orders  amount  by rule  dropped  unexplained
           source    16      15 17040.0        0        0            0
     drop_repeats    15      15 15540.0        1        1            0
exclude_cancelled    13      13 14100.0        2        2            0
       add_region    11      11 12160.0        0        2            2
       keep_sales    10      10 11520.0        1        1            0

add_region lost 2 rows that no rule removed

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 reconcile_week.py

week/orders.csv

order_id,store,status,amount
A-1001,S1,delivered,1250.00
A-1002,S2,delivered,480.00
A-1003,S1,cancelled,990.00
A-1004,S3,delivered,2100.00
A-1005,S4,delivered,760.00
A-1006,S2,delivered,1500.00
A-1006,S2,delivered,1500.00
A-1007,S3,returned,640.00
A-1008,S1,delivered,330.00
A-1009,S4,delivered,1180.00
A-1010,S2,cancelled,450.00
A-1011,S3,delivered,870.00
A-1012,S1,delivered,2400.00
A-1013,S2,delivered,560.00
A-1014,S3,delivered,1320.00
A-1015,S1,delivered,710.00

week/stores.csv

store,region
S1,West
S2,South
S3,North
A week of orders falls from 16 rows to 10 over four steps; rules explain 4 of the 6 rows that leave, and a join loses 2 without a rule.source16 rows · 15 orders · 17,040drop_repeats15 rows · 15 orders · 15,540exclude_cancelled13 rows · 13 orders · 14,100add_region11 rows · 11 orders · 12,160keep_sales10 rows · 10 orders · 11,520Audit trailthe 4 rows removed by rules,each with its step and reason2 rows lost with no rule:store S4 has no region1 exact repeat2 cancelledjoin with stores1 returned

A reconciliation table as a ladder

Text description of the diagram

The source holds 16 rows, 15 distinct orders and an amount of 17,040.

  • drop_repeats removes 1 exact repeat: 15 rows, 15 orders, 15,540.
  • exclude_cancelled removes 2 cancelled orders: 13 rows, 13 orders, 14,100.
  • add_region joins the stores table: 11 rows, 11 orders, 12,160. No rule removes rows here, yet 2 rows are lost, because store S4 has no region in the stores table.
  • keep_sales removes 1 returned order: 10 rows, 10 orders, 11,520.

The 4 rows removed by rules go to the audit trail, each with its step and reason. The 2 rows lost in the join have no reason, which is what the reconciliation table flags.

Six rows leave between the source and the final table, and the rules explain four of them: one exact repeat, two cancelled orders and one return. The join that adds each store’s region was not meant to remove anything, yet it lost two rows, because store S4 is missing from the stores table and an inner join drops orders without a partner. The two orders were worth 1,940, so the week’s sales would have been reported as 11,520 instead of 13,460, and every other line of the report would have looked right. The table also shows what a correct join looks like: the same rows, the same number of orders and the same total before and after.

Compare Two Excel or CSV Files Compare two versions of a table, such as a report and its source extract, row by row in the browser.

Use the original 16 rows and two lost join rows to locate the unexplained stage.

Interactive · stage by stage Each stage accounts for the rows it changes Use the original 16 rows and two lost join rows to locate the unexplained stage.
Each stage accounts for the rows it changes, step 3 of 3. Showing Original source, Declared removals, Unexplained join. In focus: Unexplained join.Original source16 order rowsDeclared removalsFour rows explainedUnexplained joinTwo rows lost

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. The original source contains16 rows and 15 distinct order keys.
  2. One repeat, two cancellations and one return explain four of the six rows absent from the final result.
  3. The original inner join loses two store rows that its rule did not intend to remove. The result table identifies the stage instead of hiding the difference.

Checkpoint

Check: Each stage accounts for the rows it changes

  1. Question 1 of 2 What does an unexplained drop identify?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: A difference not accounted for by the declared rule

    The reconciliation highlights where the actual change exceeds its stated explanation. A plausible final total does not validate an unexplained stage change.

  2. Question 2 of 2 Six rows leave and the rules explain four. How many are unexplained?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 2 rows

    Six minus four leaves two unexplained rows.

Counts, row history and repeatability provide different evidence

A reconciliation table says how many rows each step removed; row audit trail: Retained row-level evidence of exclusions or changed values, with the responsible stage and reason under a stated identity and retention policy. say which rows and why. Audit trails keep the removed rows with the step and the reason, and every changed value with its old and new value, so that a question about one order becomes a lookup. Reproducibility is what lets anyone, including you in six months, re-run the analysis and get the same numbers. It needs the same input files, proven by their hashes, the same package versions, fixed random seeds and a log of the run. The UK government’s analysis profession makes reproducible analytical pipelines its default way of working and lists the minimum practices.

Item Details
Reconciliation The question it answers: Do the numbers add up from source to report?; What you keep: Rows, keys and totals after every step, with a rule for every change
Audit trail The question it answers: Where did this row go, and why does it differ?; What you keep: Removed rows and changed values, each with its step and reason
Reproducibility The question it answers: Would a second run give the same answer?; What you keep: Input hashes, versions, seeds and parameters in a run log

The parts below apply all three to real data. A full reconciliation of one month of the UCI Online Retail II data shows how two overlapping source sheets can inflate revenue. The count invariant, rows in equals rows out plus rows in the trail, detects the demonstrated unaccounted drop. Exact source-row accounting also needs row identity or an equivalent multiset check. A run-and-compare check re-runs the pipeline from a clean start and compares the outputs.

Keep counts, row identities and repeatability evidence separate when reading the original pipeline.

Interactive · stage by stage Counts, row history and repeatability provide different evidence Keep counts, row identities and repeatability evidence separate when reading the original pipeline.
Counts, row history and repeatability provide different evidence, step 3 of 3. Showing Reconciliation, Audit trail, Reproducibility. In focus: Reproducibility.ReconciliationCounts and totalsAudit trailRows and changed valuesReproducibilityDeclared run contract

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. The stage table records how many rows and keys exist and how totals change under each rule.
  2. The trail records which rows were removed or altered, with reasons and before/after values.
  3. Inputs, versions, parameters and output comparison policy provide evidence about repeating the declared run. Hashes and rounded fingerprints have specific limits.

Checkpoint

Check: Counts, row history and repeatability provide different evidence

  1. Question 1 of 2 Which evidence explains one missing order?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The retained row-level audit history

    Row history can identify the order and the removal or change step. A matching total count cannot locate a particular order.

  2. Question 2 of 2 The lesson distinguishes reconciliation, audit trail and repeatability. How many practices is that?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 3 practices

    Those are three different evidence practices.

The exercise reports unexplained losses and additions

Preserve the original input rows while returning stage summaries and signed differences. An added row makes the dropped count negative; clamping it to zero hides the change. Locate the first nonzero unexplained delta, including a negative one. Keep the row and key definitions unchanged and compare the summary with the untouched source frame.

Exercise · Medium · Python

Reconcile a pipeline and find the step that loses rows

A pipeline is a list of steps. Each step is a pair (name, function), and function(rows) returns two things: the rows after the step, and the number of rows its own rule removed (0 for a step, such as a join, that is not meant to remove anything). Rows are dictionaries of text read from a CSV file, and a value is missing when it is empty or holds only spaces. Write three functions in recon.py.

  1. stage_stats(rows, key, amount) returns {"rows": ..., "keys": ..., "total": ...}: the number of rows, the number of distinct non-missing values of key, and the sum of the non-missing values of amount, converted with float() and rounded to 2 decimal places.
  2. reconcile(rows, steps, key, amount) returns the reconciliation table as a list of dictionaries: first the source, then one entry per step, in order. Each entry has "stage" (the name, or "source"), the three numbers of stage_stats for the rows after that stage, "dropped" (rows before the step minus rows after it, so a step that adds rows has a negative value) and "unexplained" (the dropped rows minus the rows its rule removed). The source has 0 for both.
  3. first_unexplained(table) returns the name of the first stage whose "unexplained" is not 0, or None when every change is explained.

Do not change the rows you are given. The sample tests run a small pipeline in which one step loses rows that no rule removed, and another in which a step adds rows.

Starter code · recon.py

def missing(value):
    """A cell read from a CSV is missing when it is empty or holds only spaces."""
    return value is None or str(value).strip() == ""


def stage_stats(rows, key, amount):
    # The number of rows, the number of distinct non-missing keys, and the total of the non-missing amounts
    # (the amounts are text: convert them with float), rounded to 2 decimal places.
    return {"rows": 0, "keys": 0, "total": 0}


def reconcile(rows, steps, key, amount):
    # One dictionary for the source, then one per step: its stage_stats, the rows it dropped, and the dropped rows
    # that its rule does not explain. Each step is a pair (name, function); function(rows) returns
    # (rows_after, rows_removed_by_its_rule).
    return [{"stage": "source", **stage_stats(rows, key, amount), "dropped": 0, "unexplained": 0}]


def first_unexplained(table):
    # The name of the first stage whose "unexplained" is not 0, or None.
    return None
The sample tests · test_recon.py
from recon import first_unexplained, reconcile, stage_stats

ORDERS = [
    {"order_id": "B-1", "store": "S1", "status": "delivered", "amount": "1250.00"},
    {"order_id": "B-2", "store": "S2", "status": "delivered", "amount": "480.50"},
    {"order_id": "B-2", "store": "S2", "status": "delivered", "amount": "480.50"},
    {"order_id": "B-3", "store": "S5", "status": "delivered", "amount": "760.00"},
    {"order_id": "B-4", "store": "S1", "status": "cancelled", "amount": "990.00"},
    {"order_id": "B-5", "store": "S3", "status": "delivered", "amount": ""},
    {"order_id": "", "store": "S3", "status": "delivered", "amount": "100.00"},
    {"order_id": "B-6", "store": "S5", "status": "delivered", "amount": "1180.25"},
]
REGIONS = {"S1": "West", "S2": "South", "S3": "North"}


def drop_repeats(rows):
    seen, out = set(), []
    for row in rows:
        marker = tuple(sorted(row.items()))
        if marker not in seen:
            out.append(row)
        seen.add(marker)
    return out, len(rows) - len(out)


def exclude_cancelled(rows):
    out = [r for r in rows if r["status"] != "cancelled"]
    return out, len(rows) - len(out)


def add_region(rows):
    """Looks up each store's region: an inner join, so stores without a region disappear."""
    return [{**r, "region": REGIONS[r["store"]]} for r in rows if r["store"] in REGIONS], 0


def one_row_per_item(rows):
    """A fan-out: every order of S1 is split into two items, so rows are added."""
    out = []
    for r in rows:
        out.extend([r, {**r, "amount": "0"}] if r["store"] == "S1" else [r])
    return out, 0


def test_stage_stats():
    """counts rows, distinct non-missing keys and the total of the non-missing amounts"""
    assert stage_stats(ORDERS, "order_id", "amount") == {"rows": 8, "keys": 6, "total": 5241.25}
    assert stage_stats([], "order_id", "amount") == {"rows": 0, "keys": 0, "total": 0}


def test_table_rows_and_columns():
    """has one row for the source and one for each step, in order"""
    table = reconcile(ORDERS, [("drop_repeats", drop_repeats), ("exclude_cancelled", exclude_cancelled)], "order_id", "amount")
    assert [r["stage"] for r in table] == ["source", "drop_repeats", "exclude_cancelled"]
    assert table[0] == {"stage": "source", "rows": 8, "keys": 6, "total": 5241.25, "dropped": 0, "unexplained": 0}
    assert table[1] == {"stage": "drop_repeats", "rows": 7, "keys": 6, "total": 4760.75, "dropped": 1, "unexplained": 0}
    assert table[2] == {"stage": "exclude_cancelled", "rows": 6, "keys": 5, "total": 3770.75, "dropped": 1, "unexplained": 0}


def test_finds_the_join_that_loses_rows():
    """flags the step whose dropped rows no rule explains"""
    steps = [("drop_repeats", drop_repeats), ("exclude_cancelled", exclude_cancelled), ("add_region", add_region)]
    table = reconcile(ORDERS, steps, "order_id", "amount")
    assert table[3] == {"stage": "add_region", "rows": 4, "keys": 3, "total": 1830.5, "dropped": 2, "unexplained": 2}
    assert first_unexplained(table) == "add_region"


def test_rows_added_are_unexplained_too():
    """a step that adds rows has a negative drop, which no rule explains either"""
    table = reconcile(ORDERS, [("one_row_per_item", one_row_per_item)], "order_id", "amount")
    assert table[1]["rows"] == 10 and table[1]["dropped"] == -2 and table[1]["unexplained"] == -2
    assert first_unexplained(table) == "one_row_per_item"


def test_clean_pipeline():
    """returns None when every change is explained, and keeps the input rows unchanged"""
    before = [dict(r) for r in ORDERS]
    table = reconcile(ORDERS, [("drop_repeats", drop_repeats)], "order_id", "amount")
    assert first_unexplained(table) is None
    assert ORDERS == before
A hint

Build the source entry first, then loop over the steps. Before calling a step, remember len(rows); call rows, by_rule = step(rows) and compute dropped = before - len(rows) and unexplained = dropped - by_rule. In stage_stats, collect the keys in a set, skipping missing ones, and add up float(row[amount]) only where the amount is not missing.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Retain the original five tests, including the negative-drop fan-out and unchanged input rows.

Interactive · stage by stage The exercise reports unexplained losses and additions Retain the original five tests, including the negative-drop fan-out and unchanged input rows.
The exercise reports unexplained losses and additions, step 3 of 3. Showing Original stats, Explained stages, Fan-out stage. In focus: Fan-out stage.Original stats8 rows;6 keysExplained stagesRepeat and cancellationFan-out stageNegative drop: -2

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. The original exercise input has eight rows and six distinct non-missing keys.
  2. The original tests check each stage's row count, distinct keys and total while accounting for its stated removal rule.
  3. The original fan-out adds two rows, so dropped is negative and unexplained remains negative. Do not clamp additions to zero.

Checkpoint

Check: The exercise reports unexplained losses and additions

  1. Question 1 of 2 Can an added-row stage have a negative drop?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Yes, before minus after is negative when rows are added

    The sign distinguishes unexpected additions from removals. Clamping would hide the original fan-out invariant failure.

  2. Question 2 of 2 The stage grows from eight to ten rows. What is before minus after?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: -2 rows

    Eight minus ten is negative two.

One real month of Online Retail II, reconciled row by row Pro: opens with a Pro, Premium or Ultimate pass

Real data hides its biggest problems in the source files themselves. This part reconciles one month of the UCI Online Retail II workbook from the 65,004 rows read to the analysis table, with the rows, invoices, customers and revenue of every step.

In this part:

  • Count each source before stacking overlapping sheets
  • Every removed group needs its own count and meaning
  • Report a result with the exclusions that define it

3 diagrams · 1 runnable example (reconcile_december.py, steps.py) · about 7 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Audit trails: every removed row and changed value, with its reason Pro: opens with a Pro, Premium or Ultimate pass

An audit trail is only useful if it is complete, and the commonest way to lose completeness is a filter that removes more than its rule says. This part keeps the removed rows and changed values of a pipeline, checks the trail with an invariant, and answers the auditor’s question in one lookup.

In this part:

  • A complete trail needs row identity as well as counts

1 diagram · 1 runnable example (audit_trail.py, orders.csv) · about 4 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Reproducible runs: input hashes, versions, seeds and a second run that must agree Pro: opens with a Pro, Premium or Ultimate pass

A reproducible run is one you can prove you could repeat. This part writes a run log for the December pipeline, re-runs the pipeline from a clean start and compares the outputs, and reproduces three ways in which a second run differs from the first.

In this part:

  • A run log records the inputs and comparison policy
  • Reproducibility depends on more than a seed
  • Keep code, validation and audit evidence together

3 diagrams · 1 runnable example (Python) · about 7 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Practise the original quality contracts

Practice that adapts to you

Practise this lesson

6 questions about 10 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 A stage drops15 rows but explains12. How many are unexplained?

    Type a number.

    Show the answer: Question 1 of 6

    Answer: 3 rows

    The unexplained difference is 15 minus 12, or three.

  2. Question 2 of 6 A report keeps counts, row history and a run log. How many evidence categories is that?

    Type a number.

    Show the answer: Question 2 of 6

    Answer: 3 categories

    The three named categories are retained.

  3. Question 3 of 6 A stage grows from 12 to 15 rows. What signed drop is recorded?

    Type a number.

    Show the answer: Question 3 of 6

    Answer: -3 rows

    Twelve minus 15 is negative three.

  4. Question 4 of 6 A step starts with 4,210 rows and ends with 4,105. Its rule removed 98 cancelled orders. How many rows does the reconciliation table list as unexplained?

    Type a number.

    Show the answer: Question 4 of 6

    Answer: 7 rows

    The step dropped 4,210 − 4,105 = 105 rows, and its rule accounts for 98 of them, so 7 rows left without a reason. They are a bug until someone explains them, for example missing values that a filter dropped along with the cancellations.

  5. Question 5 of 6 A step removes the cancellation lines of a month, and the revenue total of the table rises. What explains it?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: Cancellation lines carry negative quantities, so removing them removes negative revenue

    A cancellation is usually recorded as a line with a negative quantity, so it holds negative revenue. Taking such lines out lowers the row count and raises the total. A reconciliation table explains the direction of each change, not only its size.

  6. Question 6 of 6 An auditor asks why one particular order is missing from the report. Which practice answers the question directly?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: The audit trail, which keeps every removed row with the step and the reason

    The reconciliation table says how many rows each step removed; the audit trail says which rows and why, so the answer for one order is a lookup. Seeds and lock files make a run repeatable, which matters when the question is whether a second run gives the same report.

Interview questions

  1. Fresher · Warm-up

    What is a reconciliation table, and what goes in each row?

    A reconciliation table follows data through a pipeline, with one row per stage: the source, then each step, then the final table. Each row holds the number of rows, the number of distinct keys, such as order ids, and the total of an additive column, such as revenue, after that stage, together with the rows the step removed and the rows its rule accounts for. A filter's rule should explain every row it drops, and a join that only adds columns should leave rows, keys and totals unchanged. Any difference that no rule explains is a bug until someone explains it, for example orders lost in an inner join because a lookup table is missing a value. The table is cheap to build with a small helper called after every step, and it turns "the numbers look right" into a check.

  2. Mid to senior

    An auditor picks one order from the source system and asks why it is not in your quarterly revenue report. What must you have kept to answer within minutes, and how do you answer?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A scheduled job's output file gets a different SHA-256 hash every night, even when its input files are byte for byte the same. How do you tell whether the results really changed, and how do you make the run reproducible?

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    For the same month, the CRM reports 48,210 active customers, billing reports 46,955 and your analysis 47,388. Leadership wants one number by the end of the week. Plan the reconciliation: what do you compare, in what order, and what do you report?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Record rows, distinct keys and the total after every step, and let every rule count what it removes; any difference without a rule is unexplained.
  • A step that only adds columns, such as a lookup join, must leave rows, keys and totals unchanged.
  • An audit trail keeps removed rows and changed values with the step and reason, so one row’s history is a lookup.
  • Reproducibility needs the same inputs, versions and seeds, recorded in a run log next to the report.

Terms in this lesson

fingerprint policy
The canonical representation hashed for result comparison, including its ordering, type and numerical precision choices.
identity accounting
A check that the required source row identities or row multiset are accounted for in the output and removal trail, beyond equal counts alone.
row audit trail
Retained row-level evidence of exclusions or changed values, with the responsible stage and reason under a stated identity and retention policy.
unexplained delta
A signed stage change remaining after the declared rule's accounted change is subtracted under a fixed reconciliation convention.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.