Bedingte Ersetzung von Werten in einem data.frame

73

Ich versuche zu verstehen, wie man Werte in einem Datenrahmen ohne Verwendung einer Schleife bedingt ersetzt. Mein Datenrahmen ist wie folgt aufgebaut:

> 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

und die dputAusgabe ist folgende:

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

Was ich tun möchte, ist den Wert von zu überprüfen b. Wenn b0 ist, möchte ich esteinen Wert von setzen a. Ich verstehe, dass df$est[df$b == 0] <- 23alle Werte estauf 23 gesetzt werden, wenn b==0. Was ich nicht verstehe, ist, wie man esteinen Wert afestlegt, wenn diese Bedingung erfüllt ist. Zum Beispiel:

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

gibt die folgende Warnung:

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

Gibt es eine Möglichkeit, die relevante Zelle anstelle des Vektors zu übergeben?

djq
quelle

Antworten:

91

Da Sie bedingt indizieren df$est, müssen Sie auch den Ersetzungsvektor bedingt indizieren df$a:

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

Natürlich ist die Variable indexnur vorübergehend und ich verwende sie, um den Code ein bisschen lesbarer zu machen. Sie können es in einem Schritt schreiben:

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

Für eine noch bessere Lesbarkeit können Sie Folgendes verwenden within:

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

Die Ergebnisse, unabhängig davon, welche Methode Sie wählen:

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

Wie andere bereits betont haben, besteht eine alternative Lösung in Ihrem Beispiel darin, sie zu verwenden ifelse.

Andrie
quelle
27

Probieren Sie den :=Operator von data.table aus :

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

Es ist schnell und kurz. In diesen verknüpften Fragen finden Sie weitere Informationen zu :=:

Warum hat data.table definiert :=

Wann sollte ich den :=Operator in verwendendata.table

Wie entferne ich Spalten aus a data.frame

R Selbstreferenz

Matt Dowle
quelle
Schön, danke für Referenzen +1 für alles.
PKumar
Sehr nützliche Antwort. Wenn Sie dies verwenden, stellen Sie sicher, dass DT keine Funktion im Paket data.table ist, sondern eine Referenz für das Datentabellenobjekt.
Alison Bennett
20

Hier ist ein Ansatz. ifelsewird vektorisiert und überprüft alle Zeilen auf Nullwerte von bund ersetzt estdurch, (a - 5)/2.53wenn dies der Fall ist.

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

Eine andere Möglichkeit wäre, case_when zu verwenden

require(dplyr)

mutate(df, est = case_when(
    b == 0 ~ (a - 5)/2.53, 
    TRUE   ~ est 
))

Diese Lösung ist noch praktischer, wenn mehr als zwei Fälle unterschieden werden müssen, da verschachtelte if_elseKonstrukte vermieden werden können.

Holger Brandl
quelle
7

Das R-Inferno oder die grundlegende R-Dokumentation erklären, warum die Verwendung von df $ * hier nicht der beste Ansatz ist. Von der Hilfeseite für "[":

"Die Indizierung nach [ähnelt Atomvektoren und wählt eine Liste der angegebenen Elemente aus. Sowohl [[als auch $ wählen ein einzelnes Element der Liste aus. Der Hauptunterschied besteht darin, dass $ keine berechneten Indizes zulässt, während [[dies tut x $ name entspricht x [["name", exakt = FALSE]]. Außerdem kann das partielle Übereinstimmungsverhalten von [[mit dem exakten Argument gesteuert werden. "

Ich empfehle stattdessen die [row,col]Notation. Beispiel:

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
Carl Witthoft
quelle
Dies verdient eine positive Bewertung, wenn Sie zuerst entweder einen Link zur R-Inferno-Seite hinzufügen oder die Probleme mit $(oder im Idealfall mit beiden) zusammenfassen.
Andrie
+1 Obwohl ich denke, dass der $Operator in diesem Fall vollkommen in Ordnung ist. (Außerdem $
stelle
@Andrie: Ja, ich habe es dort verwendet, wo es funktioniert (nicht, dass das eine große Hilfe ist :-)). Das OP versuchte damit zu definieren, auf welche Elemente reagiert wurde, und hier begann das Problem. Ich habe es nur verwendet, um eine Bedingung zu definieren, die Datenrahmenelemente ausgewählt hat. Aber das
wusstest