This page is the one to read before deciding whether you are allowed to share a blinded file. It is deliberately the least flattering page in the documentation.
datablinder reduces the risk of disclosing the values in
your data. It does not implement differential privacy,
k-anonymity, or any other model with a provable bound, and it
makes no claim of one.
What it does is structural: character, factor, label and identifier columns are built from the shape of the real values rather than from the values, and any overlap with the real column is treated as a bug — a hard failure that stops anything from being written.
This is the part people miss. All of the following survive into the blinded copy, because they are exactly what makes the code work:
blind_names = TRUErowsSo if the structure itself is confidential — the fact that
you hold a column called hiv_status, or that your file has
exactly 1,204 rows, or that a measurement ranges where it does — a
blinded copy does not solve your problem. Use
blind_names = TRUE, and then think again about whether to
share the file at all.
Columns are blinded independently. No relationship between columns is preserved, and none is even attempted.
d <- data.frame(x = 1:200, y = 1:200 * 2) # perfectly correlated
cor(d$x, d$y)
#> [1] 1
fake <- blind_data(d, seed = 1)
round(cor(fake$x, fake$y), 2) # the relationship is gone
#> [1] 0.1So no correlation, model coefficient, group difference, cross-tabulation or plot made from the blinded copy tells you anything about the real data. Results will be structurally similar and numerically meaningless. If an AI assistant interprets the numbers it sees in the copy, it is interpreting noise — say so in your prompt.
age > 60 & treated == 1 will run,
but the number of rows it returns in the copy means nothing.A,
B, C, so region == "North"
matches nothing. That is what keep_labels = TRUE is for,
and it keeps real labels — only use it when the labels are not
themselves sensitive. When the code needs a whole column as it is,
keep_real is the heavier tool; see below.Type detection works from the values, which means it needs values to work from. On a short file there is little to tell an identifier from a measurement, or a code from free text. A column detected as the wrong type is still blinded, and still leak-checked, but it may be blinded in a way that breaks the code you get back — or, more importantly, in a way you did not expect.
The summary printed after every run is the list of those decisions. Read it. That is the whole reason it exists and gets a Copy button in the app.
You will sometimes see a line like this:
Leak check: passed
Exact numeric or date coincidences: 33 (expected with rounded values and shifted date ranges)
That count is expected and is not a leak of anything usable. A column of ages rounded to whole years has only so many distinct values available; date ranges are shifted rather than relocated; placeholder words come from a fixed list. It is reported so that you can see it rather than have it hidden from you.
The columns where a coincidence would mean something — character, factor, label and identifier — are checked for zero overlap, and a single match there fails the run.
keep_real is the one place the promise is switched
offEverything above assumes the default, which keeps no real value.
keep_real names columns to copy over exactly as they are,
because code often cannot be written at all without one:
arm == "placebo", group_by(site).
Those columns are shared. That is the point of the option and the cost of it, so it is worth being deliberate:
sdcMicro is
built for; see vignette("alternatives").Leak check: passed, except 2 columns kept real: arm, visit,
and each kept column’s own line reads
REAL VALUES KEPT, not blinded. A bare
Leak check: passed never appears when part of the file is
real.rows cannot be used at the same time, because a kept
column stays matched to the rest of its row.If a column is only needed for its categories and not its contents,
keep_labels = TRUE does the job and keeps no values.
127.0.0.1. Sharing
the blinded file with an assistant is something you do afterwards,
deliberately.keep_labels = TRUE and the columns you name in
keep_real, both of which are off by default and reported in
the summary when used..Random.seed is put back as it was found.Preserving relationships between columns, reading from a database,
and complex objects such as Seurat or
SingleCellExperiment. Per-column control stops at
keep_real: whether to blind a column, but not how.
keep_real
is the one place the promise is switched off