【问题标题】:Merging two Dataframes in R by ID, One is the subset of the other按ID合并R中的两个Dataframe,一个是另一个的子集
【发布时间】:2017-10-18 07:16:44
【问题描述】:

我在 R 中有 2 个数据框:带有 175 个变量的“dfold”和带有 75 个变量的“dfnew”。 2 个数据帧由主键(即“pid”)匹配。 dfnew 是 dfold 的子集,因此 dfnew 中的所有变量也在 dfold 上,但具有更新的估算值(不再有 NA)。同时 dfold 有更多的变量,我在分析阶段需要它们。我想合并 dfmerge 中的 2 个数据框,以便从 dfnew --> dfold 更新公共变量,但同时在 dfold 中保留预先存在的变量。我已经尝试过 merge()、match()、dplyr 和 sqldf 包,但要么我获得了一个仅包含更新的 75 个变量(左连接)的 dfmerge,要么获得了一个包含 250 个变量的 dfmerge(带有 NA 的旧变量和没有它们的新变量共存)。我发现(here)的唯一方法是一个优雅但相当长(10 行)的循环,它在 pid 与 all.x = TRUE 选项合并后消除 *.x 变量)。如果可以的话,您能否建议一种更有效的方法来获得这样的结果?

提前谢谢你

P.S:为了让事情更简单,我创建了 dfold 和 dfnew 的最小版本:dfnew 现在有 3 个变量,没有 NA,而 dfold 有 5 个变量,包括 NA。这是数据帧结构

折叠:

structure(list(Country = structure(c(1L, 3L, 2L, 3L, 2L), .Label = c("France", 
"Germany", "Spain"), class = "factor"), Age = c(44L, 27L, 30L, 
38L, 40L), Salary = c(72000L, 48000L, 54000L, 61000L, NA), Purchased = structure(c(1L, 
2L, 1L, 1L, 2L), .Label = c("No", "Yes"), class = "factor"), 
    pid = 1:5), .Names = c("Country", "Age", "Salary", "Purchased", 
"pid"), row.names = c(NA, 5L), class = "data.frame")

dfnew:

structure(list(Age = c(44, 27, 30), Salary = c(72000, 48000, 
54000), pid = c(1, 2, 3)), .Names = c("Age", "Salary", "pid"), row.names = c(NA, 
3L), class = "data.frame")

虽然这里的问题仅限于 2 个变量 请注意,实际场景将涉及 75 个变量。

【问题讨论】:

  • 你能提供一些有意义的样本数据吗?在 dfold-subsample 中可能每个数据框有 10 行,其中包含 5 个公共变量和另外 5 个其他变量?
  • 肯定会很乐意这样做,但不确定如何在此处附加数据,有什么提示吗?
  • 最好的方法是在子集上使用dput() 命令,如上所述,类似于dput(dfold[1:10, 1:10])dput(dfnew[1:10, 1:5])(您可能需要更改列索引以获得5常见和 5 个不常见的变量)。然后将R 控制台的输出(通常以structure(... 开头)粘贴到您的问题文本中。
  • 好的,在原帖中编辑,谢谢 :) 我刚刚让事情变得更容易,但 dfold 和 dfnew 应该仍然有效
  • 我将自己重新创建它,但最佳情况下dfnew 应该包括所有 5 行,尤其是在 Salary 列中具有 NA 值的行。

标签: r dataframe merge


【解决方案1】:

好的,此解决方案假定您实际上并不需要合并,而只想使用 dfnew 中的估算值更新您的 dfold 中的 NA 值。

> dfold
  Country Age Salary Purchased pid
1  France  NA  72000        No   1
2   Spain  27  48000       Yes   2
3 Germany  30  54000        No   3
4   Spain  38  61000        No   4
5 Germany  40     NA       Yes   5

> dfnew
  Age Salary pid
1  44  72000   1
2  27  48000   2
3  30  54000   3
4  38  61000   4
5  40  70000   5

要对单个列执行此操作,请尝试

dfold$Salary <- ifelse(is.na(dfold$Salary), dfnew$Salary[dfnew$pid == dfold$pid], dfold$Salary)

> dfold
  Country Age Salary Purchased pid
1  France  NA  72000        No   1
2   Spain  27  48000       Yes   2
3 Germany  30  54000        No   3
4   Spain  38  61000        No   4
5 Germany  40  70000       Yes   5

在整个数据集上使用它有点棘手:

首先定义除pid之外的所有常用列名:

cols <- names(dfnew)[names(dfnew) != "pid"]

> cols
[1] "Age"    "Salary"

现在使用mapplyNA 值替换为ifelse

dfold[,cols] <- mapply(function(x, y) ifelse(is.na(x), y[dfnew$pid == dfold$pid], x), dfold[,cols], dfnew[,cols])

> dfold
  Country Age Salary Purchased pid
1  France  44  72000        No   1
2   Spain  27  48000       Yes   2
3 Germany  30  54000        No   3
4   Spain  38  61000        No   4
5 Germany  40  70000       Yes   5

这假定dfnew 仅包括dfold 中存在的列。如果不是这种情况,请使用

cols <- names(dfnew)[which(names(dfnew) %in% names(dfold))][names(dfnew) != "pid"]

【讨论】:

  • 伟大而优雅的解决方案 LAP,刚刚在整个真实数据集上进行了测试,并且工作得像冠军:)
  • 只是为了适当地结束主题并供将来参考:您认为不能使用合并或 JOIN 查询执行相同的操作?
  • 参考 Stackoverflow 上的不同问题后,我很确定这不能通过简单的合并或连接来完成。您要么需要事先定义要覆盖的单元格,要么删除之后不必要地创建的.x/.y 变量。参考this thread
  • 完美,事实上我在原始帖子中提到的循环就是这样:删除合并后创建的 .x(不必要的)。再次感谢您花费的时间和支持。
猜你喜欢
  • 1970-01-01
  • 2019-09-20
  • 1970-01-01
  • 2015-04-09
  • 2022-07-26
  • 2018-04-11
  • 1970-01-01
  • 2019-07-05
  • 2021-04-05
相关资源
最近更新 更多