【问题标题】:Deidentifying data and creating crosswalk using duawranglr in R在 R 中使用 duawranglr 去识别数据并创建人行横道
【发布时间】:2020-02-05 17:40:04
【问题描述】:

我正在尝试使用此示例中提供的 R 中的 duawranglr 包对数据进行去标识化:https://cran.r-project.org/web/packages/duawranglr/vignettes/securing_data.html

例如,我创建了一个数据框:

 data <- data.frame(
        Name = c("Kate", "Jane", "Rod", "Jan", "Martin"),
        V1 = c(16, 20, 34, 25, 26),
        V2 = c(3, 7, 5, 3, 2)
        )

我正在尝试使用 deid_dua 函数创建与 Name 列对应的没有人行横道的唯一十六进制字符串。

data <- deid_dua(data, id_col = "Name", new_id_name = "DID", write_crosswalk = TRUE, id_length = 12)

我不断收到的错误是:

Error in data.frame(old = old_ids, new = new_ids, stringsAsFactors = FALSE) : 
  arguments imply differing number of rows: 5, 0 

起初我认为问题在于名称列是一个因素。但是,在使用 data.frame 中的 stringsAsFactors = FALSE 语句将其转换为字符后,我收到了同样的错误。如果我需要这些语句,我也不确定基于 CRAN 示例:

admin_file <- system.file('extdata', 'admin_data.csv', package = 'duawranglr')
df <- read_dua_file(admin_file)
df

如果您不导入数据,它们是否适用?该示例没有很好地解释它们的用途。

【问题讨论】:

  • 你需要一个包去识别数据吗?为什么不分配一个新的 ID 列并删除“名称”列?您需要为人行横道保留包含 ID 和名称的完整数据集。
  • 确实如此,但是,我希望新的 ID 列是一长串随机字母/数字以增加安全性。

标签: r package


【解决方案1】:

这是一个更简单的解决方案:

# create a custom 8-digit random identifier string called ID:
library(stringi)
data$ID <- stri_rand_strings(nrow(data), 8)

# remove the name column to create a de-identified dataset
data_deidentified <- data[,-1]

您的 data_deidentified 数据框将如下所示:

  V1 V2       ID
1 16  3 V2Hziep8
2 20  7 vFeQW1OQ
3 34  5 E5vcWYfm
4 25  3 VLbHzU3H
5 26  2 acCbXiO1

显然保留原始数据数据框作为您的人行横道。您可以通过更改该调用中的“8”值来延长 ID 变量。

现在,如果您的数据中有重复的名称,您将需要执行一些额外的步骤:

# note that I've modified the original dataframe to include two "Martin" values:
data <- data.frame(Name = c("Kate", "Jane", "Rod", "Jan", "Martin", "Martin"),
                 V1 = c(16, 20, 34, 25, 26, 28),
                 V2 = c(3, 7, 5, 3, 2, 5))

# get list of unique names and convert to dataframe
names <- data.frame('Name' = unique(data$Name))
# assign ID string to each unique name
names$ID <- stri_rand_strings(nrow(names), 8)
# now merge back into original df
data <- merge(data, names)

你的结果是:

    Name V1 V2       ID
1    Jan 25  3 e8da7lO4
2   Jane 20  7 pGeeklL1
3   Kate 16  3 5yYAtO9B
4 Martin 26  2 BwC6jPBh
5 Martin 28  5 BwC6jPBh
6    Rod 34  5 f3xvGbu2

【讨论】:

  • 谢谢,这行得通!不过,我确实有一个问题:如果存在需要相同 ID 的重复观察结果怎么办?
  • 这是一个很好的自制解决方案,非常适合所述示例的范围。如果有重复的 id,它可能不适用于真实数据(尽管修复这个问题也相当简单)
  • 我在上面添加了一个附加选项来处理原始数据中可能出现的重复名称。
【解决方案2】:

如果我不先设置人行横道,我会收到错误消息,但这很简单:

library(duawranglr)

df <- data.frame(Name = c("Kate", "Jane", "Rod", "Jan", "Martin"),
                  V1 = c(16, 20, 34, 25, 26),
                  V2 = c(3, 7, 5, 3, 2))

# You only have a single column to obscure, so you only need a one-cell data frame to set up
set_dua_cw(data.frame(secure = "Name"))
#> -- duawranglr note -------------------------------------------------------------------
#> DUA crosswalk has been set!

# Simultaneously secure the data and write the crosswalk
df <- deid_dua(df,
               id_col = "Name",
               new_id_name = "ID",
               write_crosswalk = T,
               id_length = 12,
               crosswalk_filename = "cw.csv")

print(df)
#>             ID V1 V2
#> 1 950dce035280 16  3
#> 2 6b95d061b59f 20  7
#> 3 00a5d8ab2a4c 34  5
#> 4 ea03e704d806 25  3
#> 5 3eba984ebcba 26  2

你可以通过读取csv文件的内容来查看人行横道的内容

read.csv("cw.csv")
#>     Name           ID
#> 1   Kate 950dce035280
#> 2   Jane 6b95d061b59f
#> 3    Rod 00a5d8ab2a4c
#> 4    Jan ea03e704d806
#> 5 Martin 3eba984ebcba

如果你想在未来找回这些名字,你可以这样做:

cw <- read.csv("cw.csv")
df$Name <- cw$Name[match(cw$ID, df$ID)]

【讨论】:

  • 好的,我仍然遇到同样的错误。 set_dua_cw(data.frame(Name = 1:nrow(data))) deid_dua(data, id_col = "Name", new_id_name = "DID", id_length = 12) 还有,人行横道里是name列吗?
  • @llsabang 我做了一些小的调整。如果你重新启动 R 并运行脚本,你应该得到和我一样的输出。
  • 嗨,艾伦,这似乎是正确的,但它为每个人提供了相同的 ID。另外,我收到一条警告,“行名是从一个短变量中找到的并且已被丢弃”。我已经多次查看并输入了代码,所以我不确定出了什么问题。也许与我的 R 版本解释变量的方式有关? V1 和 V2 是数字,我尝试将 Name 既作为字符又作为因子。感谢您的帮助。
【解决方案3】:

我有点晚了,但作为包作者,我会尽力解决一些困惑。

tl;博士

@Allan Cameron 给出的答案对我有用,但如果您只想散列您的 ID,那么@mh765 的解决方案可能是最好的。

duawranglr 用途的详细说明

duawranglr 假设您有一个受限制的数据框,并且您想要做两件事以便可以共享它:

  1. 删除包含受限数据元素(如 DOB 或 其他识别信息)
  2. 将唯一标识符转换为另一个无法用于返回原始 ID 的唯一 ID(以防原始 ID 也受到限制,例如 SSN)

由于您没有尝试执行 #1,因此有一个只有一列和一个元素的 DUA 人行横道是有意义的:您的 ID 列的名称(根据 @Allan Cameron)。

但是假设您有两个潜在的安全级别,而在第二个级别中,您不能包含V1。那么你的 DUA 人行横道可能看起来像这样:

library(duawranglr)

## your data frame
df <- data.frame(Name = c("Kate", "Jane", "Rod", "Jan", "Martin"),
                   V1 = c(16, 20, 34, 25, 26),
                   V2 = c(3, 7, 5, 3, 2))

## create dua crosswalk
dua_cw <- data.frame(secure_level_i = c("Name",""), 
                     secure_level_ii = c("Name", "V1"))

## show cw (level_i won't allow name; level_ii won't allow name or V1)
dua_cw
  secure_level_i secure_level_ii
1           Name            Name
2                             V1

## set the dua cw
set_dua_cw(dua_cw)
-- duawranglr note -------------------------------------------------------------
DUA crosswalk has been set!

现在您可以设置安全级别。假设您将其设置为secure_level_i,这意味着可以将V1 保留在您共享的最终数据框中:

## set DUA level
set_dua_level("secure_level_i", deidentify_required = TRUE, id_column = "Name")
-- duawranglr note -------------------------------------------------------------
Unique IDs in [ Name ] must be deidentified; use -deid_dua()-.

现在您可以使用deid_dua() 对您的 ID 进行哈希处理,在本例中为名称。

## deidentify data (don't need to set id_col since we set it in set_dua_level)
df <- deid_dua(df, 
               new_id_name = "DID", 
               write_crosswalk = TRUE, 
               id_length = 12,
               crosswalk_filename = "cw.csv")

## show result
df
           DID V1 V2
1 d164bb624da2 16  3
2 a8b33e3b0230 20  7
3 a1d287cbdde7 34  5
4 1c00ba576e1a 25  3
5 a870564b3365 26  2

## show crosswalk
read.csv("cw.csv")

    Name          DID
1   Kate d164bb624da2
2   Jane a8b33e3b0230
3    Rod a1d287cbdde7
4    Jan 1c00ba576e1a
5 Martin a870564b3365

## check restrictions to see if you can save data
check_dua_restrictions(df)
-- duawranglr note -------------------------------------------------------------
Data set has passed check and may be saved.

但是,如果您将 set_dua_level() 更改为 "secure_level_ii",那么您将无法通过最后一次检查,因为您的数据中仍然有 V1

## set new more secure level 
set_dua_level("secure_level_ii", deidentify_required = TRUE, id_column = "Name")
-- duawranglr note -------------------------------------------------------------
Unique IDs in [ Name ] must be deidentified; use -deid_dua()-.

## check again
check_dua_restrictions(df)
-- duawranglr note -------------------------------------------------------------
The following variables are not allowed at the current data usage level
restriction [ secure_level_ii ] and MUST BE REMOVED before saving:

 - V1

要通过新级别,您需要从数据框中删除 V1

## drop
df$V1 <- NULL

## check again
check_dua_restrictions(df)
-- duawranglr note -------------------------------------------------------------
Data set has passed check and may be saved.

最后一点,您的id_col 必须包含唯一的 ID。这些名称在玩具示例中有效,因为它们是唯一的,但正如其他人所指出的那样,不同观察的重复名称不适用于 duawranglr。

【讨论】:

    猜你喜欢
    • 2014-02-04
    • 2011-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    相关资源
    最近更新 更多