【问题标题】:In R: How to consistently replace (/ anonimize) ids or names within two separate data frames在 R 中:如何在两个单独的数据框中一致地替换(/匿名化)ID 或名称
【发布时间】:2021-03-13 09:53:32
【问题描述】:

假设我有两个数据框,df1df2。两个数据帧都有一个标识符id。我的目标是在此标识符上合并这些数据集,但我想匿名化id 列中的名称。但是,问题是我想分别对两个数据集这样做,因此对于 df1df2 而不是直接在 df3 上(因为这很容易:只需用一些随机字符替换 id 列)

我认为我的解决方案需要看起来像这样。首先,我创建了一个单独的数据框,其中包含来自df1df2 的所有唯一ids。然后,我需要分配一些随机化,例如idxxxx,其中xxxx 是此单独数据框中每个id 的唯一编号。使用dplyrgsubstringr 方法,我可以根据在此单独数据框中分配的值替换df1df2 中的ids。在此之后,我将合并两个数据集。

这里我有两个示例数据框,我尝试解决的问题,以及想要的结果。请注意,id 的数量对我来说并不重要(例如,John Terry 的 id0004 或 id0003 无关紧要,只要它在两个数据帧中始终如一地更改即可。

有人可以帮我解决这个问题吗?谢谢!

id <- c("John Williams", "John Terry", "Rick Fire", "Katie Blue", "Unknown")
row1 <- c("28", "17", "17", "29", "39")
df1 <- data.frame(id,row1)

id <- c("Frank Johnson", "John Terry", "Rick Fire", "Katie Blue")
row2 <- c("Purple", "Red", "Yellow", "Green")
df2 <- data.frame(id,row2)

df3 <- merge(df1, df2, all.x = TRUE, all.y = TRUE)

#My try
#Make separate data frame
id_df <- merge(df1, df2, all.x = TRUE, all.y = TRUE)
id_df <- subset(id_df,TRUE,select = c(id))
id_df$anonymous <- id_df %>% mutate(id = row_number()) #it would be nicer to have something like id0001

#Replace ids within df1 and df2 according to the id_df anonymous variable
library(stringr)
df1$id <- str_replace(df1$id, id_df$id, as.character(id_df$anonymous)) #replacement does not work

#desired result
#df1        row1
#id0003     28 
#id0002     17
#id0005     17
#id0004     29
#id0006     39

#df2        row2
#id0001     Purple
#id0002     Red
#id0005     Yellow
#id0004     Green

#df3
#id         #row1       #row2
#id0001     NA          Purple
#id0002     17          Red
#id0003     28          NA
#id0004     29          Green
#id0005     17          Yellow
#id0006     39          NA

【问题讨论】:

    标签: r dataframe replace dplyr


    【解决方案1】:

    一个 tidyverse 解决方案,满足您的要求,在您加入之前不要在 df3 上操作

    id_df <- data.frame(id = union(df1$id,df2$id))
    id_df <- 
       id_df %>% 
       mutate(anonymous = paste0("id", stringr::str_pad(row_number(), 
                                                         width = 4, 
                                                         pad = 0)))
    
    newdf1 <- left_join(df1, id_df) %>% select(-id) %>% relocate(anonymous)
    
    newdf2 <- left_join(df2, id_df) %>% select(-id) %>% relocate(anonymous)
    
    full_join(newdf1, newdf2)
    
    #> Joining, by = "anonymous"
    #>   anonymous row1   row2
    #> 1    id0001   28   <NA>
    #> 2    id0002   17    Red
    #> 3    id0003   17 Yellow
    #> 4    id0004   29  Green
    #> 5    id0005   39   <NA>
    #> 6    id0006 <NA> Purple
    

    【讨论】:

      【解决方案2】:

      这是一个所有基数为 R 的解决方案(没有 tidyverse)。我们创建一个包含所有唯一 ID 的查找表(使用集合操作 union 来查找 ID),然后 merge 分别与每个数据帧创建查找表。

      # Find all unique ids and create a lookup table.
      all_ids <- union(df1$id, df2$id)
      id_df <- data.frame(id = all_ids, code = paste0('id', sprintf('%04d', 1:length(all_ids))))
      
      # Merge df1 with the lookup table, remove the id column, and rename the code column to id.
      df1 <- merge(df1, id_df, all.x = TRUE)
      df1 <- df1[, c('code', 'row1')]
      names(df1)[1] <- 'id'
      
      # Repeat for df2
      df2 <- merge(df2, id_df, all.x = TRUE)
      df2 <- df2[, c('code', 'row2')]
      names(df2)[1] <- 'id'
      
      df3 <- merge(df1, df2, all.x = TRUE, all.y = TRUE)
      

      请注意,sprintf('%04d, ...)` 会将数字代码用零填充到总长度为 4。

      【讨论】:

      • 是的,感谢您了解这一点。我认为@Allan Cameron 的解决方案在真正的匿名化方面可能更胜一筹!
      • 感谢您的解决方案,它确实有效!
      【解决方案3】:

      如果您想匿名化 id 并使反转它们变得相当困难,您可以计算每个字符串的 md5 哈希值。两个相同的字符串会产生相同的 md5 哈希:

      df1$id <- sapply(df1$id, digest::digest, algo = "md5")
      df2$id <- sapply(df2$id, digest::digest, algo = "md5")
      df3 <- merge(df1, df2, all.x = TRUE, all.y = TRUE)
      
      df3
      #>                                 id row1   row2
      #> 1 22e35044ed452870ad5b014e87121d9d   39   <NA>
      #> 2 69d61b42a2f549c4765699f06de3b351   28   <NA>
      #> 3 ad1cc76e26c5d73ba4a03bf51df1b6af   17 Yellow
      #> 4 b3bdcc4913da319308e6ddf47e09da12 <NA> Purple
      #> 5 badea53ae1e8a2fa66ebd1cdde9dd413   17    Red
      #> 6 d1f305c19a2f9649abe11efcf26ac645   29  Green
      

      【讨论】:

      • 谢谢,这看起来是一个合适且简单的解决方案,比我的建议简单。
      • 您可以阅读更多关于它的信息here@Silhouettes,但实际上,除了希望花费时间和资源对散列。
      • 好的,一个后续问题:如何避免为我的数据集中的 NA 值创建 md5 哈希? (例如,假设 df1 在 id 列中有一个 NA)?
      • @Silhouettes 您希望 NA 值是多少?如果您希望他们保持 NA,您可以使用 df1$id[!is.na(df1$id)] &lt;- sapply(df1$id[!is.na(df1$id)], digest::digest, algo = "md5")
      猜你喜欢
      • 1970-01-01
      • 2021-02-18
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多