[英]Conditional merge/replacement in R
我有兩個數據框:
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
使用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]
}
Joris 回答的第一部分很好,但是在df1
中的非唯一值的情況下,逐行 for 循環在大型 data.frames 上無法很好地擴展。
您可以使用data.table
“更新加入”來修改,這將非常快:
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 循環更好地擴展。
我們可以使用我的 package safejoin中的eat
,並在列沖突時將 rhs 中的匹配“修補”到 lhs 中。
# 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
我看到 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
function 默認值中沒有將stringsAsFactors
設置為 TRUE,這與 ZE1E1D3D40573127E9EE0480CAF1 的大部分歷史不同。)
它可以用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
您也可以通過匹配其他方式來做到這一點,但它更復雜。 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
這里是新的,但使用以下 dplyr 方法似乎也有效
與上述答案之一相似但略有不同
df3 <- anti_join(df1, df2, by = "x1")
df3 <- rbind(df3, df2)
df3
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.