2015-12-08 15 views
18

Vorrei utilizzare la sintassi di magrittr e dplyr per copiare singoli valori tra le righe in un sottoinsieme di colonne in base ai valori in altre colonne. Questo è un semplice esempio; Voglio applicare questa idea a molte colonne di un set di dati di grandi dimensioni con più condizioni all'interno di una lunga serie di comandi.Mutazioni condizionali del frame di dati in R con magrittr e dplyr

Prendere la dataframe df <- data.frame(a = 1:5, b = 6:10, x = 11:15, y = 16:20):

a b x y 

1 6 11 16 
2 7 12 17 
3 8 13 18 
4 9 14 19 
5 10 15 20 

Per la riga in cui a = 5, vorrei sostituire i valori di x e y con quelli nella fila in cui b = 7, per dare:

a b x y 

1 6 11 16 
2 7 12 17 
3 8 13 18 
4 9 14 19 
5 10 12 17 

Questo tentativo non riesce:

foo <- function(x){ifelse(df$a == 5, df[df$b == 7, .(df$x)], x)} 
df %<>% mutate_each(funs(foo), x, y) 

Il più vicino che posso ottenere è:

bar <- function(x){ifelse(df$a == 5, df[df$b == 7, "x"], x)} 
df %<>% mutate_each(funs(bar), x, y) 

ma questo non è corretto in quanto sostituisce entrambi i valori con il valore x, anziché x e y rispettivamente.

Grazie per il consiglio.

+1

qual è il dooffernece tra '% <>%' e '%>%'? –

+5

'x% <>% f' viene dal pacchetto' magrittr' ed è equivalente al modello comune 'x <- x %>% f'. –

+2

'%>%' viene anche dal pacchetto 'magrittr' ... –

risposta

13

Si potrebbe farlo utilizzando mutate_each e replace:

df %>% mutate_each(funs(replace(., a==5, nth(., which(b==7)))), x, y) 

uscita:

a b x y 
1 1 6 11 16 
2 2 7 12 17 
3 3 8 13 18 
4 4 9 14 19 
5 5 10 12 17 

O secondo il commento @docendodiscimus 's può essere ridotto ulteriormente a (e probabilmente [ è anche meglio di which):

df %>% mutate_each(funs(replace(., a==5, .[b==7])), x, y) 
+2

Grazie, ma voglio applicare questo metodo a un gran numero di colonne, potenzialmente definite altrove, quindi questo diventerebbe rapidamente ingombrante. –

+0

@PatrickHogan Non ho notato questo nella tua domanda. Potrei essere in grado di modificare il codice sopra. – LyzandeR

+0

Aggiornamento della risposta. Ora penso che questo dovrebbe adattarsi alle tue esigenze. – LyzandeR

9

solo per citare la soluzione data.table sarebbe:

require(data.table) 
setDT(df)[a == 5, c("x", "y") := df[b == 7, .SD, .SDcols = c("x", "y")]] 

> df 
    a b x y 
1: 1 6 11 16 
2: 2 7 12 17 
3: 3 8 13 18 
4: 4 9 14 19 
5: 5 10 12 17 

In alternativa, si potrebbe anche usare:

cols <- c("x", "y") 
setDT(df)[a == 5, (cols) := df[b == 7, .SD, .SDcols = cols]] 
# or 
cols <- c("x", "y") 
setDT(df)[a == 5, (cols) := df[b == 7, cols, with = FALSE]] 
+2

'DT <- setDT (df)' non ha molto senso in realtà .. dato che 'df' ora è anche un data.table ed è stato aggiornato per riferimento. – Arun

+0

@Arun: Sono assolutamente d'accordo. Quando ho iniziato ad usare 'data.table' il concetto di _updating by reference_ era molto strano per me. Poiché la domanda richiede una soluzione 'dplyr', ho pensato di rendere la mia risposta un po 'più comprensibile. – Rentrop

+0

Vedo .. Meglio usare as.data.table quindi. Anche usare 'with = FALSE' o' .SD + .SDcols' aiuterebbe a mostrare che è facilmente estensibile a molti cols. – Arun

5

Se il requisito principale è quello di applicare la funzione all'interno di un più dplyr-pipe, si potrebbe fare qualcosa come il seguente esempio:

foo <- function(df, cols = c("x", "y")) { 
    df[df$a == 5, cols] <- df[df$b == 7, cols] 
    df 
} 

df %>% ... %>% foo(c("x", "y")) %>% ... 
# a b x y 
#1 1 6 11 16 
#2 2 7 12 17 
#3 3 8 13 18 
#4 4 9 14 19 
#5 5 10 12 17