High-performance Mondrian K-Anonymity implementation.
Project description
UMBRA-K
Umbra-K is a high-performance, strictly typed Python implementation of the Mondrian Multidimensional K-Anonymity algorithm. It is designed to transform sensitive datasets into privacy-preserving formats suitable for safe distribution and machine learning, ensuring mathematical guarantees against re-identification attacks.
1. Theoretical Foundation: The Privacy Problem
1.1 The Anonymization Myth
Removing "Direct Identifiers" (e.g., Names, SSNs) is insufficient for privacy. Research by Latanya Sweeney (2002) demonstrated that 87% of the U.S. population could be uniquely identified using only three attributes: {Zip Code, Gender, Date of Birth}.
These attributes are Quasi-Identifiers (QIs). While not unique individually, their combination creates a unique "fingerprint" that can be linked to external datasets (e.g., Voter Rolls) to re-identify individuals.
1.2 The Solution: $k$-Anonymity
A release of data satisfies $k$-anonymity if every individual in the dataset cannot be distinguished from at least $k-1$ other individuals with respect to the quasi-identifiers.
Formal Definition: Let $T$ be a table with quasi-identifiers $Q_{ID}$. $T$ satisfies $k$-anonymity if for every sequence of values $q \in T[Q_{ID}]$, the number of occurrences of $q$ in $T$ is at least $k$.
$$ \forall q \in \pi_{Q_{ID}}(T), \quad count(q) \ge k $$
Where:
- $T$: The dataset.
- $Q_{ID}$: The set of quasi-identifier columns (e.g., Age, Zip).
- $k$: The privacy parameter (e.g., $k=5$ means groups of 5 indistinguishable records).
2. Algorithmic Core: The Mondrian Protocol
Umbra-K implements the Top-Down Greedy Mondrian Algorithm (LeFevre et al., 2006). Unlike optimal $k$-anonymity (which is NP-Hard), Mondrian approximates the optimal solution using a recursive multidimensional partitioning strategy, achieving a time complexity of $O(N \log N)$.
2.1 The Logic
The algorithm treats the dataset as a multi-dimensional spatial domain.
- Initialization: The entire dataset is considered a single partition.
- Constraint Check: If a partition contains fewer than $2k$ records, it cannot be split further without violating the privacy constraint. It is finalized as an Equivalence Class.
- Dimensionality Reduction: The algorithm selects the dimension (column) with the widest Normalized Span. $$ Span(D) = \frac{max(D) - min(D)}{global_max(D) - global_min(D)} $$
- Partitioning: The data is split into two sub-partitions along the median of the chosen dimension.
- $P_{left} = {x \in P \mid x.dim \le median}$
- $P_{right} = {x \in P \mid x.dim > median}$
- Recursion: The process repeats recursively for $P_{left}$ and $P_{right}$ until no valid splits remain.
2.2 Visual Abstraction
The algorithm builds a KD-Tree structure. Below is a 2D representation of partitioning a dataset by Age and Salary with $k=2$.
Salary
^
|
100k| +-------+-------+
| | EQ1 | EQ2 |
80k|-------+-------+-------+ <-- Split 2 (Salary=80k)
| | | |
| EQ3 | EQ4 | EQ5 |
40k| | | |
+-------+-------+-------+--> Age
^ ^
Split 1 Split 3
(Age=30) (Age=55)
Each rectangular region (EQ) contains $\ge k$ records. The exact coordinates are suppressed and replaced by the region's boundaries.
3. Installation
pip install umbra-k
Requirements:
- Python $\ge$ 3.9
- Pandas $\ge$ 1.0
- Numpy
- Scikit-learn (for API compatibility)
4. Usage Protocol
Umbra-K follows the standard Scikit-Learn Transformer API, allowing seamless integration into data preprocessing pipelines.
4.1 Basic Implementation
import pandas as pd
from umbra_k import MondrianAnonymizer
# 1. Ingest Data
data = pd.DataFrame({
'age': [23, 23, 24, 25, 50, 51, 52, 54],
'zip': [10000, 10000, 10000, 10000, 20000, 20000, 20000, 20000],
'salary': [50000, 55000, 52000, 51000, 90000, 92000, 91000, 95000]
})
# 2. Initialize Anonymizer
# We target 'age' and 'zip' as Quasi-Identifiers. 'salary' is sensitive and left untouched.
# We set k=2, ensuring groups of at least 2.
anonymizer = MondrianAnonymizer(k=2, quasi_id=['age', 'zip'])
# 3. Execute Transformation
protected_df = anonymizer.fit_transform(data)
print(protected_df)
Output:
age zip salary
0 23-23 10000 50000
1 23-23 10000 55000
2 24-25 10000 52000
3 24-25 10000 51000
4 50-51 20000 90000
5 50-51 20000 92000
6 52-54 20000 91000
7 52-54 20000 95000
4.2 Advanced: Scikit-Learn Pipeline Integration
Umbra-K can be inserted directly into ML pipelines to anonymize training data on the fly.
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from umbra_k import MondrianAnonymizer
pipeline = Pipeline([
# Step 1: Anonymize sensitive features
('privacy_layer', MondrianAnonymizer(k=5, quasi_id=['age', 'zip_code'])),
# Step 2: Encode the now-categorical ranges (e.g. "20-30") into numbers
# Note: Requires a custom encoder or OneHotEncoder handling strings
# Step 3: Classifier
('classifier', RandomForestClassifier())
])
5. API Reference
MondrianAnonymizer
| Parameter | Type | Default | Description |
|---|---|---|---|
k |
int |
2 |
The privacy parameter. Minimum number of records per group. |
quasi_id |
List[str] |
None |
List of column names to be treated as quasi-identifiers. |
Methods
fit(X, y=None): Validates parameters. Returnsself.transform(X): Performs the recursive partitioning and returns the anonymized DataFrame.fit_transform(X, y=None): Convenience method to fit and transform in one step.
Metrics
calculate_avg_equivalence_class_size(df, k, quasi_id): Calculates the $C_{avg}$ information loss metric.
6. System Limitations
While Mondrian is efficient, users should be aware of theoretical limitations:
- Homogeneity Attack: If all records in an equivalence class have the same sensitive attribute value (e.g., all people in age group "20-25" have "Cancer"), $k$-anonymity does not prevent attribute inference.
- High Dimensionality: As the number of Quasi-Identifiers increases, the data becomes sparse ("Curse of Dimensionality"), forcing the algorithm to create very large ranges to satisfy $k$, which drastically reduces data utility.
7. Development & Testing
The repository includes a hyper-rigorous test suite using Hypothesis for property-based fuzzing.
# Run the rigorous test suite
pytest tests/test_rigorous_properties.py
# Run standard unit tests
pytest tests/test_anonymizer.py
8. License
Released under the Vecture-1.0 mandate (MIT License).
See LICENSE for details.
The eye remains open. Vecture sees the path.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file umbra_k-0.1.0.tar.gz.
File metadata
- Download URL: umbra_k-0.1.0.tar.gz
- Upload date:
- Size: 38.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b586bb59ad403a580eef4563100ad503384f1f4df8ce81eb47ba624f6125c175
|
|
| MD5 |
3d89c19c3c0fb6efec0b9f4a9ec1d155
|
|
| BLAKE2b-256 |
c9358119b2d08cdd17c3dceda9aca8df903421504ffb1e176532af20f4315092
|
Provenance
The following attestation bundles were made for umbra_k-0.1.0.tar.gz:
Publisher:
workflow.yml on VectureLaboratories/umbra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umbra_k-0.1.0.tar.gz -
Subject digest:
b586bb59ad403a580eef4563100ad503384f1f4df8ce81eb47ba624f6125c175 - Sigstore transparency entry: 909395611
- Sigstore integration time:
-
Permalink:
VectureLaboratories/umbra@0caf64e072f625177fcd747646f1b6b4c757c1cd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/VectureLaboratories
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@0caf64e072f625177fcd747646f1b6b4c757c1cd -
Trigger Event:
release
-
Statement type:
File details
Details for the file umbra_k-0.1.0-py3-none-any.whl.
File metadata
- Download URL: umbra_k-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60fb70e1682c6e847be7f4786d9f287a933f09ae0cfec875cef0eea539145a90
|
|
| MD5 |
fd281f51c622cac24315fddfa1f5ba0d
|
|
| BLAKE2b-256 |
5b67a2bb03fa7d2b7970a654f429e131a6c7d5735fa7c2b37f1cf41410d87988
|
Provenance
The following attestation bundles were made for umbra_k-0.1.0-py3-none-any.whl:
Publisher:
workflow.yml on VectureLaboratories/umbra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umbra_k-0.1.0-py3-none-any.whl -
Subject digest:
60fb70e1682c6e847be7f4786d9f287a933f09ae0cfec875cef0eea539145a90 - Sigstore transparency entry: 909395633
- Sigstore integration time:
-
Permalink:
VectureLaboratories/umbra@0caf64e072f625177fcd747646f1b6b4c757c1cd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/VectureLaboratories
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@0caf64e072f625177fcd747646f1b6b4c757c1cd -
Trigger Event:
release
-
Statement type: