使用merge()函数从第二个数据框更新一个数据框的值。

27

我正在尝试弄清如何使用merge()来更新数据框。

以数据框foo为例:

foo <- data.frame(index=c('a', 'b', 'c', 'd'), value=c(100, 101, NA, NA))

这个包含以下数值:

index value
1     a   100
2     b   101
3     c    NA
4     d    NA

还有数据框 bar

bar <- data.frame(index=c('c', 'd'), value=c(200, 201))

这个值包括:

 index value
1     c   200
2     d   201

当我运行下面的merge()函数来更新cd的值时:
merge(foo, bar, by='index', all=T)

它的输出结果如下:
 index value.x value.y
1     a     100      NA
2     b     101      NA
3     c      NA     200
4     d      NA     201

我希望merge()的输出避免在这个特定的例子中创建value.xvalue.y,只保留value的原始列。有简单的方法可以做到这一点吗?

如果没有空值,结果应该是什么? - gd047
3
你曾经得到这个问题的答案吗?我正在寻找解决这个相同问题的方案。 - dataanalyst
2
我也不明白为什么合并操作(merge)没有一个 overwrite=TRUE 参数,当提供了“by”参数时自动启用。每次想要重新运行合并操作时手动删除列非常麻烦。 - Valentas
我刚遇到了同样的问题,我认为对你的问题最直接的答案是下面@jangorecki的回答,你应该接受。 - Jakob
7个回答

16

使用 data.table 的最优解决方案

library(data.table)
setDT(foo)
setDT(bar)
foo[bar, on="index", value:=i.value]
foo
#   index value
#1:     a   100
#2:     b   101
#3:     c   200
#4:     d   201

data.table 方法中,第一个参数被命名为 i,因此我们可以使用 i. 前缀在 i 参数中引用表中的列。


2
如果有多列需要更新,比如value1、value2等,你会怎么做? - harisf
这个 data.table 解决方案似乎是 VLOOKUP 的理想替代品。在 R 中合并或连接数据框不允许更新单个值,而这正是 Excel 中经常使用该函数的方式。 - user18008505
@harisf,我会这样做:foo[bar, on="index", `:=` (value1=i.value1, value2=i.value2)] - Valentin_Ștefan

12

merge()不总是将列绑定在一起吗?replace()能用吗?

foo$value <- replace(foo$value, foo$index %in% bar$index, bar$value)

或者使用match(),所以顺序很重要

foo$value[match(bar$index, foo$index)] <- bar$value

2
使用replace()方法的一个要点是,如果bar中的顺序与foo中的顺序不同,它将不能正常工作。例如,在bar <- bar[c(2,1),]之后尝试运行上面的例子,最终结果将不正确。 - andrewj
是的,match() 对于我的例子确实有效。实际上,我的实际用例更加复杂,我希望跨多个列进行匹配,而不仅仅是一个简单的向量。我认为当您想要在数据框的多个列中进行匹配时,match() 不起作用。 - andrewj
谢谢!使用match()的想法很好...但是,如果bar有另一个不包含在foo中的元素(我们想要更新并添加新内容) bar <- data.frame(index=c('c', 'd','e'), value=c(200, 201,215)) 那么当我们尝试使用match时,就会出现错误。 Error in foo$value[match(bar$index, foo$index)] <- bar$value : NAs are not allowed in subscripted assignments 有什么想法可以克服这个问题吗? - moldovean
1
如果您有多个索引列,该怎么办? - wolfsatthedoor

4
我想介绍一种使用sqldf库和R集成sqlite数据库的SQL解决方案。我喜欢SQL的简洁性、准确性和强大性。
准确性: 因为我可以精确地定义要更改的对象=行,而不必考虑数据框的顺序 (foo.id = bar.id)。
强大性: 在SET和WHERE之后的WHERE语句中 (第三行),我可以定义所有要考虑更新的条件。
简洁性: 与在向量、矩阵或数据框中使用索引相比,语法更易读。
library(sqldf)

# I changed index to id since index does not work. 
#   Obviously index is a key word in sqlite.

(foo <- data.frame(id=c('a', 'b', 'c', 'd'), value=c(100, 101, NA, NA)))
(bar <- data.frame(id=c('c', 'd'), value=c(200, 201)))

sqldf(c(paste("UPDATE foo"
             ," SET value = (SELECT bar.value FROM bar WHERE foo.id = bar.id)"
             ," WHERE value IS NULL"
             )
        , " SELECT * FROM main.foo"
    )
)

这提供了

  id value
1  a   100
2  b   101
3  c   200
4  d   201

类似问题:
R中等价于SQL update的方法?
R sqlite:使用两个表进行update操作出现语法错误


3
SQL语句可以跨多行运行,因此不需要使用“paste”命令。 - G. Grothendieck

0

merge() 只合并新数据。例如,如果您有一组关于几个城市平均收入的数据集和另一个有这些城市人口的独立数据集,则可以使用 merge() 将其中一个数据集合并到另一个数据集中。

像 apeescape 所说的那样,replace() 可能是您想要的。


0

我认为最简单的方法是在合并之前“标记”需要更新的值。

bar$update <- TRUE
foo <- merge(foo, bar, by='index', all=T, suffixes=c("",".update"))
foo[!is.na(foo$update),]$value <- foo[!is.na(foo$update),]$value.update
foo$value.update <- NULL
foo$update <- NULL

使用"data.table"会更快

library(data.table)
foo <- as.data.table(foo)
bar <- as.data.table(bar)
bar[, update:=TRUE]
foo <- merge(foo, bar, by='index', all=T, suffixes=c("",".update"))
foo[!is.na(update),value:=value.update]
foo[, c("value.update","update"):=NULL]
foo

   index value
1:     a   100
2:     b   101
3:     c   200
4:     d   201

0

另一种方法可能是:

  1. 从第一个数据框中删除NAs

  2. 使用rbind来追加数据而不是使用merge:

这些是原始的两个数据框:

foo <- data.frame(index=c('a', 'b', 'c', 'd'), value=c(100, 101, NA, NA))
bar <- data.frame(index=c('c', 'd'), value=c(200, 201))

(1)使用is.na的否定形式来删除NAs:

foo_new <- foo[!is.na(foo$value),]

(2) 绑定数据框,你就会得到你要找的答案。
new_df <- rbind(foo_new,bar)

            new_df
            index value
            1     a   100
            2     b   101
            3     c   200
            4     d   201

0

我曾经遇到过类似的问题,但这仍然是一个特定的情况,与我的情况相比。我有两个数据框,左边的是主数据框(就像您的情况中的foo),右边的是带有更新值的小数据框(就像您的情况中的bar)。现在,我需要从右侧数据框中更新左侧数据框中的值,只更新不同的值。这与您的问题有些相似,但人们已经针对缺失值回答了它。

为了更通用的解决方案,适用于具有多行和多列非NA值的情况。

foo <- data.frame(index=c('a', 'b', 'c', 'd'), value=c(100, 101, NA, NA))
bar <- data.frame(index=c('c', 'd'), value=c(200, 201))

ModifiedIndexs <-
  foo %>% 
  full_join(bar) %>% 
  group_by(index) %>% 
  summarise(count = n()) %>% 
  filter(count > 1) %>% 
  pull(index)

UpdatedDF <- foo
UpdatedDF[which(UpdatedDF$index %in% ModifiedIndexs),] <- bar

继续编码!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接