
Cell suppression with complementary suppression and a recoverability audit
Source:R/k_anonymity.R
cellsupp.RdSuppresses (sets to NA) every positive count below threshold, then, when
retcomp = TRUE, hides further cells until no suppressed
value can be solved exactly from the table's row and column totals, which
a published table normally carries.
Value
A list with class "cellsupp":
suppressednumeric matrix, suppressed cells set to NA.
primary_masklogical matrix, TRUE for primary suppressions.
complementary_masklogical matrix, TRUE for complementary suppressions (all FALSE when
retcomp = FALSE).recoverable_masklogical matrix, TRUE for a hidden cell whose value the row and column totals determine exactly.
protectedlogical, TRUE when no hidden cell is recoverable. Test this before publishing.
n_primaryinteger.
n_complementaryinteger.
n_recoverableinteger.
thresholdthe threshold used.
Details
Complements are added to a fixed point. A row or column holding exactly one hidden cell gives that cell away by subtraction, so every line with a hidden cell gets a second one; a complement placed for a row is a hidden cell in its column too, so the passes repeat until no line holds a lone hidden cell. That condition is necessary but not sufficient, so the result is then audited exactly: a hidden cell is recoverable when the linear system of row and column totals determines it (its indicator lies in the row space of the constraints over the hidden cells). Any recoverable cell receives another complement in its row, and the audit repeats. Complements are taken from positive cells, preferring one in a line that already holds a hidden cell (which creates no new lone cell), then the largest such cell, which leaves the widest range for the hidden values. A zero cell is never used as a complement: it would bound the hidden values from below at nothing.
The audit covers exact recovery from the margins. It does not compute the feasible interval of each hidden cell, which bounds from non-negativity can narrow; a table whose hidden values must stay inside a wide interval needs an interval audit (linear programming) on top.
Cells must be non-negative whole numbers or NA; infinite,
fractional and negative cells are refused. NA cells in the input
pass through unchanged and are treated as unpublished: they count as
hidden in the audit (their values are not known to a reader) and receive
complements like any hidden cell, but they are not counted as primary or
complementary suppressions. A zero cell is published as it is: the
threshold rule protects the few contributors of a small cell, and a zero
has none.
Examples
tbl <- matrix(c(120, 3, 47, 88, 2, 99, 14, 51, 60),
nrow = 3,
dimnames = list(c("A", "B", "C"), c("X", "Y", "Z"))
)
res <- cellsupp(tbl, threshold = 5)
res$suppressed # NA where suppressed
#> X Y Z
#> A NA NA 14
#> B NA NA 51
#> C 47 99 60
res$protected # no hidden cell can be solved from the totals
#> [1] TRUE
res$n_primary # cells below threshold
#> [1] 2
res$n_complementary # extra cells hidden to protect them
#> [1] 2
# Without complements the small cells are hidden but recoverable.
bare <- cellsupp(tbl, threshold = 5, retcomp = FALSE)
bare$protected
#> [1] FALSE
bare$recoverable_mask
#> X Y Z
#> A FALSE FALSE FALSE
#> B TRUE TRUE FALSE
#> C FALSE FALSE FALSE
# Works on a 2-D table too; NA cells pass through untouched.
t2 <- as.table(matrix(c(2, 40, 30, 1), 2,
dimnames = list(c("a", "b"), c("c", "d"))
))
cellsupp(t2, threshold = 5)$suppressed
#> c d
#> a
#> b