Условное слияние / замена в R

r match r-faq

17330 просмотра

5 ответа

У меня есть два кадра данных:

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
Автор: Mike Источник Размещён: 17.05.2019 02:58

Ответы (5)


15 плюса

использовать 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 Meys Размещён: 24.05.2011 02:49

4 плюса

Я вижу, что Джорис и Аарон решили строить примеры без факторов. Я, конечно, могу понять этот выбор. Для читателя с колонками, которые уже являются факторами, также был бы вариант приведения к «характеру». Существует стратегия, которая устраняет это ограничение и которая также допускает вероятность того, что в 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
Автор: 42- Размещён: 24.05.2011 03:32

4 плюса

Первая часть ответа Joris хороша, но в случае неуникальных значений in df1-for-loop цикл не будет хорошо масштабироваться на больших фреймах данных.

Вы можете использовать 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.

Автор: C8H10N4O2 Размещён: 21.07.2017 03:17

1 плюс

Вы можете сделать это, сопоставив и другой путь, но это сложнее. Решение 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
Автор: Aaron Размещён: 24.05.2011 02:59

0 плюса

Мы можем использовать 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
Автор: Moody_Mudskipper Размещён: 26.02.2019 12:36
Вопросы из категории :
32x32