Skip to content

Guide


Attribute Classification


When using the RECITALS Anonymization Manager, you must classify every column in your dataset into one of four categories. This classification dictates how the privacy models (k,l,t) treat the data.

Attribute Types

Attribute Type Description Action
Identifying Directly identifies an individual (e.g. Name, SSN, Phone Number) Removed
Quasi-Identifying Indirectly identifies via combination (e.g. Age, ZIP Code) Altered
Sensitive Harmful information (e.g. Diagnosis, Salary) Retained through (l,t) models.
Insensitive General info with no privacy risk Ignored

Example

This example shows how you can define the attribute types for each column of the dataset.

config = AnonymizationConfig(
    data="dummy_set.csv",

    # Defines Identifiers
    identifiers=["id1", "id2",...,"idn"],

    # Defines Quasi-Identifiers
    quasi_identifiers=["qid1", "qid2",...,"qidn"],

    # Defines Sensitive Attributes
    sensitive_attributes=["sa1", "sa2",...,"san"],

    # Defines Insensitive Attributes
    insensitive_attributes=["ia1", "ia2",...,"ian"],

    ...

)

Hierarchies


In the RECITALS Anonymization Manager, hierarchies are the most important part of the generalization process. They dictate how to transform specific data into broader categories (e.g. turning "25" into "20-30" or "Greece" into "Europe").

These are provided as .csv files where each column represents a level of abstraction.

Structure

A hierarchy file must be a .csv where:

  • Column 0 (Level 0): The original, raw data values.
  • Column 1 (Level 1): The first level of generalization.
  • Column 2 (Level 2): The second level of generalization.
  • ...
  • Column k (Level k): Usually contains a * to represent total suppression.

Example 1. Numerical Hierarchy (Age)

Level (0) Level 1 Level 2 Level 3
23 20-25 20-30 *
21 20-25 20-30 *
28 25-30 20-30 *
31 30-35 30-40 *

Example 2. Categorical Hierarchy (Location)

Level (0) Level 1 Level 2 Level 3
Athens Greece Europe *
Thesaloniki Greece Europe *
Rome Italy Europe *
Delhi India Asia *

Hierarchy Usage

To use hierarchies, simply map each column name to the appropriate hierarchy file path within the AnonymizationConfig.

    config = AnonymizationConfig(
        data="dataset.csv",
        quasi_identifiers=["age", "location"]

        # Defines the hierarchies.
        hierarchies={
            "age":"hierarchies/age.csv",
            "location":"hierarchies/location.csv"
        }
    )

Privacy Models


Using k-anonymity

To use k-anonymity, specify the k threshold and provide hierarchies for all quasi-identifiers. This ensures that each record is indistinguishable from at least k-1 records.

Hierarchies

You must provide hierarchies for every quasi-identifier!

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        k=3,
    )

    result = AnonymizationManager.anonymize(config)
    print(result.get_anonymized_data_as_dataframe())

Using l-diversity

l-diversity extends k-anonymity by guaranteeing that each equivalence class has at least l well-preserved values for the sensitive attributes. To use l-diversity, simply pass the l parameter to the AnonymizationConfig.

Sensitive Attributes

In constrast to k-anonymity, l-diversity requires that the user has defined the sensitive attributes!

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        sensitive_attributes=["salary-class"],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        l=2,
    )

    result = AnonymizationManager.anonymize(config)
    print(result.get_anonymized_data_as_dataframe())

Using t-closeness

t-closeness requires that the distribution of a sensitive attribute in any equivalence class is close to the distribution of the attribute in the entire table (within a threshold parameter t). To use t-closeness, simply pass the parameter t to the AnonymizationConfig.

Sensitive Attributes

In constrast to k-anonymity, t-closeness requires that the user has defined the sensitive attributes!

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        sensitive_attributes=["salary-class"],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        t=0.5,
    )

    result = AnonymizationManager.anonymize(config)
    print(result.get_anonymized_data_as_dataframe())

Using All Models

It is highly recommended to use a combination of privacy models. While k-anonymity protects against identity disclosure (i.e. linking a person to a table entry), l-diversity and t-closeness protect against attribute disclosure (i.e. learning sensitive information about a person).

Best Practice

Using all three models (k,l,t) provides a more resilient approach, satisfying stricter privacy guarantees!

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        sensitive_attributes=["salary-class"],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        k=3,
        l=2,
        t=0.5,
    )

    result = AnonymizationManager.anonymize(config)
    print(result.get_anonymized_data_as_dataframe())

Suppression


Sometimes, a dataset might contain outliers that make anonymization difficult without aggressively generalizing attributes. By using suppression, you effectively allow the exclusion of a small percentage of records to maintain data usability.

Suppression Limit

A suppression_limit=0.05 means that the algorithm is allowed to delete up to 5% of the records if it helps to satisfy the k,l,t requirements.

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        sensitive_attributes=["salary-class", "capital-gain", "capital-loss"],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        k=4,
        l=2,
        suppression_limit=0.05
    )

    result = AnonymizationManager.anonymize(config)
    dataframe = print(result.get_anonymized_data_as_dataframe())

ARX vs Anjana


The RECITALS Anonymization Manager is designed to be backend agnostic, supporting two primary engines ARX and Anjana. While both serve as the "backbone" for the anonymization process, they offer different advantages depending on your project's needs.

Comparison

Feature ARX Anjana
Implementation Java Python
Best For Production & Research Prototyping & Simple Tasks
Performance High (Super Optimized) Moderate (Python Native)
Environment Requires JPype No Other Dependencies

Use ARX when:

  • You require access to advanced functionalities, e.g. fine-grained quality metrics mentioned later in this guide.
  • You are dealing with large datasets.
  • You need to satisfy complex privacy requirements.

Use Anjana when:

  • You prefer a native python approach.
  • Your task is relatively simple.

Advanced Optimization


Weighted Attributes

When using the ARX backend, you can influence how the anonymization algorithm chooses which attributes to generalize. By assigning weights, you tell the engine which columns are more important to keep in their original form.

Why use Weights?

  • Low Weight (e.g. 0.1) tells the engine it is "cheap" to generalize this attribute. It will be transformed first to satisfy privacy requirements.
  • High Weight (e.g., 2.0) tells the engine this attribute is valuable. The algorithm will try harder to keep this data specific and only generalize it as a last resort.
    from anonymization_manager import *
    
    if __name__ == "__main__":
        config = AnonymizationConfig(
            data="examples/arx_example/data/adult.csv",
            identifiers=["education-num"],
            quasi_identifiers=[
                "age",
                "native-country",
                "race",
                "sex",
                "marital-status",
                "occupation",
                "workclass",
                "education",
            ],
            attribute_weights = {
                "age": 0.1,
                "race":2
            },
            sensitive_attributes=["salary-class", "capital-gain", "capital-loss"],
            insensitive_attributes=["hours-per-week"],
            hierarchies={
                "age": "examples/arx_example/hierarchies/age.csv",
                "native-country": "examples/arx_example/hierarchies/country.csv",
                "race": "examples/arx_example/hierarchies/race.csv",
                "sex": "examples/arx_example/hierarchies/sex.csv",
                "marital-status": "examples/arx_example/hierarchies/marital.csv",
                "occupation": "examples/arx_example/hierarchies/occupation.csv",
                "workclass": "examples/arx_example/hierarchies/workclass.csv",
                "education": "examples/arx_example/hierarchies/education.csv",
            },
            k=4,
            l=2,
            backend="arx",
        )
    
        result = AnonymizationManager.anonymize(config)
        print(result.get_anonymized_data_as_dataframe())
    

Quality Metrics

Anonymization is a trade-off between privacy and utility. By defining a quality_metric, you can guide the ARX engine to optimize the output for a specific type of data utility.

When configuring advanced metrics, keep the following rules in mind to ensure compatibility with the underlying ARX engine:

  • Parameter Order: Parameters in your configuration must follow the exact order and structure defined in the official ARX documentation.

  • Aggregate Functions: Any aggregate functions must be provided as ALL CAPS string .e.g. "SUM", "MEAN", "MAX".

In the following example, we use the Discernability metric.

from anonymization_manager import *

if __name__ == "__main__":
    config = AnonymizationConfig(
        data="examples/arx_example/data/adult.csv",
        identifiers=["education-num"],
        quasi_identifiers=[
            "age",
            "native-country",
            "race",
            "sex",
            "marital-status",
            "occupation",
            "workclass",
            "education",
        ],
        sensitive_attributes=["salary-class", "capital-gain", "capital-loss"],
        insensitive_attributes=["hours-per-week"],
        hierarchies={
            "age": "examples/arx_example/hierarchies/age.csv",
            "native-country": "examples/arx_example/hierarchies/country.csv",
            "race": "examples/arx_example/hierarchies/race.csv",
            "sex": "examples/arx_example/hierarchies/sex.csv",
            "marital-status": "examples/arx_example/hierarchies/marital.csv",
            "occupation": "examples/arx_example/hierarchies/occupation.csv",
            "workclass": "examples/arx_example/hierarchies/workclass.csv",
            "education": "examples/arx_example/hierarchies/education.csv",
        },
        k=4,
        l=2,
        quality_metric={"name":"discernability"},
        backend="arx",
    )

    result = AnonymizationManager.anonymize(config)
    print("Discernability : ", result.get_discernability_metric())