it-swarm.com.de

Geschachtelte if else-Anweisung

Ich lerne immer noch, wie man einen SAS Code in R übersetzt, und ich erhalte Warnungen. Ich muss verstehen, wo ich Fehler mache. Ich möchte eine Variable erstellen, die und zusammenfasst Unterscheidet 3 Status einer Bevölkerung: Festland, Übersee, Ausländer. Ich habe eine Datenbank mit 2 Variablen:

  • iD Nationalität: idnat (Französisch, Ausländer),

Wenn idnat französisch ist, dann:

  • id Geburtsort: idbp (Festland, Kolonie, Übersee)

Ich möchte die Informationen aus idnat und idbp in einer neuen Variablen namens idnat2 Zusammenfassen:

  • status: k (Festland, Übersee, Ausländer)

Alle diese Variablen verwenden "Zeichentyp".

Ergebnisse in Spalte idnat2 erwartet:

   idnat     idbp   idnat2
1  french mainland mainland
2  french   colony overseas
3  french overseas overseas
4 foreign  foreign  foreign

Hier ist mein SAS Code, den ich in R übersetzen möchte:

if idnat = "french" then do;
   if idbp in ("overseas","colony") then idnat2 = "overseas";
   else idnat2 = "mainland";
end;
else idnat2 = "foreigner";
run;

Hier ist mein Versuch in R:

if(idnat=="french"){
    idnat2 <- "mainland"
} else if(idbp=="overseas"|idbp=="colony"){
    idnat2 <- "overseas"
} else {
    idnat2 <- "foreigner"
}

Ich erhalte diese Warnung:

Warning message:
In if (idnat=="french") { :
  the condition has length > 1 and only the first element will be used

Es wurde mir empfohlen, stattdessen ein "verschachteltes ifelse" zu verwenden, aber es werden mehr Warnungen angezeigt:

idnat2 <- ifelse (idnat=="french", "mainland",
        ifelse (idbp=="overseas"|idbp=="colony", "overseas")
      )
            else (idnat2 <- "foreigner")

Laut Warnmeldung ist die Länge größer als 1, sodass nur die Angaben zwischen den ersten Klammern berücksichtigt werden. Entschuldigung, aber ich verstehe nicht, was diese Länge hier zu tun hat? Weiß jemand wo ich falsch liege?

53
balour

Wenn Sie eine Tabellenkalkulationsanwendung verwenden, gibt es eine Grundfunktion if() mit folgender Syntax:

if(<condition>, <yes>, <no>)

Die Syntax ist für ifelse() in R genau gleich:

ifelse(<condition>, <yes>, <no>)

Der einzige Unterschied zu if() in der Tabellenkalkulationsanwendung besteht darin, dass R ifelse() vektorisiert wird (Vektoren werden als Eingabe verwendet und Vektoren werden bei der Ausgabe zurückgegeben). Betrachten Sie den folgenden Vergleich von Formeln in der Tabellenkalkulationsanwendung und in R als Beispiel, in dem wir vergleichen möchten, ob a> b und 1 zurückgeben, wenn ja, und 0, wenn nicht.

In der Tabelle:

  A  B C
1 3  1 =if(A1 > B1, 1, 0)
2 2  2 =if(A2 > B2, 1, 0)
3 1  3 =if(A3 > B3, 1, 0)

In R:

> a <- 3:1; b <- 1:3
> ifelse(a > b, 1, 0)
[1] 1 0 0

ifelse() kann auf verschiedene Arten verschachtelt werden:

ifelse(<condition>, <yes>, ifelse(<condition>, <yes>, <no>))

ifelse(<condition>, ifelse(<condition>, <yes>, <no>), <no>)

ifelse(<condition>, 
       ifelse(<condition>, <yes>, <no>), 
       ifelse(<condition>, <yes>, <no>)
      )

ifelse(<condition>, <yes>, 
       ifelse(<condition>, <yes>, 
              ifelse(<condition>, <yes>, <no>)
             )
       )

Um die Spalte idnat2 Zu berechnen, können Sie:

df <- read.table(header=TRUE, text="
idnat idbp idnat2
french mainland mainland
french colony overseas
french overseas overseas
foreign foreign foreign"
)

with(df, 
     ifelse(idnat=="french",
       ifelse(idbp %in% c("overseas","colony"),"overseas","mainland"),"foreign")
     )

R Dokumentation

Was ist the condition has length > 1 and only the first element will be used? Wir werden sehen:

> # What is first condition really testing?
> with(df, idnat=="french")
[1]  TRUE  TRUE  TRUE FALSE
> # This is result of vectorized function - equality of all elements in idnat and 
> # string "french" is tested.
> # Vector of logical values is returned (has the same length as idnat)
> df$idnat2 <- with(df,
+   if(idnat=="french"){
+   idnat2 <- "xxx"
+   }
+   )
Warning message:
In if (idnat == "french") { :
  the condition has length > 1 and only the first element will be used
> # Note that the first element of comparison is TRUE and that's whay we get:
> df
    idnat     idbp idnat2
1  french mainland    xxx
2  french   colony    xxx
3  french overseas    xxx
4 foreign  foreign    xxx
> # There is really logic in it, you have to get used to it

Kann ich noch if() verwenden? Ja, das kannst du, aber die Syntax ist nicht so cool :)

test <- function(x) {
  if(x=="french") {
    "french"
  } else{
    "not really french"
  }
}

apply(array(df[["idnat"]]),MARGIN=1, FUN=test)

Wenn Sie mit SQL vertraut sind, können Sie auch CASEAnweisung in sqldfPaket verwenden.

97
Tomas Greif

Versuchen Sie Folgendes:

# some sample data
idnat <- sample(c("french","foreigner"),100,TRUE)
idbp <- rep(NA,100)
idbp[idnat=="french"] <- sample(c("mainland","overseas","colony"),sum(idnat=="french"),TRUE)

# recoding
out <- ifelse(idnat=="french" & !idbp %in% c("overseas","colony"), "mainland",
              ifelse(idbp %in% c("overseas","colony"),"overseas",
                     "foreigner"))
cbind(idnat,idbp,out) # check result

Ihre Verwirrung rührt daher, wie SAS und R mit if-else-Konstruktionen umgehen. In R werden if und else nicht vektorisiert, dh sie prüfen, ob eine einzelne Bedingung vorliegt ist wahr (dh if("french"=="french") funktioniert) und kann nicht mit mehreren logischen Verknüpfungen umgehen (dh if(c("french","foreigner")=="french") funktioniert nicht) und R gibt Ihnen die Warnung aus, die Sie erhalten.

Im Gegensatz dazu ist ifelse vektorisiert, sodass Ihre Vektoren (auch als Eingabevariablen bezeichnet) die logischen Bedingungen für jedes ihrer Elemente testen können, wie Sie es in SAS gewohnt sind. Eine alternative Möglichkeit, dies in den Kopf zu schließen, besteht darin, eine Schleife mit den Anweisungen if und else (wie hier beschrieben) zu erstellen, jedoch mit dem vektorisierten Ansatz ifelse ist effizienter und erfordert im Allgemeinen weniger Code.

10
Thomas

Wenn der Datensatz viele Zeilen enthält, ist es möglicherweise effizienter, mit data.table Anstelle von ifelse() eine Verknüpfung mit einer Nachschlagetabelle herzustellen.

Bereitgestellt die Nachschlagetabelle unten

lookup
     idnat     idbp   idnat2
1:  french mainland mainland
2:  french   colony overseas
3:  french overseas overseas
4: foreign  foreign  foreign

und einen Beispieldatensatz

library(data.table)
n_row <- 10L
set.seed(1L)
DT <- data.table(idnat = "french",
                 idbp = sample(c("mainland", "colony", "overseas", "foreign"), n_row, replace = TRUE))
DT[idbp == "foreign", idnat := "foreign"][]
      idnat     idbp
 1:  french   colony
 2:  french   colony
 3:  french overseas
 4: foreign  foreign
 5:  french mainland
 6: foreign  foreign
 7: foreign  foreign
 8:  french overseas
 9:  french overseas
10:  french mainland

dann können wir ein Update durchführen, während wir beitreten :

DT[lookup, on = .(idnat, idbp), idnat2 := i.idnat2][]
      idnat     idbp   idnat2
 1:  french   colony overseas
 2:  french   colony overseas
 3:  french overseas overseas
 4: foreign  foreign  foreign
 5:  french mainland mainland
 6: foreign  foreign  foreign
 7: foreign  foreign  foreign
 8:  french overseas overseas
 9:  french overseas overseas
10:  french mainland mainland
8
Uwe

Sie können den Vektor idnat2 Ohne if und ifelse erstellen.

Die Funktion replace kann verwendet werden, um alle Vorkommen von "colony" Durch "overseas" Zu ersetzen:

idnat2 <- replace(idbp, idbp == "colony", "overseas")
7
Sven Hohenstein

Verwenden der SQL-Anweisung CASE mit den Paketen dplyr und sqldf:

Daten

df <-structure(list(idnat = structure(c(2L, 2L, 2L, 1L), .Label = c("foreign", 
"french"), class = "factor"), idbp = structure(c(3L, 1L, 4L, 
2L), .Label = c("colony", "foreign", "mainland", "overseas"), class = "factor")), .Names = c("idnat", 
"idbp"), class = "data.frame", row.names = c(NA, -4L))

sqldf

library(sqldf)
sqldf("SELECT idnat, idbp,
        CASE 
          WHEN idbp IN ('colony', 'overseas') THEN 'overseas' 
          ELSE idbp 
        END AS idnat2
       FROM df")

dplyr

library(dplyr)
df %>% 
mutate(idnat2 = case_when(.$idbp == 'mainland' ~ "mainland", 
                          .$idbp %in% c("colony", "overseas") ~ "overseas", 
                         TRUE ~ "foreign"))

Ausgabe

    idnat     idbp   idnat2
1  french mainland mainland
2  french   colony overseas
3  french overseas overseas
4 foreign  foreign  foreign
3
mpalanco

Bei data.table sind die Lösungen:

DT[, idnat2 := ifelse(idbp %in% "foreign", "foreign", 
        ifelse(idbp %in% c("colony", "overseas"), "overseas", "mainland" ))]

Das ifelse wird vektorisiert. Das if-else ist nicht. Hier ist DT:

    idnat     idbp
1  french mainland
2  french   colony
3  french overseas
4 foreign  foreign

Das gibt:

   idnat     idbp   idnat2
1:  french mainland mainland
2:  french   colony overseas
3:  french overseas overseas
4: foreign  foreign  foreign
1
Sun Bee
# Read in the data.

idnat=c("french","french","french","foreign")
idbp=c("mainland","colony","overseas","foreign")

# Initialize the new variable.

idnat2=as.character(vector())

# Logically evaluate "idnat" and "idbp" for each case, assigning the appropriate level to "idnat2".

for(i in 1:length(idnat)) {
  if(idnat[i] == "french" & idbp[i] == "mainland") {
    idnat2[i] = "mainland"
} else if (idnat[i] == "french" & (idbp[i] == "colony" | idbp[i] == "overseas")) {
  idnat2[i] = "overseas"
} else {
  idnat2[i] = "foreign"
} 
}

# Create a data frame with the two old variables and the new variable.

data.frame(idnat,idbp,idnat2) 
0
Azul