anonymization-lib 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anonymization_lib/__init__.py +7 -0
- anonymization_lib/io/__init__.py +4 -0
- anonymization_lib/io/data_import.py +90 -0
- anonymization_lib/io/export.py +87 -0
- anonymization_lib/metrics/__init__.py +5 -0
- anonymization_lib/metrics/k_anonymity.py +391 -0
- anonymization_lib/metrics/l_diversity.py +393 -0
- anonymization_lib/metrics/t_closeness.py +618 -0
- anonymization_lib/techniques/__init__.py +6 -0
- anonymization_lib/techniques/generalization.py +907 -0
- anonymization_lib/techniques/pipeline.py +134 -0
- anonymization_lib/techniques/substitution.py +343 -0
- anonymization_lib/techniques/suppression.py +255 -0
- anonymization_lib/utils.py +153 -0
- anonymization_lib/visualization/Suggestion.py +730 -0
- anonymization_lib/visualization/__init__.py +4 -0
- anonymization_lib/visualization/visualization.py +845 -0
- anonymization_lib-1.4.0.dist-info/METADATA +302 -0
- anonymization_lib-1.4.0.dist-info/RECORD +22 -0
- anonymization_lib-1.4.0.dist-info/WHEEL +5 -0
- anonymization_lib-1.4.0.dist-info/licenses/LICENSE +21 -0
- anonymization_lib-1.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from .metrics import KAnonymity, LDiversity, TCloseness
|
|
2
|
+
from .techniques import Substitution, Suppression, Generalization
|
|
3
|
+
from .visualization import Visualization, AnonymizationAdvisor
|
|
4
|
+
from .io import DataExporter, DataImporter
|
|
5
|
+
|
|
6
|
+
__all__ = ["KAnonymity", "LDiversity", "TCloseness", "Substitution", "Suppression", "Generalization", "Visualization", "AnonymizationAdvisor", "DataExporter", "DataImporter"]
|
|
7
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pyspark.sql import DataFrame, SparkSession
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DataImporter:
|
|
6
|
+
"""
|
|
7
|
+
Utility class for importing datasets into Spark DataFrames.
|
|
8
|
+
|
|
9
|
+
Supported formats:
|
|
10
|
+
- csv
|
|
11
|
+
- parquet
|
|
12
|
+
- orc
|
|
13
|
+
- s3
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
VALID_FORMATS = {"csv", "parquet", "orc", "s3"}
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def import_data(
|
|
20
|
+
spark: SparkSession,
|
|
21
|
+
path: str,
|
|
22
|
+
file_format: str = "parquet",
|
|
23
|
+
header: bool = True,
|
|
24
|
+
infer_schema: bool = True
|
|
25
|
+
) -> DataFrame:
|
|
26
|
+
"""
|
|
27
|
+
Imports a dataset into a Spark DataFrame.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
spark : pyspark.sql.SparkSession
|
|
32
|
+
Active Spark session.
|
|
33
|
+
path : str
|
|
34
|
+
Source dataset path.
|
|
35
|
+
file_format : str, default="parquet"
|
|
36
|
+
Input format. Supported values are:
|
|
37
|
+
- 'csv'
|
|
38
|
+
- 'parquet'
|
|
39
|
+
- 'orc'
|
|
40
|
+
- 's3'
|
|
41
|
+
header : bool, default=True
|
|
42
|
+
Whether the CSV file contains a header row.
|
|
43
|
+
Ignored for non-CSV formats.
|
|
44
|
+
infer_schema : bool, default=True
|
|
45
|
+
Whether Spark should infer the schema automatically for CSV files.
|
|
46
|
+
Ignored for non-CSV formats.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
pyspark.sql.DataFrame
|
|
51
|
+
Imported Spark DataFrame.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
TypeError
|
|
56
|
+
If spark is not a SparkSession.
|
|
57
|
+
ValueError
|
|
58
|
+
If path or file_format are invalid.
|
|
59
|
+
FileNotFoundError
|
|
60
|
+
If the local source path does not exist.
|
|
61
|
+
"""
|
|
62
|
+
if not hasattr(spark, "read"):
|
|
63
|
+
raise TypeError("spark must provide a Spark-compatible read interface.")
|
|
64
|
+
|
|
65
|
+
if not isinstance(path, str) or not path.strip():
|
|
66
|
+
raise ValueError("The source path must be a non-empty string.")
|
|
67
|
+
|
|
68
|
+
if file_format not in DataImporter.VALID_FORMATS:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"Unsupported format. Use one of: {DataImporter.VALID_FORMATS}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if file_format == "s3":
|
|
74
|
+
if not path.startswith("s3a://"):
|
|
75
|
+
raise ValueError("S3 paths must start with 's3a://'")
|
|
76
|
+
return spark.read.parquet(path)
|
|
77
|
+
|
|
78
|
+
if file_format == "csv":
|
|
79
|
+
return (
|
|
80
|
+
spark.read
|
|
81
|
+
.option("header", header)
|
|
82
|
+
.option("inferSchema", infer_schema)
|
|
83
|
+
.csv(path)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if file_format == "parquet":
|
|
87
|
+
return spark.read.parquet(path)
|
|
88
|
+
|
|
89
|
+
if file_format == "orc":
|
|
90
|
+
return spark.read.orc(path)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from pyspark.sql import DataFrame
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DataExporter:
|
|
6
|
+
"""
|
|
7
|
+
Utility class for exporting Spark DataFrames to different storage formats.
|
|
8
|
+
|
|
9
|
+
Supported formats:
|
|
10
|
+
- csv
|
|
11
|
+
- parquet
|
|
12
|
+
- orc
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
VALID_FORMATS = {"csv", "parquet", "orc"}
|
|
16
|
+
VALID_MODES = {"overwrite", "append", "ignore", "error", "errorifexists"}
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def export(
|
|
20
|
+
df: DataFrame,
|
|
21
|
+
path: str,
|
|
22
|
+
file_format: str = "parquet",
|
|
23
|
+
mode: str = "overwrite",
|
|
24
|
+
header: bool = True
|
|
25
|
+
) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Exports a Spark DataFrame to the specified storage format.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
df : pyspark.sql.DataFrame
|
|
32
|
+
Spark DataFrame to export.
|
|
33
|
+
path : str
|
|
34
|
+
Destination path where the dataset will be written.
|
|
35
|
+
file_format : str, default="parquet"
|
|
36
|
+
Output format. Supported values are:
|
|
37
|
+
- 'csv'
|
|
38
|
+
- 'parquet'
|
|
39
|
+
- 'orc'
|
|
40
|
+
mode : str, default="overwrite"
|
|
41
|
+
Spark write mode. Supported values are:
|
|
42
|
+
- 'overwrite'
|
|
43
|
+
- 'append'
|
|
44
|
+
- 'ignore'
|
|
45
|
+
- 'error'
|
|
46
|
+
- 'errorifexists'
|
|
47
|
+
header : bool, default=True
|
|
48
|
+
Whether to include a header when exporting to CSV.
|
|
49
|
+
Ignored for Parquet and ORC.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
str
|
|
54
|
+
Confirmation message indicating that the export was completed.
|
|
55
|
+
"""
|
|
56
|
+
if not hasattr(df, "write"):
|
|
57
|
+
raise TypeError("df must provide a Spark-compatible write interface.")
|
|
58
|
+
|
|
59
|
+
if file_format not in DataExporter.VALID_FORMATS:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"Unsupported format. Use one of: {DataExporter.VALID_FORMATS}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if mode not in DataExporter.VALID_MODES:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"Unsupported mode. Use one of: {DataExporter.VALID_MODES}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if not isinstance(path, str) or not path.strip():
|
|
70
|
+
raise ValueError("The destination path must be a non-empty string.")
|
|
71
|
+
|
|
72
|
+
writer = df.write.mode(mode)
|
|
73
|
+
|
|
74
|
+
if file_format == "csv":
|
|
75
|
+
writer.option("header", header).csv(path)
|
|
76
|
+
|
|
77
|
+
elif file_format == "parquet":
|
|
78
|
+
writer.parquet(path)
|
|
79
|
+
|
|
80
|
+
elif file_format == "orc":
|
|
81
|
+
writer.orc(path)
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
f"Dataset successfully exported in '{file_format}' format to: "
|
|
85
|
+
f"{path}"
|
|
86
|
+
)
|
|
87
|
+
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
from pyspark.sql import DataFrame
|
|
2
|
+
from pyspark.sql import functions as F
|
|
3
|
+
|
|
4
|
+
from ..utils import EquivalenceGroups
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class KAnonymity(EquivalenceGroups):
|
|
8
|
+
"""
|
|
9
|
+
Calculates k-anonymity for a dataset based on a set of
|
|
10
|
+
quasi-identifiers.
|
|
11
|
+
|
|
12
|
+
K-anonymity measures whether each combination of quasi-identifier
|
|
13
|
+
values is shared by at least ``k`` records.
|
|
14
|
+
|
|
15
|
+
Records with the same values for all quasi-identifiers form an
|
|
16
|
+
equivalence group. The size of the smallest equivalence group
|
|
17
|
+
represents the k-anonymity level of the dataset.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
quasi_identifiers : list of str, optional
|
|
22
|
+
Columns used as quasi-identifiers to construct equivalence groups.
|
|
23
|
+
|
|
24
|
+
k_threshold : int, default=2
|
|
25
|
+
Minimum required size for every equivalence group.
|
|
26
|
+
|
|
27
|
+
A dataset satisfies the configured k-anonymity requirement when
|
|
28
|
+
every equivalence group contains at least ``k_threshold`` records.
|
|
29
|
+
|
|
30
|
+
Notes
|
|
31
|
+
-----
|
|
32
|
+
Equivalence groups are computed using native Spark operations, allowing
|
|
33
|
+
the calculation to be performed in a distributed manner.
|
|
34
|
+
|
|
35
|
+
No dataset records are collected to the driver.
|
|
36
|
+
|
|
37
|
+
The computation is lazy. Spark execution occurs only when an action is
|
|
38
|
+
performed on one of the resulting DataFrames.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
quasi_identifiers=None,
|
|
44
|
+
k_threshold: int = 2
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initializes the k-anonymity metric.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
quasi_identifiers : list of str, optional
|
|
52
|
+
Columns used as quasi-identifiers.
|
|
53
|
+
|
|
54
|
+
Column names must be non-empty strings and duplicate columns
|
|
55
|
+
are not allowed.
|
|
56
|
+
|
|
57
|
+
k_threshold : int, default=2
|
|
58
|
+
Minimum required size of every equivalence group.
|
|
59
|
+
|
|
60
|
+
The value must be an integer greater than 1.
|
|
61
|
+
|
|
62
|
+
Raises
|
|
63
|
+
------
|
|
64
|
+
ValueError
|
|
65
|
+
If ``quasi_identifiers`` is not a list, contains invalid or
|
|
66
|
+
duplicate column names, or ``k_threshold`` is not an integer
|
|
67
|
+
greater than 1.
|
|
68
|
+
"""
|
|
69
|
+
if quasi_identifiers is not None:
|
|
70
|
+
if not isinstance(quasi_identifiers, list):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
"'quasi_identifiers' must be a list of column names."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if not all(
|
|
76
|
+
isinstance(column, str) and column.strip()
|
|
77
|
+
for column in quasi_identifiers
|
|
78
|
+
):
|
|
79
|
+
raise ValueError(
|
|
80
|
+
"'quasi_identifiers' must contain only "
|
|
81
|
+
"non-empty strings."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if len(quasi_identifiers) != len(set(quasi_identifiers)):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"'quasi_identifiers' must not contain "
|
|
87
|
+
"duplicate columns."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
not isinstance(k_threshold, int)
|
|
92
|
+
or isinstance(k_threshold, bool)
|
|
93
|
+
or k_threshold <= 1
|
|
94
|
+
):
|
|
95
|
+
raise ValueError(
|
|
96
|
+
"'k_threshold' must be an integer greater than 1."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.quasi_identifiers = quasi_identifiers or []
|
|
100
|
+
self.k_threshold = k_threshold
|
|
101
|
+
|
|
102
|
+
def summary(
|
|
103
|
+
self,
|
|
104
|
+
df: DataFrame
|
|
105
|
+
):
|
|
106
|
+
"""
|
|
107
|
+
Computes k-anonymity statistics for the input dataset.
|
|
108
|
+
|
|
109
|
+
Records are grouped according to their values for the configured
|
|
110
|
+
quasi-identifiers. Each resulting equivalence group contains a
|
|
111
|
+
``group_size`` representing the number of records sharing that
|
|
112
|
+
combination of quasi-identifier values.
|
|
113
|
+
|
|
114
|
+
The k-anonymity value of the dataset is defined as the size of the
|
|
115
|
+
smallest equivalence group:
|
|
116
|
+
|
|
117
|
+
k = min(group_size)
|
|
118
|
+
|
|
119
|
+
Equivalence groups whose size is smaller than ``k_threshold`` are
|
|
120
|
+
considered violating groups.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
df : pyspark.sql.DataFrame
|
|
125
|
+
Input dataset containing the configured quasi-identifiers.
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
KAnonymityResult
|
|
130
|
+
Container containing:
|
|
131
|
+
|
|
132
|
+
- ``summary_df``:
|
|
133
|
+
DataFrame with aggregated k-anonymity statistics.
|
|
134
|
+
|
|
135
|
+
- ``violating_groups``:
|
|
136
|
+
Equivalence groups whose size is below the configured
|
|
137
|
+
``k_threshold``.
|
|
138
|
+
|
|
139
|
+
Raises
|
|
140
|
+
------
|
|
141
|
+
ValueError
|
|
142
|
+
If the input DataFrame is None, is not Spark-compatible, or
|
|
143
|
+
does not contain one or more configured quasi-identifiers.
|
|
144
|
+
|
|
145
|
+
Notes
|
|
146
|
+
-----
|
|
147
|
+
The computation uses native Spark aggregations and is performed
|
|
148
|
+
in a distributed manner.
|
|
149
|
+
|
|
150
|
+
This method does not trigger Spark execution by itself. Both
|
|
151
|
+
returned DataFrames are lazy and are evaluated only when a Spark
|
|
152
|
+
action is performed.
|
|
153
|
+
|
|
154
|
+
``summary_df`` and ``violating_groups`` share the same logical
|
|
155
|
+
equivalence-group computation. Without persistence, separate
|
|
156
|
+
actions on both result DataFrames may cause Spark to recompute
|
|
157
|
+
that shared lineage.
|
|
158
|
+
"""
|
|
159
|
+
if df is None:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"Input DataFrame cannot be None."
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Compatible with standard Spark and Spark Connect.
|
|
165
|
+
if not hasattr(df, "groupBy"):
|
|
166
|
+
raise ValueError(
|
|
167
|
+
"Input must be a Spark-compatible DataFrame."
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
self._validate_columns(
|
|
171
|
+
df,
|
|
172
|
+
self.quasi_identifiers
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# ------------------------------------------------------
|
|
176
|
+
# EQUIVALENCE GROUPS
|
|
177
|
+
# ------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
equivalence_groups = self._build_equivalence_groups(
|
|
180
|
+
df,
|
|
181
|
+
self.quasi_identifiers
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# ------------------------------------------------------
|
|
185
|
+
# VIOLATING GROUPS
|
|
186
|
+
# ------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
violating_groups = equivalence_groups.filter(
|
|
189
|
+
F.col("group_size") < self.k_threshold
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# ------------------------------------------------------
|
|
193
|
+
# SUMMARY
|
|
194
|
+
# ------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
summary_df = (
|
|
197
|
+
equivalence_groups
|
|
198
|
+
.agg(
|
|
199
|
+
F.count("*").alias(
|
|
200
|
+
"num_equivalence_groups"
|
|
201
|
+
),
|
|
202
|
+
F.min("group_size").alias(
|
|
203
|
+
"k_value"
|
|
204
|
+
),
|
|
205
|
+
F.max("group_size").alias(
|
|
206
|
+
"max_group_size"
|
|
207
|
+
),
|
|
208
|
+
F.round(
|
|
209
|
+
F.avg("group_size"),
|
|
210
|
+
2
|
|
211
|
+
).alias(
|
|
212
|
+
"avg_group_size"
|
|
213
|
+
),
|
|
214
|
+
F.sum(
|
|
215
|
+
F.when(
|
|
216
|
+
F.col("group_size") < self.k_threshold,
|
|
217
|
+
1
|
|
218
|
+
).otherwise(0)
|
|
219
|
+
).alias(
|
|
220
|
+
"num_violating_groups"
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
.withColumn(
|
|
224
|
+
"k_threshold",
|
|
225
|
+
F.lit(self.k_threshold)
|
|
226
|
+
)
|
|
227
|
+
.withColumn(
|
|
228
|
+
"satisfies_k_anonymity",
|
|
229
|
+
F.col("k_value")
|
|
230
|
+
>= F.col("k_threshold")
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return KAnonymityResult(
|
|
235
|
+
summary_df=summary_df,
|
|
236
|
+
violating_groups=violating_groups
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class KAnonymityResult:
|
|
241
|
+
"""
|
|
242
|
+
Container for k-anonymity results.
|
|
243
|
+
|
|
244
|
+
The object stores both the aggregated summary of the k-anonymity
|
|
245
|
+
analysis and the equivalence groups that violate the configured
|
|
246
|
+
k-threshold.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
summary_df : pyspark.sql.DataFrame
|
|
251
|
+
DataFrame containing aggregated k-anonymity statistics.
|
|
252
|
+
|
|
253
|
+
violating_groups : pyspark.sql.DataFrame
|
|
254
|
+
Equivalence groups whose size is below the configured k-threshold.
|
|
255
|
+
|
|
256
|
+
Notes
|
|
257
|
+
-----
|
|
258
|
+
The stored DataFrames remain lazy Spark DataFrames. Creating this
|
|
259
|
+
result object does not trigger Spark execution.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
def __init__(
|
|
263
|
+
self,
|
|
264
|
+
summary_df: DataFrame,
|
|
265
|
+
violating_groups: DataFrame
|
|
266
|
+
):
|
|
267
|
+
"""
|
|
268
|
+
Initializes the k-anonymity result container.
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
summary_df : pyspark.sql.DataFrame
|
|
273
|
+
Aggregated statistics of k-anonymity across equivalence groups.
|
|
274
|
+
|
|
275
|
+
violating_groups : pyspark.sql.DataFrame
|
|
276
|
+
Subset of equivalence groups whose size is below the configured
|
|
277
|
+
k-threshold, representing potential privacy risks.
|
|
278
|
+
"""
|
|
279
|
+
self.summary_df = summary_df
|
|
280
|
+
self.violating_groups = violating_groups
|
|
281
|
+
|
|
282
|
+
def show_summary(self) -> None:
|
|
283
|
+
"""
|
|
284
|
+
Displays the aggregated k-anonymity summary.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
None
|
|
289
|
+
The summary is displayed using Spark's ``show`` method.
|
|
290
|
+
|
|
291
|
+
Notes
|
|
292
|
+
-----
|
|
293
|
+
Calling this method triggers Spark execution.
|
|
294
|
+
"""
|
|
295
|
+
self.summary_df.show(
|
|
296
|
+
truncate=False
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def show_violating_groups(
|
|
300
|
+
self,
|
|
301
|
+
n: int = 10,
|
|
302
|
+
sort: bool = False
|
|
303
|
+
) -> None:
|
|
304
|
+
"""
|
|
305
|
+
Displays equivalence groups that do not satisfy k-anonymity.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
n : int, default=10
|
|
310
|
+
Maximum number of violating groups to display.
|
|
311
|
+
|
|
312
|
+
sort : bool, default=False
|
|
313
|
+
Whether to sort violating groups by ``group_size`` in
|
|
314
|
+
ascending order before displaying them.
|
|
315
|
+
|
|
316
|
+
Sorting may increase computational cost for large datasets
|
|
317
|
+
because it can require an additional distributed ordering
|
|
318
|
+
operation.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
None
|
|
323
|
+
The selected violating groups are displayed using Spark's
|
|
324
|
+
``show`` method.
|
|
325
|
+
|
|
326
|
+
Raises
|
|
327
|
+
------
|
|
328
|
+
ValueError
|
|
329
|
+
If ``n`` is not a positive integer or ``sort`` is not a
|
|
330
|
+
boolean.
|
|
331
|
+
|
|
332
|
+
Notes
|
|
333
|
+
-----
|
|
334
|
+
Calling this method triggers Spark execution.
|
|
335
|
+
"""
|
|
336
|
+
if (
|
|
337
|
+
not isinstance(n, int)
|
|
338
|
+
or isinstance(n, bool)
|
|
339
|
+
or n <= 0
|
|
340
|
+
):
|
|
341
|
+
raise ValueError(
|
|
342
|
+
"'n' must be a positive integer."
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
if not isinstance(sort, bool):
|
|
346
|
+
raise ValueError(
|
|
347
|
+
"'sort' must be a boolean."
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
df = self.violating_groups
|
|
351
|
+
|
|
352
|
+
if sort:
|
|
353
|
+
df = df.orderBy(
|
|
354
|
+
F.col("group_size").asc()
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
df.show(
|
|
358
|
+
n,
|
|
359
|
+
truncate=False
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
def get_summary_df(self) -> DataFrame:
|
|
363
|
+
"""
|
|
364
|
+
Returns the aggregated k-anonymity summary.
|
|
365
|
+
|
|
366
|
+
Returns
|
|
367
|
+
-------
|
|
368
|
+
pyspark.sql.DataFrame
|
|
369
|
+
DataFrame containing the aggregated k-anonymity statistics.
|
|
370
|
+
|
|
371
|
+
Notes
|
|
372
|
+
-----
|
|
373
|
+
Returning the DataFrame does not trigger Spark execution.
|
|
374
|
+
"""
|
|
375
|
+
return self.summary_df
|
|
376
|
+
|
|
377
|
+
def get_violating_groups(self) -> DataFrame:
|
|
378
|
+
"""
|
|
379
|
+
Returns the equivalence groups that violate k-anonymity.
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
pyspark.sql.DataFrame
|
|
384
|
+
Equivalence groups whose size is below the configured
|
|
385
|
+
k-threshold.
|
|
386
|
+
|
|
387
|
+
Notes
|
|
388
|
+
-----
|
|
389
|
+
Returning the DataFrame does not trigger Spark execution.
|
|
390
|
+
"""
|
|
391
|
+
return self.violating_groups
|