【问题标题】:How to merge two data frames on common columns in R with sum of others?如何将R中公共列上的两个数据框与其他数据框合并?
【发布时间】:2011-04-24 07:37:43
【问题描述】:

Windows 7 上的 R 版本 2.11.1 32 位

我有两个数据集:data_A 和 data_B:

数据_A

USER_A USER_B ACTION
1      11     0.3
1      13     0.25
1      16     0.63
1      17     0.26
2      11     0.14
2      14     0.28

数据_B

USER_A USER_B ACTION
1      13     0.17
1      14     0.27
2      11     0.25

现在我想将 data_B 的 ACTION 添加到 data_A 如果它们的 USER_A 和 USER_B 相等。如上例,结果为:

数据_A

USER_A USER_B ACTION
1      11     0.3
1      13     0.25+0.17
1      16     0.63
1      17     0.26
2      11     0.14+0.25
2      14     0.28

那么我该如何实现呢?

【问题讨论】:

  • 通常情况下,我会为此使用 awk 或 perl。这样的解决方案可以吗?
  • 虽然上面链接的问题较新,但它有更多更好、更现代的方法。

标签: r merge plyr


【解决方案1】:

您可以在包plyr 中使用ddply 并将其与merge 结合使用:

library(plyr)
ddply(merge(data_A, data_B, all.x=TRUE), 
  .(USER_A, USER_B), summarise, ACTION=sum(ACTION))

注意merge 是使用参数all.x=TRUE 调用的——这将返回第一个data.frame 中传递给merge 的所有值,即data_A:

  USER_A USER_B ACTION
1      1     11   0.30
2      1     13   0.25
3      1     16   0.63
4      1     17   0.26
5      2     11   0.14
6      2     14   0.28

【讨论】:

  • 该输出并不完全是 OP 所拥有的 - 请注意,您比 OP 想要的多一行。我们需要data_A,但需要更新两个ACTION 条目。您的答案的等效基数 R 将是:aggregate(ACTION ~ USER_B + USER_A, data = rbind(data_A, data_B), FUN = sum)[, c(2,1,3)],但我不考虑这个,因为它不是 data_A 的更新。
  • @GavinSimpson 感谢您发现这一点。我现在修改了代码以使用合并,而不是 rbind。
  • 在消耗 2Gb 内存后在两组 3M 行上快速死亡。
  • @ArtemOboturov 如果您想要更快的处理速度和更低的内存消耗,请尝试使用 data.table 包
  • 我错过了什么吗? OP 要求对 action 列的值求和,但这个答案没有?
【解决方案2】:

使用类似数据库的操作很容易做到这一点。这里我使用包sqldf 进行左(外)连接,然后总结生成的对象:

require(sqldf)
tmp <- sqldf("select * from data_A left join data_B using (USER_A, USER_B)")

这会导致:

> tmp
  USER_A USER_B ACTION ACTION
1      1     11   0.30     NA
2      1     13   0.25   0.17
3      1     16   0.63     NA
4      1     17   0.26     NA
5      2     11   0.14   0.25
6      2     14   0.28     NA

现在我们只需要将两个 ACTION 列相加:

data_C <- transform(data_A, ACTION = rowSums(tmp[, 3:4], na.rm = TRUE))

这给出了预期的结果:

> data_C
  USER_A USER_B ACTION
1      1     11   0.30
2      1     13   0.42
3      1     16   0.63
4      1     17   0.26
5      2     11   0.39
6      2     14   0.28

这可以使用标准 R 函数 merge 来完成:

> merge(data_A, data_B, by = c("USER_A","USER_B"), all.x = TRUE)
  USER_A USER_B ACTION.x ACTION.y
1      1     11     0.30       NA
2      1     13     0.25     0.17
3      1     16     0.63       NA
4      1     17     0.26       NA
5      2     11     0.14     0.25
6      2     14     0.28       NA

所以我们可以将上面的 sqldf() 调用替换为:

tmp <- merge(data_A, data_B, by = c("USER_A","USER_B"), all.x = TRUE)

而使用transform() 的第二行保持不变。

【讨论】:

  • 您可以简单地将这些添加到 SQL 查询中,然后就不需要 transform例如 SELECT A.USER_A, B.USER_B, A.ACTION + B.ACTION AS ACTION FROM data_A A INNER JOIN data_B B ON A.USER_A = B.USER_B
  • 这个答案有效,而我尝试了很多接受的答案却没有实现...+1
【解决方案3】:

我写了包safejoin,它非常简洁地解决了这个问题:

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
safe_left_join(data_A,data_B, by = c("USER_A", "USER_B"), 
               conflict = ~ .x+ ifelse(is.na(.y),0,.y))
#   USER_A USER_B ACTION
# 1      1     11   0.30
# 2      1     13   0.42
# 3      1     16   0.63
# 4      1     17   0.26
# 5      2     11   0.39
# 6      2     14   0.28

如果发生冲突,将使用提供给 conflict 参数的函数 在成对的冲突列上

【讨论】:

  • 我真的很喜欢这个不错的选择,谢谢。在无与伦比的“by”的情况下也可以很好地总结:~ifelse(is.na(.x),0,.x)+ifelse(is.na(.y),0,.y)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 2021-08-02
  • 2019-03-13
  • 2017-09-03
相关资源
最近更新 更多