PIICleaner
A fast, Rust-powered Python library for detecting and cleaning Personal Identifiable Information (PII) from text data, with seamless Polars and Pandas integration.
Features
- Fast PII Detection: Rust-based regex engine for high-performance text processing
- Case-Insensitive Matching: Detects PII regardless of case by default, with optional case-sensitive mode
- Multiple PII Types: Detects emails, phone numbers, postcodes, National Insurance numbers, addresses, and more
- Flexible Cleaning: Replace or redact detected PII with customisable strategies
- Semantic Redaction: Replace PII with meaningful labels like
[email-redacted]instead of generic dashes - Custom Replacement Strings: Define your own replacement text for the "replace" cleaning method
- PII Type Detection: Get detailed information about what type of PII was detected
- Polars Integration: A native Polars plugin cleans whole columns in Rust, in parallel, in eager and lazy queries
- Pandas Integration: Native support for cleaning DataFrames and Series
- Easy to Use: Simple API for both single strings and batch processing
Installation
# Using uv
uv add piicleaner
# With Polars support
uv add 'piicleaner[polars]'
# With Pandas support
uv add 'piicleaner[pandas]'
# Using pip
pip install piicleaner
# With Polars support
pip install 'piicleaner[polars]'
# With Pandas support
pip install 'piicleaner[pandas]'
Platform Support
PIICleaner provides pre-built wheels for the platforms below. Each wheel works on every Python version from 3.11 onwards, including new releases.
- Windows: x86_64 (Intel/AMD 64-bit)
- macOS: x86_64 (Intel) and arm64 (Apple Silicon)
- Linux: x86_64 (Intel/AMD 64-bit) and aarch64 (ARM64, e.g. AWS Graviton, Raspberry Pi), for both glibc (manylinux) and musl (musllinux, e.g. Alpine) distributions
Building from Source
On other platforms pip builds piicleaner from source, which needs:
- Rust toolchain (1.95 or newer), install from rustup.rs
- Python development headers
# Using uv
uv add piicleaner --no-binary piicleaner
# Using pip
pip install piicleaner --no-binary piicleaner
Quick Start
Basic Usage
from piicleaner import Cleaner
# Instantiate a cleaner
cleaner = Cleaner()
# Clean a single string (case-insensitive by default)
text = "Contact John at JOHN@EXAMPLE.COM or call +44 20 7946 0958"
cleaned = cleaner.clean_pii(text, "redact")
print(cleaned) # "Contact John at [email-redacted] or call [telephone-redacted]"
# Detect PII locations with type information
matches = cleaner.detect_pii(text)
print(matches)
# [{'start': 16, 'end': 32, 'text': 'JOHN@EXAMPLE.COM', 'type': 'email'},
# {'start': 41, 'end': 57, 'text': '+44 20 7946 0958', 'type': 'telephone'},
# {'start': 41, 'end': 57, 'text': '+44 20 7946 0958', 'type': 'international-telephone'}]
# Every cleaner's match is listed, so one piece of text can appear more than once
# 'start' and 'end' are character offsets, so they slice the original string:
# text[16:32] == 'JOHN@EXAMPLE.COM', even when the text contains non-ASCII
# characters such as '£' or 'é'
# Case-sensitive detection
nino_cleaner = Cleaner(cleaners=["nino"])
matches_sensitive = nino_cleaner.detect_pii("nino: ab123456c", ignore_case=False)
print(matches_sensitive) # [] - no match because NINO pattern expects uppercase
matches_insensitive = nino_cleaner.detect_pii("nino: ab123456c", ignore_case=True)
print(matches_insensitive) # [{'start': 6, 'end': 15, 'text': 'ab123456c', 'type': 'nino'}]
Polars Integration
import polars as pl
from piicleaner import Cleaner
# Create DataFrame with PII
df = pl.DataFrame({
"text": [
"Email: alice@company.com",
"NINO: AB123456C",
"Phone: +44 20 7946 0958"
],
"id": [1, 2, 3]
})
cleaner = Cleaner()
# Clean PII in DataFrame
cleaned_df = cleaner.clean_dataframe(df, "text", "redact", new_column_name="cleaned_text")
print(cleaned_df)
# Detect PII in DataFrame
pii_df = cleaner.detect_dataframe(df, "text")
print(pii_df)
# Using namespace API
result = df.with_columns(
pl.col("text").pii.clean_pii("redact").alias("cleaned")
)
Pandas Integration
import pandas as pd
from piicleaner import Cleaner
# Create DataFrame with PII
df = pd.DataFrame({
"text": [
"Email: alice@company.com",
"NINO: AB123456C",
"Phone: +44 20 7946 0958"
],
"id": [1, 2, 3]
})
cleaner = Cleaner()
# The same DataFrame methods as for Polars
cleaned_df = cleaner.clean_dataframe(df, "text", "redact", new_column_name="cleaned_text")
print(cleaned_df)
# Detect PII in DataFrame
pii_df = cleaner.detect_dataframe(df, "text")
print(pii_df)
# Using Series accessor API
df["cleaned"] = df["text"].pii.clean_pii("redact")
df["pii_detected"] = df["text"].pii.detect_pii()
Specific PII Types and Custom Replacement
# Use specific cleaners
email_cleaner = Cleaner(cleaners=["email"])
phone_cleaner = Cleaner(cleaners=["telephone", "postcode"])
# Case-insensitive cleaning with specific cleaners
text = "EMAIL: JOHN@EXAMPLE.COM"
cleaned = email_cleaner.clean_pii(text, "redact", ignore_case=True)
print(cleaned) # "EMAIL: [email-redacted]"
# Custom replacement string
custom_cleaner = Cleaner(replace_string="[CONFIDENTIAL]")
text = "Contact john@example.com"
replaced = custom_cleaner.clean_pii(text, "replace")
print(replaced) # "[CONFIDENTIAL]"
# See available cleaners
print(Cleaner.get_available_cleaners())
# ['address', 'case-id', 'cash-amount', 'email', 'international-telephone', 'ip_address', 'nino', 'postcode', 'tag', 'telephone']
Supported PII Types
| Type | Description | Example |
|---|---|---|
email |
Email addresses | john@example.com |
telephone |
UK phone numbers | +44 20 7946 0958 |
international-telephone |
Phone numbers with a + or 00 international prefix |
+1 415 555 0132, +33 1 23 45 67 89 |
postcode |
UK postcodes, with any whitespace (or none) between the two halves | SW1A 1AA, SW1A1AA |
nino |
National Insurance numbers, with spaces, dashes or dots between groups | AB123456C, AB-12-34-56-C, AB 123 456 C |
address |
Street addresses | 123 High Street |
cash-amount |
Currency amounts | £1,500, $2000 |
case-id |
Case/reference IDs | UUIDs, reference numbers |
tag |
HTML/XML tags | <script>, <div> |
ip_address |
IPv4 and IPv6 addresses | 192.168.1.1, 2001:db8::1, fe80::1%eth0 |
Cleaning Methods
"redact": Redact the PII, replacing it with semantic labels like[email-redacted],[telephone-redacted]. Where matches from different cleaners overlap, the whole overlapping region is redacted and labelled with the highest-priority cleaner, in the order returned bypiicleaner.get_available_cleaners(). The same input always produces the same output."replace": Replace the entire string if any PII is detected (uses custom replacement string if provided)
Over-Matching Is Expected
PIICleaner errs towards redaction. Missing PII costs far more than redacting text that isn't PII, so the patterns are deliberately broad and some false positives are expected behaviour, not bugs. For example, with the default case-insensitive matching, item a1 1aa is redacted as a postcode.
If you need fewer false positives, choose only the cleaners you need, or use ignore_case=False. Both make it more likely that some PII is missed.
Case Sensitivity
By default, PIICleaner performs case-insensitive matching to catch PII regardless of how it's formatted:
ignore_case=True(default): Detectsab123456c,AB123456C, andAb123456Cas valid NINOsignore_case=False: Only detects patterns matching the exact case defined in regex patterns
Case-insensitive matching finds more PII, and also more false positives, notably for nino, postcode and case-id. Set it once for a Cleaner with Cleaner(ignore_case=False), or pass ignore_case to a single call.
API Reference
Cleaner Class
Cleaner(cleaners="all", replace_string=None, ignore_case=True)
Parameters:
cleaners(str | list[str]): PII types to detect. Use"all"for all types or specify a list like["email", "telephone"]replace_string(str | None): Custom replacement string for "replace" cleaning methodignore_case(bool): Default for every method'signore_case
Methods (cleaning is "redact" or "replace"; ignore_case=None means the Cleaner's setting):
detect_pii(text, ignore_case=None): Detect PII and return match locations with type informationdetect_pii_list(texts, ignore_case=None): Detect PII in list of stringsclean_pii(text, cleaning, ignore_case=None): Clean PII from textclean_pii_list(texts, cleaning, ignore_case=None): Clean list of stringsclean_dataframe(df, column, cleaning, *, ignore_case=None, new_column_name=None): Clean a column of a Polars or pandas DataFramedetect_dataframe(df, column, *, ignore_case=None, new_column_name=None): Detect PII in a column of a Polars or pandas DataFrameget_available_cleaners(): Get list of available PII types
In the DataFrame methods, ignore_case and new_column_name are keyword-only, e.g. clean_dataframe(df, "text", "redact", new_column_name="cleaned_text").
clean_pandas_dataframe() and detect_pandas_dataframe() are deprecated and will be removed in 0.8.0. Use clean_dataframe() and detect_dataframe(), which take pandas DataFrames too.
DataFrame Integration Features:
- Polars:
.pii.clean_pii()and.pii.detect_pii()expressions, which run as a native Polars plugin (no per-row Python) and work in lazy queries.clean_dataframe()/detect_dataframe()use the same plugin - Pandas: Series accessor
.pii.clean_pii()and.pii.detect_pii()methods - Nulls: null values stay null. Cleaning returns them unchanged, and detection gives null rather than an empty list
- Strings only: a column of non-string values raises an error instead of being converted.
clean_dataframe()/detect_dataframe()and the pandas.piiaccessor raiseTypeError; the Polars.piiexpressions raise a Polars error when the query runs. If the column holds text, cast it to strings first - Vectorized Processing: Efficient batch processing for large datasets
Performance
PIICleaner is built with Rust for maximum performance:
- Compiled regex patterns for fast matching
- Efficient string processing
- Minimal Python overhead
- Scales well with large datasets
Requirements
- Python ≥ 3.11
- Polars ≥ 1.0.0 (optional, for Polars DataFrame support)
- Pandas ≥ 2.3.0 (optional, for Pandas DataFrame support)
License
MIT License - see LICENSE file for details.
Contributing
Contributions welcome! Please see the GitHub repository for development setup and guidelines.
Metadata
Release files for piicleaner 0.7.0
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 | Actions |
|---|---|---|---|---|
| piicleaner-0.7.0.tar.gz | 170.3 kB | With provenance | Details for piicleaner-0.7.0.tar.gz |
Built distributions (wheels)
Total release size: 52.2 MB
Release files / piicleaner-0.7.0.tar.gz
| Download URL | piicleaner-0.7.0.tar.gz |
|---|---|
| Size | 170.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9638a2eb1b40afe3b6b1fd1182057eabbdeef3017d62929cd69c62e1efd4a070
|
|
BLAKE2b-256 checksum How to use checksums |
2f88768ad50d9dbfc65b46a18680d0cd9f3aa79930ca318d522a09930699bc17
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-win_amd64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-win_amd64.whl |
|---|---|
| Size | 7.0 MB |
| Tags | CPython 3.11 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
5324ee35b19c15fa81b57aa668c0a7b48ab7612005dea214ae0b2c1f49296c03
|
|
BLAKE2b-256 checksum How to use checksums |
10970aeb26bc9b990c2140c7835cee054d67b2952f1d597ad88bb2fa8493fd9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-musllinux_1_2_x86_64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 8.5 MB |
| Tags | CPython 3.11 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0d67807753d6d79515fd0b9b26f5e7339536170b9f4d9f095b7656e4c61855cc
|
|
BLAKE2b-256 checksum How to use checksums |
fddbdd92179a8c3b0553d84ca396963ab4ab5e6f2084af46087d9e9be825130f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-musllinux_1_2_aarch64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 8.0 MB |
| Tags | CPython 3.11 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
3acc31972dbd5a2cca320787a3855c4af93110f1c35d63b8b926def8521866dd
|
|
BLAKE2b-256 checksum How to use checksums |
f375b57983d8904fb54f2e106b3d3a3dc110292acdd090b52d656adfc3c01cf3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 7.5 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
a7b046d738e24f6730fbb38d970903e8ec0b279343eccbaf93e17d28326d4f01
|
|
BLAKE2b-256 checksum How to use checksums |
50f11ee4b92557cd05703197c33f70274d7c9ea747809282018b0d14dccad57a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 7.8 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
915630b6de415ed2a6dfe13d52f91e60b2010e95b1a92f8c9e64a198f66bd6e5
|
|
BLAKE2b-256 checksum How to use checksums |
173deac6f23139ff7a14dfbd1f409f7f076d8ac68969e155376b12b03a681371
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 6.4 MB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
4d06ad319e35270e0f8ff8b54280d2fdf515f9468fd86acd26cd8d29cceccb92
|
|
BLAKE2b-256 checksum How to use checksums |
e9ad906ee39f0cc9f209db92e4af14207f0505ffcb84c841de84e668ab7d5923
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / piicleaner-0.7.0-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | piicleaner-0.7.0-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 6.9 MB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
fb2cb8e3b8e172550286067c65c09992915cd2cee28a0fcbdeb3d12291aa126c
|
|
BLAKE2b-256 checksum How to use checksums |
2332ac0eee0a9ae6af7460f42d925787bbcc484991ccf1604d8bbe5759061660
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for piicleaner 0.7.0
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.
Full provenance
All 8 files in this release have been uploaded with provenance metadata.
Attestation for piicleaner-0.7.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA 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.