2011-05-24 2 views
11

У меня есть два кадра данных:условного слияния/замены в 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 и x2 df2 $ производить:

df1 
x1 x2 
1 a 
2 zz 
3 qq 
4 d 

ответ

11

использования 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] 
} 
+0

Ницца. Я написал матч с противоположными аргументами и не мог понять, почему он был более сложным, чем я думал, что это должно быть. Я также добавлю свой ответ, потому что это может помочь другим подумать о том, как изменение порядка аргументов в матче может сделать вещи проще или сложнее. – Aaron

+0

Спасибо, Joris. Я работал с «матчем», но не мог заставить его работать. – Mike

+0

Я добавил решение, которое будет работать лучше в случае не уникальных значений в df1. – C8H10N4O2

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 
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")) 

It требует, чтобы уровни быть расширены, чтобы включать в себя пересечение два переменных факторов, а затем также необходимость отказаться от несоответствующих столбцов (= значения 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 
+1

Nice. Факторы могут быть сложными, и советы по расширению уровней полезны. В итоге вы получаете ненужный уровень в 'df1 $ x2' (' xx'). – Aaron

+0

Если вы хотите удалить лишние уровни, сделайте следующее: 'df1 $ x2 <- factor (df1 $ x2)' –

4

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

Вы можете использовать data.table «обновление присоединиться», чтобы изменить на месте, что будет довольно быстро:

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

# (thanks to @Frank for suggestion) 
# Or if you didn't set keys beforehand you could do: 
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 

Любой из них будет масштабировать намного лучше, чем строчном для цикла.

+0

Идентификатор data.table - 'df1 [df2, on =. (X1), x2: = i.x2] '- изменяется на месте (« замените некоторые из значений в df1 $ x2 »по запросу OP) и не требует установки ключей. Это похоже на обновление соединения с SQL. – Frank

+0

@ Фрэнк, ты бил меня. – C8H10N4O2

+1

Хорошо. 'df1 [df2, x2: = df2 [, x2]]' не то же самое, fyi. – Frank