Условное слияние / замена в R
У меня есть два кадра данных:
df1
x1 x2
1 a
2 b
3 c
4 d
а также
df2
x1 x2
2 zz
3 qq
Я хочу заменить некоторые значения в df1$x2 значениями в df2$x2 на основе условного соответствия между df1$x1 и df2$x2, чтобы получить:
df1
x1 x2
1 a
2 zz
3 qq
4 d
8 ответов
Использование 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]
}
Мы могли бы использовать eat
из моего пакета safejoin, и " пропатчите " совпадения из 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
Первая часть ответа Джориса хороша, но в случае неуникальных значений в df1
цикл для строки не будет хорошо масштабироваться на больших фреймах данных.
Вы могли бы использовать 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.
Я вижу, что Джорис и Аарон решили строить примеры без факторов. Я, конечно, могу понять этот выбор. Для читателя со столбцами, которые уже являются факторами, также существует возможность приведения к "характеру". Существует стратегия, которая устраняет это ограничение и которая также допускает вероятность того, что в df2 могут быть индексы, которых нет в df1, что, как я полагаю, лишит законной силы Joris Meys, но не решения Aarons, опубликованные до сих пор:
df1 <- data.frame(x1=1:4,x2=letters[1:4])
df2 <- data.frame(x1=c(2,3,5), x2=c("zz", "qq", "xx") )
Это требует, чтобы уровни были расширены, чтобы включить пересечение обеих факторных переменных, а затем также необходимость отбрасывать несовпадающие столбцы (= значения NA) в совпадении (df1$x1, df2$x1)
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
Это может быть сделано с 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
Начиная с dplyr 1.0.0 для этого есть специальная функция:
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)
rows_update(df1, df2, by = "x1")
См. /questions/41629389/obnovlenie-zamena-znachenij-v-datafrejme-s-tidyverse-join/60195845#60195845