使用一个data.frame更新另一个

SFu*_*n28 14 indexing r dataframe

给定2个在列名/数据类型方面相同的数据帧,其中一些列唯一地标识行,是否有一个有效的函数/方法用于一个data.frame来"更新"另一个?

例如,在下文中,originalreplacement通过识别'Name''Id'. goal为找到的所有行的结果replacementoriginal(由唯一的ID),并与替换Value1Value2

original = data.frame( Name = c("joe","john") , Id = c( 1 , 2) , Value1 = c(1.2,NA), Value2 = c(NA,9.2) )
replacement = data.frame( Name = c("john") , Id = 2 , Value1 = 2.2 , value2 = 5.9)
goal = data.frame( Name = c("joe","john") , Id = c( 1 , 2) , Value1 = c(1.2,2.2), Value2 = c(NA,5.9) )
Run Code Online (Sandbox Code Playgroud)

该解决方案应为工作originalreplacement任意长度的(尽管replacement不应该有超过行original).在实践中,我使用2个id列.

Ryo*_*ogi 11

我会用data.table对象.此代码似乎适用于您的示例:

library(data.table)

# set keys
original.dt <- data.table(original, key=c("Name", "Id"))        
replacement.dt <- data.table(replacement, key=c("Name", "Id"))

goal2 <- original.dt
# subset and reassign
# goal2[replacement.dt[, list(Name, Id)]] <- replacement.dt
goal2[replacement.dt] <- replacement.dt  # cleaner and faster, see Matthew's comment

goal2 <- as.data.frame(goal2)

identical(goal, goal2) # FALSE, why? See Joris's comment
all.equal(goal, goal2) # TRUE
Run Code Online (Sandbox Code Playgroud)

  • 小改进:可以删除`[,list(Name,Id)]位.只是`goal2 [replacement.dt] < - replacement.dt`更短更快,因为`replacement.dt`的键已经是``Name,Id"`.`i`不一定要被键入,但是当它被连接时,它是连接的`i`键列,并且内部使用更快的合并算法,利用两个表都被排序的事实. (3认同)

Joh*_*lby 7

只需将唯一ID设置为行名称即可.然后它是简单的索引:

rownames(original) = original$Id
rownames(replacement) = replacement$Id

original[rownames(replacement), ] = replacement
Run Code Online (Sandbox Code Playgroud)


Jor*_*eys 6

使用base R,您可以使用replace.df()下面的函数,该函数大致基于的源代码merge.data.frame()。与其他解决方案相反,此解决方案允许使用多个列进行标识。我在工作中经常使用它。随时复制和使用。

此函数控制在x中找不到y中的行的情况。请注意,该函数不会检查组合是否唯一。match()将仅用组合的首次出现替换第一次出现。

该函数的用法如下:

> replace.df(original, replacement,by=c('Name','Id'))
  Name Id Value1 Value2
1  joe  1    1.2     NA
2 john  2    2.2    9.2
Run Code Online (Sandbox Code Playgroud)

请注意,这可以有效检测原始代码中的写入错误。replacement包含一个名为“ value2”(小v)的变量,而不是Value2(大写V)。纠正此问题后,结果将变为:

> replace.df(original, replacement,by=c('Name','Id'))
  Name Id Value1 Value2
1  joe  1    1.2     NA
2 john  2    2.2    5.9
Run Code Online (Sandbox Code Playgroud)

您也可以使用该函数来仅更改某些列中的值

> replace.df(original, replacement,by=c('Name','Id'),cols='Value2')
  Name Id Value1 Value2
1  joe  1    1.2     NA
2 john  2     NA    5.9
Run Code Online (Sandbox Code Playgroud)

功能:

replace.df <- function(x,y,by,cols=NULL
           ){
    nx <- nrow(x)
    ny <- nrow(y)

    bx <- x[,by,drop=FALSE]
    by <- y[,by,drop=FALSE]
    bz <- do.call("paste", c(rbind(bx, by), sep = "\r"))

    bx <- bz[seq_len(nx)]
    by <- bz[nx + seq_len(ny)]

    idx <- match(by,bx)
    idy <- match(bx,by)
    idy <- idy[!is.na(idy)]

    if(is.null(cols)) {
      cols <- intersect(names(x),names(y))
      cols <- cols[!cols %in% by]
    }

    x[idx,cols] <- y[idy,cols]
    x
  }
Run Code Online (Sandbox Code Playgroud)