Skip to contents

**Generalised columns are refused.** Edit distance between a raw value and a published *region* – `adist("37", "[30,40)")` is 6 – measures the length of the bracket string and nothing else, but it is a plausible-looking number and no error is raised. On `docs/investigation/generalization-benchmark.R` that misuse reports a success rate of 0.1017 where [score_containment()] reports 0.4450, so the release looks about four times safer than it is (Issue #40, and `docs/lessons-learned.md` section 2). This stops instead; see [is_generalized_value()] for exactly what is detected, and note that a *categorical* generalisation (千代田区 published as 東京都) cannot be detected structurally at all.

Usage

score_char(
  dat_raw_anon,
  target,
  row_number = "ROW_NUMBER",
  generalized = c("stop", "warn", "ignore"),
  .fn_name = "score_char"
)

Arguments

dat_raw_anon

dataframe of raw_anon form

target

target column

row_number

name of the row-number column *before* the RAW_/ANON_ prefixing done by [join_raw_anon_data()] (default: "ROW_NUMBER")

generalized

what to do when `target` turns out to hold generalised values on the ANON side: `"stop"` (default), `"warn"` (compute the edit distances anyway, having said so) or `"ignore"` (skip the check). Use `"ignore"` only when the column is meant to be compared literally, e.g. when RAW and ANON carry the *same* already-binned values.

.fn_name

name used in error messages; a function that wraps this one passes its own name so the message points at the function the user actually called

Value

a "reid_scores" table whose SCORE is the edit distance between the RAW and ANON values of `target` (a distance: smaller is a better match).

See also

[score_containment()] for generalised columns.

Examples

raw  <- data.frame(ROW_NUMBER = 1:3, NAME = c("aaa", "bbb", "ccc"))
anon <- data.frame(ROW_NUMBER = 1:3, NAME = c("aax", "bbb", "ccd"))
d <- join_raw_anon_data(raw, anon)
score_char(d, "NAME")
#> reid scores (distance): 9 candidate pair(s), 3 ANON x 3 RAW record(s)
#>   RAW_ROW_NUMBER ANON_ROW_NUMBER SCORE
#> 1              1               1     1
#> 2              2               1     3
#> 3              3               1     3
#> 4              1               2     3
#> 5              2               2     0
#> 6              3               2     3
#> # ... 3 more pair(s)

# a generalised ANON column is refused rather than scored (Issue #40)
g_raw  <- data.frame(ROW_NUMBER = 1:3, AGE = c("31", "37", "46"))
g_anon <- data.frame(ROW_NUMBER = 1:3, AGE = c("30s", "30s", "40s"))
try(score_char(join_raw_anon_data(g_raw, g_anon), "AGE"))
#> Error : score_char(): column "AGE" is generalised on the ANON side (100% of its published values are regions) -- the published value is a region containing the raw one, not a value to compare with it (RAW "31" falls inside ANON "30s"; RAW "46" falls inside ANON "40s"). Comparing them directly measures the printed shape of the region, not the risk: on generalised data it reports a success rate several times lower than the real one and raises no error, so the release looks safer than it is (docs/lessons-learned.md section 2). Use score_containment(dat, "AGE"), which asks which RAW records could have produced each published region. If this column really is meant to be compared literally, pass generalized = "warn" or generalized = "ignore". In a column specification -- attacker_knowledge() or score_multi() -- declare it as "containment" instead: c(AGE = "containment").