Skip to content

Before Power Query removes duplicates, check whether they agree

A repeated document identifier is not enough reason to discard a row. Check that its business fields agree first. Our native Office example legitimately reduces 483 export rows to 480 documents; a separate fault-injection test rejects a repeated identifier with a different amount instead of silently choosing one.

By Renaissance Superintelligence Corporation (RSI)
· 4 min read

In this article
  1. Separate document identity from row equality
  2. Normalize only differences your policy treats as equivalent
  3. Exercise the guard with deliberate failures
  4. Put the preflight before the native refresh
  5. Run the check before an agent writes the workbook

Separate document identity from row equality

Our expense export contains DocID, PostingDate, CostCenter, Account, Amount and Vendor. It has three repeated document rows. The native query trims and types the fields, then removes duplicate DocIDs. That is valid for this fixture because the repeated rows agree. The independent validator checks the repeated payloads rather than assuming the duplicate identifiers are harmless.

Real exports can contain a correction, a partial update, a retry or an identifier that is only unique within a company. Decide the grain before deduplicating. If a document number can repeat across entities or years, the key must include those dimensions. Our test uses DocID because that is the public fixture’s stated grain; it is not a universal accounting key.

The Table.Distinct documentation explains that you cannot generally rely on which duplicate survives. Even a deterministic first-row policy would not tell you which of two conflicting financial amounts is correct. A conflict needs a review rule and source evidence.

Normalize only differences your policy treats as equivalent

The tested guard trims the identifier, parses the US-format posting date, trims the cost center and vendor, normalizes the account label’s case, and parses the amount as a Decimal. It compares the remaining business fields for every repeated key. Blank identifiers and invalid or non-finite amounts are rejected.

That means a padded identifier can agree with the unpadded identifier, and $39,494.00 can agree with 39494.000. A different amount does not agree. Those normalization rules reflect this CSV’s conventions. Do not silently reuse them for a source whose date locale, currency or identifier semantics differ.

Keep the untouched raw export alongside the normalized records. If a reviewer disputes the classification, the original bytes and source hash should still be available. A cleaning step should not erase the evidence needed to explain it.

Exercise the guard with deliberate failures

We ran six cases against the published source. The original 483-row CSV produces 480 unique documents and three identical duplicates. Appending one unchanged row adds a duplicate but does not change the unique count or October total. Appending the same identifier with $999,999.99 instead of its original amount is rejected.

A whitespace-padded identifier and an equivalent differently formatted amount are accepted after normalization. A blank identifier is rejected. Each fault is added in memory; the public CSV and real Office artifacts remain unchanged.

Local preflight results against the native workflow’s source CSV
CaseResultUnique documents / October total
Original source, 483 rowsAccepted480 / $686,500.00
One additional identical rowAccepted480 / $686,500.00
Same ID, different amountRejectedNo report accepted
Whitespace around same IDAccepted480 / $686,500.00
Empty IDRejectedNo report accepted
Equivalent numeric amount formatAccepted480 / $686,500.00

Put the preflight before the native refresh

A practical agent workflow is: preserve the source export, run the data guard, import accepted data, refresh Excel, reconcile the report and save the deliverable. If the guard finds a conflicting document, leave the existing close intact and return the conflicting rows for review. Do not guess which amount is authoritative.

The real Power Query stage creates the cleaned table in desktop Excel. The native deliverable has 480 cleaned documents, a PivotTable and editable charts. Our separate guard strengthens the source boundary; it does not claim to replace Excel’s query engine.

For your own pipeline, decide whether conflicts block the entire refresh or are quarantined under an approved policy. If you quarantine them, the report must show that some data was excluded. A silent “successful refresh” with missing documents is a misleading result.

Actual Microsoft Excel and PowerPoint desktop from the Panes expense-report workflow
The existing real Panes expense-report recording uses the original synthetic CSV. The new fault injections are local Python tests, not additional recorded desktop runs.

Run the check before an agent writes the workbook

Download the guard, result report and original CSV. The script uses only the Python standard library. Running it reproduces the original case and the deliberate fault cases; it writes a report and leaves the source unchanged.

Use the tested guard function as a starting point, then add your source’s entity, currency and document-version rules. Keep tests for legitimate replays and real conflicts. A robust pipeline should be able to explain both why a duplicate was discarded and why a conflicting row stopped delivery.

When the accepted data is ready, use Panes for the native Office steps and retain the independent workbook checks at the end. Source validation and workbook validation catch different classes of error.

python duplicate_guard.py GL-export.csv --report duplicate-report.json

Evidence and method

The original CSV feeds the real Excel Power Query workflow recorded on Panes; its source hash is retained in the downloadable report. The six new preflight cases run locally in Python 3.14.4, with deliberate bad rows added only in memory. They do not run Power Query or modify the native workbook. The original case and exact duplicates reconcile to $686,500 October expense. Single local guard timings range from about 3 to 7 milliseconds in this run; these are not native Office or task timings. No per-task bill is measured.

Sources and files

Read as plain text · Editorial method and corrections

Give your agent the desktop.

Run the Office steps on Panes. Keep your agent and the rest of your stack.