R中的条件合并/替换

Posted

技术标签:

【中文标题】R中的条件合并/替换【英文标题】:Conditional merge/replacement in R 【发布时间】:2011-09-01 00:28:54 【问题描述】:

我有两个数据框:

df1
x1  x2
1   a
2   b
3   c
4   d

df2
x1  x2
2   zz
3   qq

我想根据 df1$x1 和 df2$x2 之间的条件匹配将 df1$x2 中的一些值替换为 df2$x2 中的值以产生:

df1
x1  x2
1   a
2   zz
3   qq
4   d

【问题讨论】:

【参考方案1】:

使用 match(),假设 df1 中的值是唯一的。

df1 <- data.frame(x1=1:4,x2=letters[1:4],stringsAsFactors=FALSE)
df2 <- data.frame(x1=2:3,x2=c("zz","qq"),stringsAsFactors=FALSE)

df1$x2[match(df2$x1,df1$x1)] <- df2$x2
> df1
  x1 x2
1  1  a
2  2 zz
3  3 qq
4  4  d

如果值不是唯一的,请使用:

for(id in 1:nrow(df2))
  df1$x2[df1$x1 %in% df2$x1[id]] <- df2$x2[id]

【讨论】:

不错。我用相反的论点编写了比赛,但不明白为什么它比我想象的要复杂。我也会添加我的答案,因为它可以帮助其他人思考更改匹配中的参数顺序如何使事情变得更容易或更难。 谢谢乔里斯。我正在使用“匹配”,但无法正常工作。 我添加了一个解决方案,在 df1 中的非唯一值的情况下表现更好。【参考方案2】:

Joris 回答的第一部分很好,但在 df1 中的非唯一值的情况下,逐行 for 循环将无法在大型 data.frames 上很好地扩展。

您可以使用data.table "update join" 就地修改,速度相当快:

library(data.table)
setDT(df1); setDT(df2)
df1[df2, on = .(x1), x2 := i.x2]

或者,假设您不关心维护行顺序,您可以使用受 SQL 启发的 dplyr

library(dplyr)
union_all(
  inner_join( df1["x1"], df2 ), # x1 from df1 with matches in df2, x2 from df2
  anti_join(  df1, df2["x1"] )  # rows of df1 with no match in df2
) # %>% arrange(x1) # optional, won't maintain an arbitrary row order

其中任何一个都将比逐行 for 循环更好地扩展。

【讨论】:

data.table 成语是df1[df2, on=.(x1), x2 := i.x2 ] - 就地修改(“替换 df1$x2 中的一些值”作为 OP 要求)并且不需要设置键。它类似于来自 SQL 的更新连接。 @Frank 是的,你打败了我。 好的。 df1[df2, x2 := df2[,x2]] 不是一回事,仅供参考。 @Frank 看起来像 Hadley decided not 在 dplyr 中实现更新加入,这对我来说似乎是包中的一个弱点。 是的,我看到了。 Hadley 排除它们的原因很弱(说他坚持使用纯 SQL),因为更新连接存在于某些 SQL 风格中。这只是归结为他提出的“语法”不够灵活。【参考方案3】:

我们可以使用我的包safejoin 中的eat 和“补丁” 列冲突时从右轴匹配到左轴。

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
library(dplyr)

df1 <- data.frame(x1=1:4,x2=letters[1:4],stringsAsFactors=FALSE)
df2 <- data.frame(x1=2:3,x2=c("zz","qq"),stringsAsFactors=FALSE)

eat(df1, df2, .by = "x1", .conflict = "patch")
#   x1 x2
# 1  1  a
# 2  2 zz
# 3  3 qq
# 4  4  d

【讨论】:

【参考方案4】:

我看到 Joris 和 Aaron 都选择在没有因素的情况下构建示例。我当然可以理解这种选择。对于已经是因素的列的读者,还可以选择强制转换为“字符”。有一种策略可以避免这种限制,并且还允许df2 中的索引可能不在df1 中,我认为这会使 Joris Meys 的解决方案无效,但 Aaron 迄今为止发布的解决方案不会:

df1 <- data.frame(x1=1:4,x2=letters[1:4])
df2 <- data.frame(x1=c(2,3,5), x2=c("zz", "qq", "xx") )

需要扩展级别以包括两个因子变量的交集,然后还需要在 match(df1$x1, df2$x1) 中删除不匹配的列(= NA 值)

 df1$x2 <- factor(df1$x2 , levels=c(levels(df1$x2), levels(df2$x2)) )
 df1$x2[na.omit(match(df2$x1,df1$x1))] <- df2$x2[which(df2$x1 %in% df1$x1)]
 df1
#-----------
  x1 x2
1  1  a
2  2 zz
3  3 qq
4  4  d

(请注意,R 的最新版本在 data.frame 函数默认值中没有将 stringsAsFactors 设置为 TRUE,这与 R 的大部分历史不同。)

【讨论】:

不错。因素可能很棘手,扩大水平的建议很有帮助。不过,您确实会在 df1$x2 中获得不需要的级别(xx)。 如果您想删除现在多余的关卡,请执行以下操作:df1$x2 &lt;- factor(df1$x2)【参考方案5】:

您也可以通过匹配其他方式来做到这一点,但它更复杂。 Joris 的解决方案更好,但我将其放在这里也是为了提醒您考虑要匹配的方式。

df1 <- data.frame(x1=1:4, x2=letters[1:4], stringsAsFactors=FALSE)
df2 <- data.frame(x1=2:3, x2=c("zz", "qq"), stringsAsFactors=FALSE)
swap <- df2$x2[match(df1$x1, df2$x1)]
ok <- !is.na(swap)
df1$x2[ok] <- swap[ok]

> df1
  x1 x2
1  1  a
2  2 zz
3  3 qq
4  4  d

【讨论】:

【参考方案6】:

可以通过dplyr完成。

library(dplyr)

full_join(df1,df2,by = c("x1" = "x1")) %>% 
  transmute(x1 = x1,x2 = coalesce(x2.y,x2.x))

  x1 x2
1  1  a
2  2 zz
3  3 qq
4  4  d

【讨论】:

【参考方案7】:

这里是新的,但使用以下 dplyr 方法似乎也可以工作 与上述答案之一相似但略有不同

df3 <- anti_join(df1, df2, by = "x1")
df3 <- rbind(df3, df2)
df3

【讨论】:

以上是关于R中的条件合并/替换的主要内容,如果未能解决你的问题,请参考以下文章

使用 R 中的条件替换列中的值

根据R中的条件合并列

Group_by,条件求和并替换R中的变量

根据条件替换R数据帧中的值[重复]

合并条件(或合并后用 NaN 替换)

R语言is.na函数实战(删除替换统计条件判断等)