Skip to content

Merge main into #185 and resolve conflicts - #206

Merged
omri374 merged 14 commits into
fix/span-evaluator-two-sided-countingfrom
claude/presidio-pr-conflict-dh6ej6
Sep 10, 2026
Merged

Merge main into #185 and resolve conflicts#206
omri374 merged 14 commits into
fix/span-evaluator-two-sided-countingfrom
claude/presidio-pr-conflict-dh6ej6

Conversation

@omri374

@omri374 omri374 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Merges main into the #185 branch and resolves the conflicts. Merging this PR updates #185 so it becomes mergeable again. No evaluator logic changes beyond what #185 already contains.

Conflict resolution

Verification

  • 723 unit tests pass on the merged tree.
  • ruff check and ruff format --check are clean on the touched files.

Known limitation (unchanged from #185, candidate for a follow-up)

Same-type coverage of an annotation pools every overlapping span of that type rather than the best subset. A good single-span match can therefore be dragged below the threshold by a neighbouring same-type span that only brushes the annotation, turning one TP into one FN plus two FPs. Taking the coverage as the maximum of the best pairwise IoU and the combined IoU, and crediting only the spans in the winning selection, would remove this.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SMHDXc7d43rZLiRrunemYY

omri374 and others added 14 commits August 4, 2026 17:18
* feat: branch-level aliases (LOC->LOCATION, ORG->ORGANIZATION)

Non-leaf hierarchy nodes can now declare raw aliases via a reserved
`_aliases` key, mirroring the alias lists leaf nodes already have. The key
is skipped by every tree-walk so it never becomes a canonical entity, and
`add_alias()` on a branch records the alias instead of creating a spurious
child leaf.

Moves the coarse labels LOC/ORG onto the LOCATION/ORGANIZATION branches, so
they canonicalize to the readable branch name and match a model's
LOCATION/ORGANIZATION at the exact (leaf) level, not only at branch level.
This is what lets coarse corpora such as TAB be scored at the exact level
against models that emit the long-form labels.

Because LOC/ORG stop being canonical entities, several lookups keyed on
canonical names needed to learn about aliases:

- `to_branch()` previously returned raw aliases unchanged, so `to_branch("LOC")`
  would have silently degraded from "LOCATION" to "LOC" -- projecting into the
  wrong branch rather than failing. It now resolves aliases first. Same for
  `get_depth()`, which would otherwise raise.
- `add_alias()` accepts an alias as its subject, so add_alias("LOC", ...) works.
- `_find_node()` no longer matches the reserved `_aliases` key, which would
  otherwise let `add_alias("_aliases", x)` attach x to whichever branch the
  tree walk happened to reach first.
- `add_alias()` raises ValueError instead of silently persisting an alias that
  a descendant already claims (branch aliases are applied before the descent
  into their own subtree, so a descendant wins). The hierarchy is rolled back.

Updates two docs examples that used map({'LOCATION': 'LOC'}) as a resolution
target; LOC is no longer a valid target, and the mapping is no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ceeaa56-7275-46b5-9f57-12e9bb3c20e3

* Address PR #186 review comments

- add_alias: rollback no longer deletes an alias the target already owned.
  Appending is now tracked with an `appended` flag, so a failed call leaves
  the hierarchy exactly as it found it (negruber1).
- add_alias: remove the duplicated, unreachable rollback+raise block
  (negruber1, Copilot).
- definitions: PER moves from an empty leaf under PERSON > NAME to a
  branch alias of PERSON, matching LOC/ORG (omri374).
- hierarchy: warn at construction time when a branch alias declared in the
  hierarchy definition is shadowed by one of its own descendants. The
  add_alias() guard only covered the runtime path (omri374).
- get_depth: error message said "Canonical entity" even for raw aliases.
- docs/entity_hierarchy.md: new "Aliases" section covering leaf vs branch
  aliases, add_alias(), the collision ValueError, and alias resolution in
  canonicalize/to_branch/get_depth (omri374).
- docs: replace the stale COLLISION_AMBIGUOUS / COLLISION_TRIVIAL issue
  types with the actual COLLISION_CROSS_BRANCH / COLLISION_SAME_BRANCH
  (Copilot). ADR-003 left as-is; it records a decision at a point in time.
- CHANGELOG: add PER to the breaking-change list.

728 passed, 2 skipped; ruff clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ceeaa56-7275-46b5-9f57-12e9bb3c20e3

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ceeaa56-7275-46b5-9f57-12e9bb3c20e3
"PATIENT" was an alias of the PATIENT_ID leaf, i.e. the same leaf as
MEDICALRECORD. In the i2b2/n2c2 2014 de-identification schema, PATIENT and
DOCTOR are both subtypes of the NAME category and MEDICALRECORD is the ID
subtype, so the PATIENT tag marks a person's name ("Yosef Villegas"), not an
identifier.

The hierarchy already treated the neighbouring labels correctly - DOCTOR,
DOCTOR_NAME, PATIENT_NAME and HCW all resolve to NAME - which made bare
PATIENT the odd one out:

    PATIENT       -> PATIENT_ID  (branch PHI)      <- wrong
    PATIENT_NAME  -> NAME        (branch PERSON)
    DOCTOR        -> NAME        (branch PERSON)

Because the two disagree at branch level as well as leaf level, a model that
correctly labels a patient name as a NAME was scored wrong at every level, and
a model that reproduced the mislabel was scored right. On a 20-record n2c2
sample this affected 32 of 328 gold spans (~10%).

Moves "PATIENT" to FULL_NAME alongside DOCTOR and PATIENT_NAME. The
record-number aliases (MEDICALRECORD, MEDICAL_RECORD) stay on PATIENT_ID.

Noticed while writing the test, not fixed here: MEDICAL_RECORD_NUMBER is listed
as an alias of both PATIENT_ID and MRN, and MRN currently wins. The new test
asserts that label at branch level only, so it does not silently encode which
leaf shadows the other.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ceeaa56-7275-46b5-9f57-12e9bb3c20e3
WikiData's API now rejects unauthenticated requests with a 403 and a
message asking clients to set a User-Agent per its robot policy. This
made HospitalProvider silently fall back to its default hospital list
whenever load_wiki_hospitals() ran.

Also corrects the log message on the fallback path: it previously
claimed to be "returning an empty list" when it actually returns
self.default_list.

Fixes #173
Removed Python version 3.10 from CI workflow matrix.
Resolve conflicts with the 0.3.2 release and the hierarchy projection work:

- CHANGELOG: 0.3.2 shipped after this branch was written, so the two-sided
  counting entries move from the 0.3.2 section to Unreleased > Behavior
  Changes, next to the deepest-annotated-ancestor projection entry.
- span_evaluator.calculate_score_on_df: keep the allow_generic_entities
  parameter docstring from main inside this branch's typed docstring.
- test_low_iou_descendants_use_the_projected_type: two NAME spans that
  jointly fail to cover a PERSON annotation now count one false positive
  per span (num_predicted 2, FP 2), as documented for two-sided counting,
  while still being attributed to the projected PERSON label.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SMHDXc7d43rZLiRrunemYY
@omri374 omri374 changed the title Two-sided span counting: precision per prediction span, recall per annotation (rebased on main) Merge main into #185 and resolve conflicts Sep 10, 2026
@omri374
omri374 changed the base branch from main to fix/span-evaluator-two-sided-counting September 10, 2026 06:38
@omri374
omri374 merged commit fed4469 into fix/span-evaluator-two-sided-counting Sep 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants