2011-05-24 16 views
11

Mam dwóch ramek danych:Warunkowe seryjnej/wymiany w R

df1 
x1 x2 
1 a 
2 b 
3 c 
4 d 

i

df2 
x1 x2 
2 zz 
3 qq 

Chcę zastąpić niektóre wartości w DF1 $ x2 z wartościami w df2 $ x2 na podstawie mecz warunkowego między DF1 $ x1 i x2 DF2 $ produkować:

df1 
x1 x2 
1 a 
2 zz 
3 qq 
4 d 

Odpowiedz

11

użycie match(), przyjmując wartości w DF1 są unikatowe.

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 

Jeśli wartości nie są wyjątkowe, należy:

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

Nice. Napisałem mecz z odwróconymi argumentami i nie mogłem się domyślić, dlaczego było to bardziej skomplikowane, niż myślałem. Dodam również moją odpowiedź, ponieważ może pomóc innym zastanowić się, w jaki sposób zmiana kolejności argumentów w meczu może ułatwić lub utrudnić proces. – Aaron

+0

Dzięki Joris. Pracowałem z "dopasowaniem", ale nie mogłem go uruchomić. – Mike

+0

Dodałem rozwiązanie, które będzie działać lepiej w przypadku nieunikalnych wartości w df1. – C8H10N4O2

1

Można to zrobić poprzez dopasowanie w drugą stronę, ale jest to bardziej skomplikowane. Rozwiązanie Joris jest lepsze, ale umieszczam to tutaj również jako przypomnienie, aby zastanowić się, w którą stronę chcesz się dopasować.

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

Widzę, że Joris i Aaron postanowili zbudować przykłady bez czynników. Z pewnością mogę zrozumieć ten wybór. Dla czytelnika z kolumnami, które są już czynnikami, istnieje również opcja przymusu do "znaku". Jest to strategia, która pozwala uniknąć tego ograniczenia, a które także dopuszcza możliwość, że mogą być indeksy w DF2, które nie są w DF1 który wierzę, że unieważnia Joris Meys ale nie Aarons rozwiązania pisał do tej pory:

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

It wymaga to poziom jest rozszerzona na przecięcie obu zmiennych czynników, a następnie również potrzeby usuwania niedopasowanych kolumny (= wartości nA) dopasowania (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. Czynniki mogą być trudne, a porady dotyczące poszerzenia poziomów są pomocne. W końcu kończysz z niepotrzebnym poziomem w 'df1 $ x2' (' xx'). – Aaron

+0

Jeśli chcesz usunąć teraz zbędne poziomy, wykonaj następujące czynności: 'df1 $ x2 <- factor (df1 $ x2)' –

4

pierwszy część odpowiedzi Jorisa jest dobra, ale w przypadku nieunikalnych wartości w df1, pętla for-forse nie będzie dobrze skalowana large data.frames.

Można użyć aktualizacji „join” data.table zmodyfikować w miejscu, które będzie dość szybko:

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] 

Albo, zakładając, że nie dbają o utrzymanie porządku wierszy, można użyć SQL inspirowane 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 

Każda z nich będzie skala znacznie lepiej niż row-mądry dla pętli.

+0

Idiomem data.table jest 'df1 [df2, on =. (X1), x2: = i.x2] '- modyfikuje na miejscu (" zastępuje niektóre wartości w df1 $ x2 "jako zadane przez OP) i nie wymaga ustawiania klawiszy. Jest podobny do sprzężenia aktualizacji z SQL. – Frank

+0

@Frank yep mnie bić do tego. – C8H10N4O2

+1

Ok. 'df1 [df2, x2: = df2 [, x2]]' to nie to samo, fyi. – Frank