【问题标题】:How to merge two data frames based on similar values in R如何基于R中的相似值合并两个数据框
【发布时间】:2017-01-04 20:32:02
【问题描述】:

我在 R 方面相对较新,我有一个关于合并两个数据帧的问题,这两个数据帧确实包含来自两个域(mz 和 rt)但不相同的相似数字数据。 这是一个描述我的问题的示例:

mz1    <- c(seq(100, 190, by = 10))
rt1    <- c(seq(1, 10, by = 1))
value1 <- runif(10, min = 100, max = 100000)
mz2    <- mz1 + runif(10, -0.1, 0.1)
rt2    <- rt1 + runif(10, -0.2, 0.2)
value2 <- runif(10, min = 100, max = 100000)

df1 <- as.data.frame(cbind(mz1, rt1, value1))
df2 <- as.data.frame(cbind(mz2, rt2, value2))


df1
   mz1 rt1    value1
1  100   1 44605.646
2  110   2 13924.598
3  120   3 35727.265
4  130   4 75175.652
5  140   5 25221.724
6  150   6 29080.653
7  160   7  3170.749
8  170   8 10184.708
9  180   9 48055.072
10 190  10 77644.865


df2
        mz2      rt2   value2
1  100.0243 1.043092 58099.49
2  110.0514 2.164753 76397.67
3  120.0258 2.838141 43901.05
4  130.0921 4.044322 34543.96
5  139.9577 5.023823 53086.10
6  150.0170 6.061794 13929.27
7  160.0884 6.828779 60905.61
8  170.0440 7.932000 66627.20
9  180.0872 9.116425 44587.62
10 189.9694 9.834091 51186.03

我想合并来自 df1 和 df2 的所有行,它们在 rt 域 在 mz 域中具有

到目前为止我所尝试的:

merge.data.frame(df1, df2, by.x = c("mz1", "rt1"), by.y = c("mz2", "rt2") , all = T)

        mz1 rt1    value1      rt2   value2
1  100.0000   1 44605.646       NA       NA
2  100.0243  NA        NA 1.043092 58099.49
3  110.0000   2 13924.598       NA       NA
4  110.0514  NA        NA 2.164753 76397.67
5  120.0000   3 35727.265       NA       NA
6  120.0258  NA        NA 2.838141 43901.05
7  130.0000   4 75175.652       NA       NA
8  130.0921  NA        NA 4.044322 34543.96
9  139.9577  NA        NA 5.023823 53086.10
10 140.0000   5 25221.724       NA       NA
11 150.0000   6 29080.653       NA       NA
12 150.0170  NA        NA 6.061794 13929.27
13 160.0000   7  3170.749       NA       NA
14 160.0884  NA        NA 6.828779 60905.61
15 170.0000   8 10184.708       NA       NA
16 170.0440  NA        NA 7.932000 66627.20
17 180.0000   9 48055.072       NA       NA
18 180.0872  NA        NA 9.116425 44587.62
19 189.9694  NA        NA 9.834091 51186.03
20 190.0000  10 77644.865       NA       NA

这至少给了我一个正确格式的数据框,其中包含无法合并的 NA。

如果有人能帮我解决这个问题,那就太棒了!

问候

更新

好的,我会记住的。到目前为止谢谢。我尝试了以下想法:

#select data in joined which has no partner
no_match_df1 <- anti_join(joined, df2)
no_match_df1 <- no_match_df1[1:3]

#select data in df2 which has been excluded due to duplication
collist <- c("mz2", "rt2", "value2")
dublicates <- joined[complete.cases(joined[collist]), collist]
dublicates <- anti_join(df2, dublicates)


#repetition for joining
joined2 <- fuzzy_join(no_match_df1, dublicates, multi_by = c("mz1" = "mz2", "rt1" = "rt2"),
                     multi_match_fun = mmf, mode = "full")

joined2 <- group_by(joined2, mz1, rt1) %>%
  mutate(min_dist = min(dist))
head(joined2)

joined2 <- filter(joined2, dist == min_dist | is.na(dist)) %>%
  select(-dist, -min_dist)
head(joined2)

#select only rows with new match or where dublicates coulnd't find a partner

add <- subset(joined2, !is.na(joined2$mz2) | !is.na(joined2$mz2) &  !is.na(joined2$mz1))

#add to joined
##I need some help here, how can I update the existing joined data frame?

Maybe this helps

也许我们可以像以前那样将no_match_df1duplicates 连接起来,然后通过覆盖现有joined 数据框中的特定行来添加结果。 最后,我们必须重复该过程作为日志,因为duplicates 的长度>1。

【问题讨论】:

  • 您可以尝试查看包 fuzzyjoin
  • 我也从我的同事那里遇到过这样的案例,他们曾经嘲笑在 SAS 中实现这种合并的方式很容易。
  • @Marco,如果您需要更新您的问题,因为答案以某种方式帮助了您 edit 您自己的问题,而不是帮助您的答案。
  • 好吧,对不起我的错误。我是新来的,这实际上是一次错误的点击。

标签: r dataframe merge


【解决方案1】:

按照 joran 的建议,我找到了使用 fuzzyjoin 包的解决方案。我创建的数据集如下:

set.seed(123)
mz1    <- c(seq(100, 190, by = 10))
rt1    <- c(seq(1, 10, by = 1))
value1 <- runif(10, min = 100, max = 100000)
mz2    <- mz1 + runif(10, -0.1, 0.1)
rt2    <- rt1 + runif(10, -0.2, 0.2)
value2 <- runif(10, min = 100, max = 100000)

df1 <- as.data.frame(cbind(mz1, rt1, value1))
df2 <- as.data.frame(cbind(mz2, rt2, value2))

(一点旁白:你做了一个很好的可重复的例子。唯一的缺点是你没有设置种子,这是上面与你的代码的唯一区别。)

为了确保存在找到两个匹配项的情况,我在df2 中添加了一行:

df2 <- rbind(df2, c(180.001, 9.09, 0))

现在,我可以使用函数fuzzy_join() 来合并数据框:

library(fuzzyjoin)
joined <- fuzzy_join(df1, df2, multi_by = c("mz1" = "mz2", "rt1" = "rt2"),
                     multi_match_fun = mmf, mode = "full")

请注意,语法与dplyr 中的join() 非常相似。但是有一个关键的区别:您可以为multi_match_fun 提供一个函数,该函数确定两行是否匹配。它返回一个数据框,其中第一列必须是逻辑的。此列确定两行是否匹配。所有其他列都简单地添加到结果数据框中。我将这个函数定义如下:

mmf <- function(x, y) {
  mz_dist <- abs(x[, 1] - y[, 1])
  rt_dist <- abs(x[, 2] - y[, 2])

  out <- data_frame(merge = rt_dist <= 0.1 & mz_dist < 0.05,
                    dist = sqrt(mz_dist^2 + rt_dist^2))
  return (out)
}

如果满足您指定的条件,您可以看到列merge(名称是任意的)是TRUE。此外,还会添加一个包含距离的列以供以后使用。我设置mode = "full" 是为了获得NA 值,如果没有匹配。

结果如下:

head(joined)
##   mz1 rt1   value1      mz2      rt2   value2       dist
## 1 110   2 78851.68 109.9907 2.077121 90239.67 0.07768406
## 2 120   3 40956.79 120.0355 3.056203 69101.46 0.06648308
## 3 180   9 55188.36 179.9656 8.915664 31886.28 0.09108803
## 4 180   9 55188.36 180.0010 9.090000     0.00 0.09000556
## 5 100   1 28828.99       NA       NA       NA         NA
## 6 130   4 88313.44       NA       NA       NA         NA

在第 3 行和第 4 行中,您可以看到在这种情况下确实有两个匹配项。从dist 列中,您可以看到第 4 行是我们要保留的行。这意味着第 3 行应被视为未找到匹配项,并且 mz1rt1value1 列应使用 NA 填充。我通过按mz1rt1 对行进行分组,然后为每个组添加距离的最小值来做到这一点:

library(dplyr)
joined <- group_by(joined, mz1, rt1) %>%
            mutate(min_dist = min(dist))
head(joined)
## Source: local data frame [6 x 8]
## Groups: mz1, rt1 [5]
## 
##     mz1   rt1   value1      mz2      rt2   value2       dist   min_dist
##   <dbl> <dbl>    <dbl>    <dbl>    <dbl>    <dbl>      <dbl>      <dbl>
## 1   110     2 78851.68 109.9907 2.077121 90239.67 0.07768406 0.07768406
## 2   120     3 40956.79 120.0355 3.056203 69101.46 0.06648308 0.06648308
## 3   180     9 55188.36 179.9656 8.915664 31886.28 0.09108803 0.09000556
## 4   180     9 55188.36 180.0010 9.090000     0.00 0.09000556 0.09000556
## 5   100     1 28828.99       NA       NA       NA         NA         NA
## 6   130     4 88313.44       NA       NA       NA         NA         NA

有效匹配的行是所有那些,其中distmin_dist 相同。此外,我们也不应该丢失distNA 的行。这可以按如下方式完成:

dbls <- which(joined$dist != joined$min_dist)
joined[dbls, c("mz1", "rt1", "value1")] <- NA
joined <- select(joined, -dist, -min_dist)
head(joined)
## Source: local data frame [6 x 6]
## Groups: mz1, rt1 [6]
## 
##     mz1   rt1   value1      mz2      rt2   value2
##   <dbl> <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
## 1   110     2 78851.68 109.9907 2.077121 90239.67
## 2   120     3 40956.79 120.0355 3.056203 69101.46
## 3    NA    NA       NA 179.9656 8.915664 31886.28
## 4   180     9 55188.36 180.0010 9.090000     0.00
## 5   100     1 28828.99       NA       NA       NA
## 6   130     4 88313.44       NA       NA       NA

根据您的数据的外观,也有可能在双重匹配的情况下,mz1rt1 的值不一致,但另一对值一致。然后,您还必须对其他分组重复上述步骤。

【讨论】:

  • 哇,我真的很高兴收到你们的所有帮助!
  • 这正是我想要的。只缺少一件事,只保留了 df2 中的第 4 行。首先这是正确的决定,但我不想丢失 df2 中第 3 行的信息。例如,从 df2 获取数据,它首先找到了一个合作伙伴,但由于双重匹配和距离太远而被拒绝。该数据应该有第二次机会在 df1 中找到具有相同标准的合作伙伴(尚未有合作伙伴)。只要有双打就重复。
  • @Marco 你是对的:你想要NA 值不匹配的行,因此,我不应该简单地扔掉双打。由于我现在没有时间,我会稍后更新我的答案以解决这个问题。但是请注意,虽然在简单示例中很容易做到,但在更复杂的情况下事情可能会变得更加复杂。
  • 谢谢,我已经用一个想法更新了我的帖子。我不确定论坛中的一切是如何运作的,以及您是否已经可以看到它。
  • @Marco 我已按照承诺更新了我的答案。在您的编辑中,您提出了一种不同的方法来解决问题。虽然这种方法可能会带来一个完美的解决方案,但不幸的是,您的编辑改变了问题:您现在不是询问如何在某些条件下进行合并,而是询问在解决部分问题后应该如何继续。收到答案后更改问题不是一个好主意,因为这会使您已经获得的答案无效。如果您有一个新问题,那么您应该提出一个新问题。
猜你喜欢
  • 2016-05-24
  • 2023-04-01
  • 1970-01-01
  • 2019-05-01
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多