yungisimon commited on
Commit
24de66d
verified
1 Parent(s): be03d5b

Upload pii_hybrid_decode.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. pii_hybrid_decode.py +19 -4
pii_hybrid_decode.py CHANGED
@@ -70,6 +70,19 @@ _SNAP = {
70
  }
71
  _AUTH_TYPES = {t for t, _, _ in _AUTH}
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
74
  """model_spans: [{'start','end','type'}...] from the token classifier. Returns the
75
  hybrid-decoded spans (dicts with start/end/type/text)."""
@@ -81,6 +94,7 @@ def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
81
  s, e = mm.start(), mm.end()
82
  if any(claimed[s:e]): continue
83
  if val and not val(mm.group(0)): continue
 
84
  for i in range(s, e): claimed[i] = True
85
  auth.append({"start": s, "end": e, "type": t, "text": mm.group(0)})
86
  # 2. model spans for non-AUTH types; SNAP types expand to overlapping regex match
@@ -97,14 +111,15 @@ def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
97
  if snap:
98
  out.append({"start": snap.start(), "end": snap.end(), "type": t,
99
  "text": text[snap.start():snap.end()]}); continue
100
- out.append({"start": m["start"], "end": m["end"], "type": t,
101
- "text": text[m["start"]:m["end"]]})
102
  out.extend(auth)
103
  seen, uniq = set(), []
104
  for sp in sorted(out, key=lambda s: (s["start"], s["end"])):
105
  k = (sp["start"], sp["end"], sp["type"])
106
- if k not in seen:
107
- seen.add(k); uniq.append(sp)
 
108
  return uniq
109
 
110
  def model_spans(text: str, tok, model):
 
70
  }
71
  _AUTH_TYPES = {t for t, _, _ in _AUTH}
72
 
73
+ # boundary-snap: the token classifier drops leading/trailing sub-tokens of Latin-script
74
+ # words ('Ibuprofen'->'profen'). Extend a span across contiguous Latin word-chars to
75
+ # complete the partial word(s). Latin-only -> CJK/Arabic left untouched (no over-extend).
76
+ _LAT = re.compile(r"[0-9A-Za-z脌-脰脴-枚酶-每]")
77
+ def _snap_word(text, s, e):
78
+ n = len(text)
79
+ while s > 0 and _LAT.match(text[s - 1]) and _LAT.match(text[s]): s -= 1
80
+ while e < n and _LAT.match(text[e]) and _LAT.match(text[e - 1]): e += 1
81
+ return s, e
82
+ _SWIFT_CUE = re.compile(r"(?i)(swift|bic)")
83
+ def _swift_ok(text, s, val): # kill ALL-CAPS-word false BICs (PARTICULARS, CONFIDENTIAL)
84
+ return bool(re.search(r"\d", val)) or bool(_SWIFT_CUE.search(text[max(0, s - 12):s]))
85
+
86
  def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
87
  """model_spans: [{'start','end','type'}...] from the token classifier. Returns the
88
  hybrid-decoded spans (dicts with start/end/type/text)."""
 
94
  s, e = mm.start(), mm.end()
95
  if any(claimed[s:e]): continue
96
  if val and not val(mm.group(0)): continue
97
+ if t == "financial.swift_bic" and not _swift_ok(text, s, mm.group(0)): continue
98
  for i in range(s, e): claimed[i] = True
99
  auth.append({"start": s, "end": e, "type": t, "text": mm.group(0)})
100
  # 2. model spans for non-AUTH types; SNAP types expand to overlapping regex match
 
111
  if snap:
112
  out.append({"start": snap.start(), "end": snap.end(), "type": t,
113
  "text": text[snap.start():snap.end()]}); continue
114
+ ss, ee = _snap_word(text, m["start"], m["end"]) # complete partial Latin words
115
+ out.append({"start": ss, "end": ee, "type": t, "text": text[ss:ee]})
116
  out.extend(auth)
117
  seen, uniq = set(), []
118
  for sp in sorted(out, key=lambda s: (s["start"], s["end"])):
119
  k = (sp["start"], sp["end"], sp["type"])
120
+ if k in seen: continue
121
+ if len(text[sp["start"]:sp["end"]].strip()) < 3: continue # drop fragments
122
+ seen.add(k); uniq.append(sp)
123
  return uniq
124
 
125
  def model_spans(text: str, tok, model):