Detection Approach Trade-offs

Choosing how to find personally identifiable information in legal documents is an engineering decision with compliance consequences: deterministic regex is exact on structured identifiers but blind to context, statistical named-entity recognition reads context but drifts, and neither alone clears a defensible recall bar. This guide, part of the PII Detection & Automated Redaction Patterns field guide, shows how to weigh, combine, and layer them.

Detection approach comparison Deterministic regex and statistical NER each detect different entity classes with different precision and latency profiles, then feed a hybrid resolver that merges their spans and de-duplicates overlaps. Deterministic regex SSN / EIN / case no. high precision, sub-ms context-blind Statistical NER names / orgs / roles contextual, ms/token probabilistic, drifts Hybrid resolver merge spans, de-duplicate overlaps
Regex and NER cover complementary entity classes; a resolver reconciles them.

Approach comparison matrix Permalink to this section

No single detector satisfies both the precision needed to avoid over-redacting privileged text and the recall needed to avoid leaking identifiers. The matrix below summarizes where each approach earns its place, and why most production pipelines layer them rather than pick one.

Approach Precision / recall profile Latency Maintenance Best-fit entity types
Deterministic regex High precision, low-to-medium recall; brittle to formatting variance Sub-millisecond per pattern Pattern edits, versioned dictionaries SSN, EIN, case numbers, account IDs, Bates numbers
Statistical NER Medium precision, higher recall on free text; probabilistic, drifts over time Milliseconds per token (CPU) Retraining, calibration, drift monitoring Person names, organizations, locations, privileged relationships
Hybrid layering Best combined recall; precision managed by conflict resolution Sum of both, gated by routing Both, plus resolver rules Full document coverage across structured and free text
Build vs buy Buy accelerates coverage; build maximizes control and auditability Framework overhead vs bespoke Vendor upgrades vs in-house Depends on custom legal entities and air-gap needs

Deterministic patterns should own anything with a fixed grammar, as covered in Regex Rule Optimization for Legal Entities. Free-text entities that depend on surrounding language belong to spaCy NER for PII Detection. The build-vs-buy axis is a distinct decision explored in the companion page on Microsoft Presidio vs Custom spaCy Pipelines.

Verified hybrid resolver Permalink to this section

The value of layering is realized only if overlapping spans are reconciled deterministically. The resolver below merges regex anchors and NER spans, then de-duplicates overlaps with a stable priority order so identical inputs always yield identical redaction sets — a hard requirement for chain-of-custody reproducibility.

from dataclasses import dataclass
from typing import List, Literal

@dataclass(frozen=True)
class Span:
    start: int          # inclusive character offset
    end: int            # exclusive character offset (half-open interval)
    label: str          # e.g. "US_SSN", "PERSON"
    source: Literal["regex", "ner"]
    score: float        # 1.0 for deterministic regex anchors

def _overlaps(a: Span, b: Span) -> bool:
    # Half-open intervals share a character iff they cross.
    return a.start < b.end and b.start < a.end

def resolve(spans: List[Span]) -> List[Span]:
    # Deterministic ordering: earliest start, then widest span,
    # then regex before NER (structured anchors win ties), then score.
    priority = {"regex": 0, "ner": 1}
    ordered = sorted(
        spans,
        key=lambda s: (s.start, -(s.end - s.start), priority[s.source], -s.score),
    )
    kept: List[Span] = []
    for span in ordered:
        # Drop any candidate overlapping an already-accepted span.
        if any(_overlaps(span, k) for k in kept):
            continue
        kept.append(span)
    return kept

if __name__ == "__main__":
    # A regex SSN anchor and an NER span that partially overlap it.
    candidates = [
        Span(10, 21, "US_SSN", "regex", 1.0),
        Span(14, 30, "PERSON", "ner", 0.88),   # overlaps the SSN, dropped
        Span(48, 61, "PERSON", "ner", 0.91),   # disjoint, kept
    ]
    for s in resolve(candidates):
        print(f"{s.label:8} [{s.start}:{s.end}] via {s.source}")
    # US_SSN   [10:21] via regex
    # PERSON   [48:61] via ner

Compliance alignment Permalink to this section

A redaction methodology is only defensible if it is documented and reproducible. Under FRCP Rule 34, producing parties must handle electronically stored information in a form that can be scrutinized; a hybrid detector whose span-resolution logic is version-controlled and deterministic lets you demonstrate exactly why each character was masked. The NIST Privacy Framework category PR.DS (Data Security) expects controls that protect data at rest and in transit throughout processing — recording which detector fired, its score, and the resolved outcome for every span satisfies that evidentiary expectation without ever persisting raw identifiers.

Performance and scale Permalink to this section

The two approaches sit on opposite ends of the cost curve. A compiled regex evaluates a candidate in the sub-millisecond range, so a bank of a few dozen patterns adds negligible latency to ingestion. Statistical NER costs roughly milliseconds per token on CPU; a 3,000-token filing can take 200–400 ms on en_core_web_sm and several times that on a transformer pipeline. The practical pattern is to run regex first as a cheap, high-precision anchor pass, then reserve NER for the residual free text, keeping combined throughput near 6,000–9,000 pages/hour on 4 vCPUs while confidence routing, described in Confidence Threshold Configuration, gates the borderline cases.

Edge cases & failure handling Permalink to this section

  • Overlapping spans double-redact. When regex and NER both fire on adjacent-but-crossing offsets, naive concatenation masks the same text twice and corrupts downstream offset math. Remediation: run every candidate through the deterministic resolver above so a stable priority order picks one span per region.
  • NER drift. Model accuracy degrades as document corpora evolve (new firm styles, redacted training leakage), silently lowering recall. Remediation: track precision/recall against a quarterly gold set and alert when either crosses a floor, then retrain or recalibrate.
  • Regex catastrophic backtracking. A poorly bounded pattern with nested quantifiers can pin a CPU for seconds on an adversarial input, stalling the queue. Remediation: prefer possessive/atomic constructs and anchored character classes, cap input length per match, and unit-test patterns against long non-matching strings.
  • Silent under-redaction. Both engines missing a rare identifier produces a false negative with no error. Remediation: enforce a combined recall floor and route low-coverage documents to human review rather than auto-committing.

Continue with the companion pages on Regex vs NLP for Legal PII Detection and Microsoft Presidio vs Custom spaCy Pipelines, then tune the individual engines through Regex Rule Optimization for Legal Entities, spaCy NER for PII Detection, and Confidence Threshold Configuration. Part of the PII Detection & Automated Redaction Patterns field guide.