通过r中所有可能的id合并两个数据集

6ljaweal  于 2023-06-19  发布在  其他
关注(0)|答案(3)|浏览(121)

下面有两个dataframes:

df1 <- data.frame(id = c(11,22,33,44,55),
                  score = c(20,22,33,22,11))

df2 <- data.frame(id1 = c(11,22,NA,NA,8),
                  id2 = c(98,9,33,NA,24),
                  id3 = c(NA,NA,66,44,88),
                  id4 = c(NA,NA,NA,16,17))

> df1
  id score
1  11    20
2  22    22
3  33    33
4  44    22
5  55    11
> df2
  id1 id2 id3 id4
1   11  98  NA  NA
2   22   9  NA  NA
3  NA   33  66  NA
4  NA  NA   44  16
5   8  24   88  17

我需要通过df1中的iddf2中的四个id合并两个数据集。我还想保留df1中任何不匹配的id。我怎样才能一次达到如下所示的预期输出?

df.merged <- data.frame(id = c(11,22,33,44,55),
                        score = c(20,22,33,22,11),
                        id1 = c(11,22,NA,NA, NA),
                        id2 = c(98,9,33,NA,NA),
                        id3 = c(NA,NA,66,44,NA),
                        id4 = c(NA,NA,NA,16,NA))

> df.merged
  id score id1 id2 id3 id4
1  11    20   11  98  NA  NA
2  22    22   22   9  NA  NA
3  33    33  NA   33  66  NA
4  44    22  NA  NA   44  16
5  55    11  NA  NA  NA  NA
hgc7kmma

hgc7kmma1#

你可以“半透视”df2,这样就有一个包含所有id的id列,同时保留列id1-id4,然后用id连接:

library(dplyr)
library(tidyr)

df2_semi_pivot <- df2 %>% 
  mutate(across(id1:id4, .names = "{col}_pivot")) %>% 
  pivot_longer(ends_with("pivot"), names_to = NULL, values_to = "id")

left_join(df1, df2_semi_pivot)
id score id1 id2 id3 id4
1 11    20  11  98  NA  NA
2 22    22  22   9  NA  NA
3 33    33  NA  33  66  NA
4 44    22  NA  NA  44  16
5 55    11  NA  NA  NA  NA
6ovsh4lw

6ovsh4lw2#

您可以将来自df2的所有id粘贴在一起,创建一个正则表达式模式,然后查看每个模式与df1的行匹配的位置。我想有一个更简单的方法来做到这一点。
对于每行rowwise(),将所有id粘贴为“|”(regex或operator)在中间。然后,用str_remove_all("NA\\||\\|NA")删除额外的NA

library(tidyverse)

df2_ids <- df2 %>%
  rowwise() %>% #for each row
  mutate(ids = across() %>% paste(collapse = "|") %>% str_remove_all("NA\\||\\|NA")) %>% 
  pull(ids)

df2_ids 
#> [1] "1|98|1|98" "2|9|2|9" "3|66|3|66" "4|16|4|16" "8|24|44|17|8|24|44|17"

然后,对于每个模式,获取df1的哪一行与之匹配。如果没有匹配,which(...)的长度将是0,我们保存一个NA

df2$id <- df1$id[map_dbl(df2_ids, ~ which(grepl(.x, df1$id)) %>% {ifelse(length(.) >0,  ., NA)})]

现在,我们有了一个合适的连接键,可以做一个简单的left_join(保留df1的所有行):

left_join(df1, df2, by = 'id')

这在df2更复杂的情况下失效,其中一行引用多个df1行。下面是一个更一般的方法。

index <- map(df2_ids, ~ which(grepl(.x, df1$id)) %>% {`if`(length(.) >0,  ., NA)})

df2 <- df2 %>%
  uncount(map_dbl(index, length)) %>%
  mutate(id = df1$id[reduce(index, c)])

left_join(df1, df2)
deikduxw

deikduxw3#

i <- rowSums(array(match(unlist(df2), df1$id, 0L), dim(df2)))
i[duplicated(i) | !i] <- nrow(df2) + 1L
cbind(df1, rbind(df2, NA)[i,])
#>   id score id1 id2 id3 id4
#> 1 11    20  11  98  NA  NA
#> 2 22    22  22   9  NA  NA
#> 3 33    33  NA  33  66  NA
#> 4 44    22  NA  NA  44  16
#> 6 55    11  NA  NA  NA  NA

相关问题