Checks whether a data.frame satisfies k-anonymity over the supplied
quasi-identifier columns. A dataset is k-anonymous if every combination
of quasi-identifier values appears in at least k rows.
Value
A list with class "morie_k_anon" containing:
satisfieslogical, whether the dataset is k-anonymous.
kthe threshold used.
min_class_sizeinteger, size of the smallest class.
n_classesinteger, total number of equivalence classes.
n_violationsinteger, number of classes below the threshold.
violating_classesdata.frame of class keys plus their
.nsizes (empty data.frame when none).summaryhuman-readable one-line summary.
Details
A missing value (NA) in a quasi-identifier is treated as a value of
its own: rows with an unrecorded ward, age band or race form their own
classes and are counted, never dropped. Unrecorded values are often the
rarest records, which makes them the most re-identifiable, so they must
meet the threshold like any other class. Every row is accounted for:
the class sizes sum to nrow(data). NaN counts as missing, in the
same class as NA.
Values are compared exactly. Two numbers are in one class only when
they are the same number in storage, whatever the column's class
(plain double, Date, POSIXct, difftime, I() and other classed
numbers alike), so 0.1 + 0.2 and 0.3, which differ in the last bit,
are two classes, and so are two times a microsecond apart; -0 and 0
are one. A complex number is compared by both parts. Integer, logical,
character and raw columns are compared by value, a factor by its level
label and a POSIXlt by the instant it names. A factor level that is
itself NA is missing, and the string "NA" is a value, not a missing
one. A column that cannot be compared exactly is refused with an error
naming it: a bit64::integer64 column (convert it with
as.character()), a list column and a matrix column.
Examples
df <- data.frame(
age = c(25, 25, 25, 32, 32, 40),
sex = c("F", "F", "F", "M", "M", "M")
)
# k = 2: the class {age=40, sex=M} has only 1 row -> VIOLATED.
res <- kanonvrf(df, c("age", "sex"), k = 2)
res$summary
#> [1] "k=2: VIOLATED (min class size=1; 1/3 classes below threshold)"
res$satisfies
#> [1] FALSE
res$violating_classes # the offending quasi-identifier combos
#> age sex .n
#> 1 40 M 1
# Loosening to k = 1 always holds; the default k = 5 is stricter.
kanonvrf(df, c("age", "sex"), k = 1)$satisfies
#> [1] TRUE
kanonvrf(df, c("age", "sex"))$satisfies # k = 5
#> [1] FALSE
# A single quasi-identifier is fine too.
kanonvrf(df, "sex", k = 3)$min_class_size
#> [1] 3
# On real bundled data: are (year, arrest) cells 5-anonymous?
kanonvrf(complaint_sample,
c("year", "arrest"),
k = 5
)$summary
#> [1] "k=5: VIOLATED (min class size=1; 1/3 classes below threshold)"
