2011-11-21 18 views
48

Sto cercando di capire come sostituire i valori condizionali in un dataframe senza utilizzare un ciclo. La mia struttura dei dati è strutturata come segue:Sostituzione condizionale dei valori in un data.frame

> df 
      a b est 
1 11.77000 2 0 
2 10.90000 3 0 
3 10.32000 2 0 
4 10.96000 0 0 
5 9.90600 0 0 
6 10.70000 0 0 
7 11.43000 1 0 
8 11.41000 2 0 
9 10.48512 4 0 
10 11.19000 0 0 

e l'uscita dput è questo:

structure(list(a = c(11.77, 10.9, 10.32, 10.96, 9.906, 10.7, 
11.43, 11.41, 10.48512, 11.19), b = c(2, 3, 2, 0, 0, 0, 1, 2, 
4, 0), est = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), .Names = c("a", 
"b", "est"), row.names = c(NA, -10L), class = "data.frame") 

Quello che voglio fare, è quello di verificare il valore di b. Se b è 0, voglio impostare est su un valore da a. Capisco che df$est[df$b == 0] <- 23 imposterà tutti i valori di est a 23, quando b==0. Quello che non capisco è come impostare est su un valore di a quando tale condizione è vera. Per esempio:

df$est[df$b == 0] <- (df$a - 5)/2.533 

ha pronunciato la seguente avvertenza:

Warning message: 
In df$est[df$b == 0] <- (df$a - 5)/2.533 : 
    number of items to replace is not a multiple of replacement length 

C'è un modo che io possa passare il cellulare in questione, invece di vettore?

risposta

53

Dal momento che sono condizionalmente l'indicizzazione df$est, è inoltre necessario condizionale indicizzare il vettore di sostituzione df$a:

index <- df$b == 0 
df$est[index] <- (df$a[index] - 5)/2.533 

Naturalmente, la variabile index è solo temporanea, e lo uso per rendere il codice un po 'più leggibile. È possibile scrivere in un solo passaggio:

df$est[df$b == 0] <- (df$a[df$b == 0] - 5)/2.533 

Per ancora migliore leggibilità, è possibile utilizzare within:

df <- within(df, est[b==0] <- (a[b==0]-5)/2.533) 

I risultati, indipendentemente dal metodo scelto:

df 
      a b  est 
1 11.77000 2 0.000000 
2 10.90000 3 0.000000 
3 10.32000 2 0.000000 
4 10.96000 0 2.352941 
5 9.90600 0 1.936834 
6 10.70000 0 2.250296 
7 11.43000 1 0.000000 
8 11.41000 2 0.000000 
9 10.48512 4 0.000000 
10 11.19000 0 2.443743 

Come altri hanno sottolineato, una soluzione alternativa nel tuo esempio è usare ifelse .

11

Ecco un approccio. ifelse è vettorizzato e controlla tutte le righe per i valori zero di b e sostituisce est con (a - 5)/2.53 se questo è il caso.

df <- transform(df, est = ifelse(b == 0, (a - 5)/2.53, est)) 
5

Il R-inferno o la documentazione di base R spiegherà perché l'utilizzo di df $ * non è l'approccio migliore qui. Dalla pagina di aiuto per "[":

"Indicizzazione da [è simile a vettori atomiche e sceglie un elenco dell'elemento specificato (s) Entrambi [[e $ selezionare un singolo elemento della lista La principale.. la differenza è che $ non consente indici calcolati, mentre [[fa. $ nome è equivalente a x [["nome", esatto = FALSO]]. Inoltre, il comportamento di corrispondenza parziale di [[può essere controllato usando l'argomento esatto . "

Si consiglia di utilizzare invece la notazione [row,col].Esempio:

Rgames: foo 
     x y z 
    [1,] 1e+00 1 0 
    [2,] 2e+00 2 0 
    [3,] 3e+00 1 0 
    [4,] 4e+00 2 0 
    [5,] 5e+00 1 0 
    [6,] 6e+00 2 0 
    [7,] 7e+00 1 0 
    [8,] 8e+00 2 0 
    [9,] 9e+00 1 0 
    [10,] 1e+01 2 0 
Rgames: foo<-as.data.frame(foo) 

Rgames: foo[foo$y==2,3]<-foo[foo$y==2,1] 
Rgames: foo 
     x y  z 
1 1e+00 1 0e+00 
2 2e+00 2 2e+00 
3 3e+00 1 0e+00 
4 4e+00 2 4e+00 
5 5e+00 1 0e+00 
6 6e+00 2 6e+00 
7 7e+00 1 0e+00 
8 8e+00 2 8e+00 
9 9e+00 1 0e+00 
10 1e+01 2 1e+01 
+0

questo merita un upvote se si aggiunge prima o un link alla pagina R-Inferno , o riepilogare i problemi con '$' (o idealmente entrambi). – Andrie

+0

+1 Anche se penso che l'operatore '$' stia perfettamente bene in questo caso. (Inoltre, noto che nonostante il tuo avviso tu usi '$' te stesso ...) – Andrie

+0

@Andrie: sì, l'ho usato dove funziona (non che sia molto utile :-)). L'OP ha cercato di usarlo per definire su quali elementi si stava agendo, ed è qui che sono iniziati i problemi. L'ho usato solo per definire una condizione che selezionava gli elementi del dataframe. Ma tu lo sapevi :-) –

24

Try 's data.table:= operator:

DT = as.data.table(df) 
DT[b==0, est := (a-5)/2.533] 

E' veloce e breve. Vedere queste domande legate per ulteriori informazioni su :=:

Why has data.table defined :=

When should I use the := operator in data.table

How do you remove columns from a data.frame

R self reference

+0

Bello, grazie per i riferimenti +1 per tutto. – PKumar

+0

Risposta molto utile. Se si utilizza questo, assicurarsi di notare che DT non è una funzione nel pacchetto data.table, ma un riferimento per l'oggetto tabella di dati. –

Problemi correlati