我是 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 域中差异 <= 0.1 和 在 mz 域中差异 <= 0.05 的所有行。 此外,如果有两行或更多行满足此条件,则应合并到两个域的距离最小的行(可能需要进行额外计算:distance = sqrt(mz^2+rt^2)),其余行行必须找到不同的合并伙伴(如果存在)。 如果没有合并伙伴保留该行并将“NA”填入缺失值。
到目前为止我尝试了什么:
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?
也许我们可以像以前那样将 no_match_df1
与 duplicates
连接起来,然后通过覆盖现有 joined< 中的特定行来添加结果
数据框。
最后,我们必须重复该过程,因为 duplicates
的长度 >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 行应被视为未找到匹配项,mz1
、rt1
和 value1
列应填充 NA
。为此,我按 mz1
和 rt1
对行进行分组,然后为每组添加距离的最小值:
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
具有有效匹配的行是所有那些,其中dist
与min_dist
相同。此外,我们也不应丢失 dist
为 NA
的行。这可以按如下方式完成:
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
根据您的数据看起来,也有可能在双重匹配的情况下,mz1
和 rt1
的值不一致,但是另一对值可以。然后,您还必须对其他分组重复上述步骤。
关于r - 如何根据R中的相似值合并两个数据帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41472722/