【问题标题】:How to update values from another table with conditions如何使用条件更新另一个表中的值
【发布时间】:2019-08-30 09:38:05
【问题描述】:

我想用 df2 中的值更新表 df1 中的值,只更新空值或零。 我可以用 data.table 或 dplyr 做到这一点,但我不能自动化所有列。

#data.table
df1 <- data.frame(x1=1:4, x2=c('a','b', NA, 'd'), x3=c(0,0,2,2), stringsAsFactors=FALSE)
df2 <- data.frame(x1=2:3, x2=c("zz", "qq"),x3=6:7, stringsAsFactors=FALSE)

require(data.table)
setDT(df1); setDT(df2)

df1[df2, on = .(x1), x2 := ifelse(is.na(x2) | x2 == 0 ,i.x2,x2)]

#dplyr
require(dplyr)
require(dplyr)
inner_join(df1,df2,by = c("x1" = "x1")) %>% 
  transmute(x1 = x1,
            x2 =ifelse(is.na(x2.x) | x2.x == 0,x2.y,x2.x),
            x3 =ifelse(is.na(x3.x) | x3.x == 0,x3.y,x3.x))

使用 dplyr 至少我可以手动添加列以获得预期的输出,问题是真实的数据框有这么多列。因此,我想遍历列来完成任务。

我尝试过的:

# dplyr + apply
inner_join(df1,df2,by = c("x1" = "x1")) %>% 
  cbind(.$x1, 
        apply(.[-1],2, function(cname) ifelse(is.na(cname) | cname == 'b',paste(cname, ".x", collapse = ""),paste(cname, ".y", collapse = "")))
  )

# data.table with for
for (cname in names(df1)[!names(df1) %in% c("x1")]) {
  df1[i = df2, on = .(x1), j = cname := {function (x) ifelse(is.na(x) | x == 'b',i.x,x)} (cname)
        , with = FALSE]
} 

# data.table + lapply
df1[i = df2, on = .(x1)  ,names(df1)[!names(df1) %in% c("x1")] := lapply(df1[,names(df1)[!names(df1) %in% c("x1")],with=FALSE],
                           function(x) ifelse(is.na(x) | x == 0,df2.x,df1.x))]

【问题讨论】:

  • 如果您共享预期输出会很好。如果无法更新,第 1 行是否应该保持为 0?
  • @sindri_baldur 我猜预期的输出是inner_join(df1,df2,by = c("x1" = "x1")) %&gt;% transmute(x1 = x1, x2 =ifelse(is.na(x2.x) | x2.x == 0,x2.y,x2.x), x3 =ifelse(is.na(x3.x) | x3.x == 0,x3.y,x3.x)),而无需为所有列组合手动执行。

标签: r dplyr data.table


【解决方案1】:

使用base R,您可以创建一个函数,将NA 和0 替换为另一列中的对应值

replace_na_0 <- function(x) {
   ifelse(is.na(x[[1]]) | x[[1]] == 0,x[[2]],x[[1]])
}

通过删除列的后缀(.x.y)replace_na_0 函数来合并和传递列组

temp_df <- merge(df1, df2, by = "x1")

cbind(temp_df[1], sapply(split.default(temp_df[-1], 
       sub("\\..*", "", names(temp_df)[-1])), replace_na_0))

#  x1 x2 x3
#1  2  b  6
#2  3 qq  2

【讨论】:

    【解决方案2】:

    对于data.table,您可以使用:

    for (x in setdiff(names(df1), "x1")) {
        df1[is.na(get(x)) ! get(x)==0, (x) := df2[.SD, on=.(x1), get(x)]]
    }
    

    【讨论】:

      【解决方案3】:

      这是一个纯粹的data.table 方法...

      熔化过程会处理您希望填充的所有列,将它们全部放在一组列(变量和值)中。 然后使用更新连接填写所有 0/NA 值(=fast!) 最后,将所有东西重铸回原来的形状。

      library(data.table)
      #set to data.table
      setDT(df1)
      setDT(df2)
      #melt to long
      melt1 <- melt(df1, id.vars = "x1" )
      melt2 <- melt(df2, id.vars = "x1" )
      #join all values with value NA or 0
      melt1[ is.na(value) | value == 0, 
             value := melt1[ is.na( value) | value == 0,][ melt2, value := i.value, on = .(x1, variable) ]$value][]
      #cast to original wide format
      dcast( melt1, x1 ~ variable )
      

      输出

      #    x1 x2 x3
      # 1:  1  a  0
      # 2:  2  b  6
      # 3:  3 qq  2
      # 4:  4  d  2
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多