Skip to content

fix: positional writes for duplicate row labels, UTC date bounds, honest join-key scoring - #383

Merged
kevincostner17 merged 1 commit into
mainfrom
fix/index-alignment-text-field-join
Sep 15, 2026
Merged

kevincostner17 merged 1 commit into
mainfrom
fix/index-alignment-text-field-join

Conversation

@kevincostner17

Copy link
Copy Markdown
Contributor

Summary

Behaviour notes: date bounds are evaluated in UTC; "" no longer counts toward exact-key overlap; int and float blocking columns now share blocks.

Tests

New module tests/test_index_alignment_and_join_keys.py:

  • duplicate-label frames (including a pd.concat-style index) give the same results as a unique-index control for clean_text, validate_fields (identifier, numeric outlier, rare category, no-spec consensus) and suggest_join_keys (with blocking and ambiguity);
  • tz-aware vs naive date bounds in both directions, UTC boundary semantics, mixed offsets;
  • NaN/None/NA/NaT/"" keys score 0 on either side;
  • int vs float key columns overlap 100% with confident matches; blocking on int vs float; _key_text edge cases (inf, NaN, 2**53, float32, bool).

All issue repros fail on main before this change and pass with it.

Verification

Closes #272
Closes #273
Refs #231 (parts 2-4)
Refs #233 (part 3)

… honest join-key scoring

clean_text (#231 part 2): changes were collected in a dict keyed by row
label and written back with .loc, so rows sharing a label (e.g. after
pd.concat) all received the last row's cleaned value. Changes are now
collected by position and written with iloc; the report still lists the
original row labels.

validate_fields (#231 part 3): suspect cells were read with
series.loc[label], which returns a Series on a duplicated label and made
_check_value raise "truth value of a Series is ambiguous". Each column is
now validated on a reset_index(drop=True) copy, and positions are mapped
back to the original labels in issues and the normalization audit.

validate_fields date bounds (#233 part 3): an offset-aware value compared
with a naive min/max bound (or the reverse) raised TypeError. Both sides
are now compared in UTC, with a naive side taken as UTC. The vectorized
prescreen parses with utc=True for the same semantics, so mixed offsets no
longer knock every cell onto the slow path.

suggest_join_keys (#231 part 4, #272, #273):
- Cells were read with frame.at[label, col], which returns a Series on a
  duplicated label, so exact matches were missed. Rows are now addressed
  by position (blocking, scoring and ambiguity resolution) and candidates
  keep the original labels.
- Values were compared through str(), so two missing keys ('nan' ==
  'nan') scored a confident 1.0 match. A field now scores 0.0 when either
  value is None/NaN/NaT/pd.NA/"", and missing values are excluded from
  exact-key overlap.
- An int key column and a NaN-promoted float column never agreed ('101'
  vs '101.0'). Key text now renders finite integral floats below 2**53
  without the trailing .0, in overlap, blocking and similarity.

Closes #272
Closes #273
Refs #231 (parts 2-4)
Refs #233 (part 3)
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 88e1de88-bdf9-4372-be8b-ccd1ed264045


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

FreshData benchmark report — performance

  • freshdata: ?
  • python: ?
  • platform: ?
fixture n_rows n_cols p50 s p95 s peak MB repair % false-repair % preserve % trust monotonic export %

Authored-code reduction (Metric 6)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant