用R中的重复键匹配两个数据帧

Matching two data frames with repeated keys in R

我想根据两个数据框的 ID 来匹配它们,并将重复的选择附加到一个列表中。我拥有的数据框示例如下:

df1 = data.frame(ID = c("A", "B", "C", "D"), count = c(4, 6, 7, 8))
df2 = data.frame(ID = c("A", "B", "C", "D"), direction = c("up", "down", "up", "up"), reference = c(38, 39, 40, 40))

两个数据框看起来像这样:

ID count
A 4
B 6
C 7
D 8
ID direction reference
A up 38
A down 39
C up 40
D up 40

基本上,我希望最终结果看起来像这样,其中来自 df2 的信息可以与 df1 合并,但详细信息连接到一个列表中,如果在 df2 中找到多个对应条目,则可以存储它们作为列表列表:

ID count Info
A 4 c(up = 38, down = 39)
B 6 NA
C 7 c(up =40)
D 8 c(up =40)

我曾尝试合并两个数据框,但这会导致多个条目作为新行重复出现。我正在考虑像我之前对数据库环境所做的那样尝试通过第二个数据框进行匹配解析,但如果您能让我知道如何使用数据框而不是环境来执行此操作,我将不胜感激。当然,如果有更有效的方法来解决这个问题,那也将不胜感激!我为将 df1 匹配到数据库环境所做的示例:

i=1
for (row in 1:nrow(df)){
  tmp <- paste(df$ID[i])
  tmp2 <- as.list(mget(tmp, mirbaseID2ACC, ifnotfound = NA))
  if (is.na(tmp2) == TRUE) {
    tmp <- paste(df$simpleID[i])
    tmp2 <- as.list(mget(tmp, mirbaseID2ACC, ifnotfound = NA))
  }
  if (i<(nrow(df)+1)) {
    df$ACCESSION[i] <- tmp2
    i=i+1
  }
}

*请注意,“简单”列用于实际数据框,因为样本具有非常具体的 ID(即 A-ab-2),但出于本示例的目的,我们可以假设 ID本身很简单,可以用我正在尝试的数据框检测到

您可以 group_by ID 列,然后 summarise directionreference 列到列表中(使用 df3$info查看列表)和 left_joindf1.

library(dplyr)

df3 <- left_join(df1, 
          df2 %>% 
            group_by(ID) %>% 
            summarize(info = list(paste(direction, "=", reference))), 
          by = "ID")

  ID count               info
1  A     4 up = 38, down = 39
2  B     6               NULL
3  C     7            up = 40
4  D     8            up = 40

df3$info
[[1]]
[1] "up = 38"   "down = 39"

[[2]]
NULL

[[3]]
[1] "up = 40"

[[4]]
[1] "up = 40"

outer 中使用 `==` 然后 apply which 替代只匹配一次的 match。然后在找到的匹配上子集 df2,并使用 pastetoString.

拼凑在一起
r1 <- cbind(df1, info=sapply(apply(outer(df1$ID, df2$ID, `==`), 1, which), \(x) {
  if (!length(x) == 0)
    toString(Reduce(\(y, z) paste0(y, '=', z), df2[x, -1]))
  else NA
}))
r1
#   ID count           info
# 1  A     4 up=38, down=39
# 2  B     6           <NA>
# 3  C     7          up=40
# 4  D     8          up=40

或者,获取所需的列表列:

r2 <- cbind(df1, info=I(lapply(apply(outer(df1$ID, df2$ID, `==`), 1, which), \(x) {
  if (!length(x) == 0)
    Reduce(\(y, z) paste0(y, '=', z), df2[x, -1])
  else NA
})))
r2
#   ID count         info
# 1  A     4 up=38, d....
# 2  B     6           NA
# 3  C     7        up=40
# 4  D     8        up=40

哪里

r2$info
# [[1]]
# [1] "up=38"   "down=39"
# 
# [[2]]
# [1] NA
# 
# [[3]]
# [1] "up=40"
# 
# [[4]]
# [1] "up=40"

注意: R >= 4.1 使用。


数据:

df1 <- structure(list(ID = c("A", "B", "C", "D"), count = c(4, 6, 7, 
8)), class = "data.frame", row.names = c(NA, -4L))

df2 <- structure(list(ID = c("A", "A", "C", "D"), direction = c("up", 
"down", "up", "up"), reference = c(38, 39, 40, 40)), class = "data.frame", row.names = c(NA, 
-4L))

使用 merge + aggregate

的基础 R 选项
aggregate(
    reference ~ ID + count,
    merge(df1, df2, all = TRUE),
    c,
    na.action = na.pass
)

给予

  ID count reference
1  A     4    38, 39
2  B     6        NA
3  C     7        40
4  D     8        40