domherre
Redacts Swedish personal names, personnummer and e-mail adresses from string columns in a polars DataFrame.
- Personnummer — regex (date-shaped) + Luhn checksum.
- Names —
KB/bert-base-swedish-cased-ner,PRSentities only.
Install
pip install domherre
What you get
import polars as pl
import domherre as kt
df = pl.DataFrame({
"id": [1, 2],
"note": [
"Engelbert Karlsson, personnummer 850101-0006, ringde idag.",
"Ingen känslig info här.",
],
})
kt.redact(df, "note")
# note -> note_redacted
# "[NAMN], personnummer [PERSONNUMMER], ringde idag."
- Redacted columns are renamed with a suffix (default
_redacted) so a redacted column - can never be mistaken for the source. The original column is replaced, not kept.
- Unlisted columns are untouched. Raises if a target name already exists.
Several columns at once:
kt.redact(df, ["note", "kommentar"])
Single string:
kt.redact_text("Anna Andersson, 850101-0006")
A one-time PandasPerformanceWarning fires per process. Silence it with
DOMHERRE_QUIET=1 if you have already made your peace with pandas.
Backend models
Name detection is pluggable. Personnummer detection is always regex + Luhn and is not affected by the backend choice. This project defaults to kb-bert model as it has shown to be the formidable model for swedish name detection.
| Backend | Install | Notes |
|---|---|---|
kb-bert (default) |
included | KB/bert-base-swedish-cased-ner. Swedish-only, trained on formal text. |
gliner |
domherre[gliner] |
Zero-shot, multilingual, CPU-optimised. Change labels to detect anything. |
presidio |
domherre[presidio] |
Full PII framework. Bring your own configured AnalyzerEngine. |
none |
included | Skips name detection. Regex only. |
kt.redact(df, "note", kt.Config(backend="gliner"))
gliner
config = kt.Config(
backend="gliner",
model="urchade/gliner_multi_pii-v1", # default
labels=("person", "full name"),
min_score=0.5,
backend_options={"map_location": "cpu", "quantize": True},
)
Custom backends
Anything with load() and person_spans(texts) -> list[list[tuple[int, int]]] works.
A plain callable works too:
def detector(texts: list[str]) -> list[list[tuple[int, int]]]:
return [[(m.start(), m.end()) for m in pattern.finditer(t)] for t in texts]
kt.redact(df, "note", kt.Config(backend=detector))
Register by name to make it selectable like a builtin:
kt.register_backend("mine", MyBackend)
kt.available_backends() # ['gliner', 'kb-bert', 'mine', 'none', 'presidio']
Backends are cached per (name, config), so a Deployment loads each model once.
Config
config = kt.Config(
name_tag="[NAMN]",
personnummer_tag="[PERSONNUMMER]",
suffix="_redacted",
backend="kb-bert", # or "gliner", "presidio", "none", or your own
model=None, # backend default, or a local path: "/models/ner"
local_only=False, # True = never hit the network, fail loudly if not cached
num_threads=2, # pin to your k8s cpu limit
batch_size=32,
min_score=0.0, # raise to cut false-positive names, at the cost of misses
redact_names=True,
redact_personnummer=True,
validate_personnummer=True, # False = redact on shape alone, catches typo'd numbers
enumerate_names=False, # [NAMN_1], [NAMN_2] instead of flat [NAMN]
extra_patterns={}, # {"[EPOST]": r"\S+@\S+\.\w+"}
)
kt.redact(df, "note", config)
enumerate_names
Distinguishes people within a single string, so downstream analysis can still tell that two mentions refer to the same person:
"[NAMN_1] mailade [NAMN_2] om [NAMN_1]s ärende"
Numbering is per string, not per DataFrame. [NAMN_1] in row 1 and row 2 are not
necessarily the same person — that would need identity resolution, which this does not do.
Service deployment
Domherre is suited as deployemnts that can be run as services, we thus support models pre-baked into images. Load the model at startup so the first request doesn't pay for it:
kt.load(backend="kb-bert", num_threads=2, local_only=True)
Bake the weights into the image and set local_only=True. Otherwise every cold pod
pulls ~440MB from HuggingFace, and a network blip becomes a runtime failure
in the middle of a batch instead of a clear error at boot.
RUN python -c "from transformers import pipeline; \
pipeline('token-classification', model='KB/bert-base-swedish-cased-ner')"
resources:
requests: { cpu: "1", memory: "2Gi" }
limits: { cpu: "2", memory: "3Gi" }
Set num_threads to match the CPU limit. Torch otherwise reads the node's core count,
not the cgroup limit, and thrashes threads on a large node.
Personnummer formats detected
YYMMDD-XXXX, YYMMDD+XXXX, YYMMDDXXXX, YYYYMMDD-XXXX, YYYYMMDDXXXX,
plus samordningsnummer (day + 60). All must pass the Luhn check unless
validate_personnummer=False.
Not detected: reservnummer/interimsnummer (region-specific, no fixed national format),
and personnummer with a typo'd control digit — set validate_personnummer=False
to catch those, at the cost of redacting unrelated 10 and 12-digit numbers.
Metadata
Release files for domherre 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| domherre-0.1.1.tar.gz | 15.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| domherre-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.7 kB
Release files / domherre-0.1.1.tar.gz
| Download URL | domherre-0.1.1.tar.gz |
|---|---|
| Size | 15.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
047b296c91c51b16763ea5d09f8bb526003701d4356c91fc126b2cf08c78f20e
|
|
BLAKE2b-256 checksum How to use checksums |
3a1d6a1eb99941e723413ecef624d30a0554300d5c2d0c8042c6afd06b93421c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / domherre-0.1.1-py3-none-any.whl
| Download URL | domherre-0.1.1-py3-none-any.whl |
|---|---|
| Size | 12.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1b84a5ab5eba83f2d2a20f51de31f90492777171e22b5a1babc594d5b8e06ee8
|
|
BLAKE2b-256 checksum How to use checksums |
a36ea94e5011756d6b27bf3df80e828279a08283dfe628d088b173441b855836
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Security for domherre 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.