Déclaration ifelse imbriquée

j'apprends encore à traduire un code SAS en R et je reçois des avertissements. J'ai besoin de comprendre où je fais des erreurs. Ce que je veux faire, c'est créer une variable qui résume et différencie 3 statuts d'une population: continentale, étrangère, étrangère. J'ai une base de données avec 2 variables:

  • id nationalité: idnat (Français, étranger),

si idnat est français alors:

  • id lieu de naissance: idbp (continentale, les colonies d'outre-mer)

je veux résumer l'information de idnat et idbp dans une nouvelle variable appelée idnat2 :

  • statut: k (métropole, outre-mer, étranger)

toutes ces variables utilisent "type de caractère".

résultats attendus dans la colonne idnat2:

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

Voici mon code SAS que je veux traduire en R:

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

voici ma tentative en R:

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

je reçois cet avertissement:

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

on m'a conseillé d'utiliser un "emboîté ifelse "à la place pour sa facilité mais obtenir plus d'Avertissements:

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

selon le message D'Avertissement, La longueur est supérieur à 1 donc seul ce qui est entre les premières parenthèses sera pris en compte. Désolé mais je ne comprends pas ce que cette longueur a à faire ici? Quelqu'un sait où je me trompe?

49
demandé sur Saranjith 2013-08-02 12:29:58

7 réponses

si vous utilisez n'importe quelle application de tableur il y a une fonction de base if() avec la syntaxe:

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

est exactement la même pour ifelse() dans R:

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

la seule différence avec if() dans l'application de tableur est que R ifelse() est vectorisé (prend les vecteurs comme vecteur d'entrée et de retour sur la sortie). La comparaison suivante des formules dans l'application de tableur et dans R pour un exemple où nous aimerions comparer Si a > b et retourner 1 si oui et 0 si non.

dans tableur:

  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)

R:

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

ifelse() peut être imbriqué de plusieurs façons:

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

pour calculer la colonne idnat2 vous pouvez:

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 De La Documentation

Qu'est-ce que the condition has length > 1 and only the first element will be used ? Nous allons voir:

> # 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

puis-je toujours utiliser if() ? Oui, vous pouvez, mais la syntaxe n'est pas si cool :)

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

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

si vous êtes familier avec SQL, vous pouvez également utiliser CASE déclaration dans sqldf paquet .

86
répondu Tomas Greif 2017-05-23 11:55:07

Essayez quelque chose comme:

# 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

votre confusion vient de la façon dont SAS et R gèrent les constructions if-else. Dans R, if et else ne sont pas vectorisés, ce qui signifie qu'ils vérifient si une seule condition est vraie (i.e., if("french"=="french") fonctionne) et ne peut pas gérer plusieurs variables logiques (i.e., if(c("french","foreigner")=="french") ne fonctionne pas) et R Vous donne l'avertissement que vous recevez.

par contraste, ifelse est vectorisé, de sorte qu'il peut prenez vos vecteurs (alias variables d'entrée) et testez la condition logique sur chacun de leurs éléments, comme vous en avez l'habitude dans SAS. Une autre façon d'enrouler votre tête autour de cela serait de construire une boucle en utilisant les instructions if et else (comme vous avez commencé à le faire ici) mais l'approche vectorisée ifelse sera plus efficace et impliquera généralement moins de code.

10
répondu Thomas 2013-08-02 08:53:10

vous pouvez créer le vecteur idnat2 sans if et ifelse .

la fonction replace peut être utilisée pour remplacer tous les occurrences de "colony" par "overseas" :

idnat2 <- replace(idbp, idbp == "colony", "overseas")
7
répondu Sven Hohenstein 2013-08-02 16:18:25

si l'ensemble de données contient beaucoup de lignes, il pourrait être plus efficace de se joindre à une table de recherche en utilisant data.table au lieu de ifelse() imbriqués .

fournit le tableau de recherche ci-dessous

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

et un ensemble de données d'échantillon

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

puis nous pouvons faire une mise à jour en rejoignant :

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
7
répondu Uwe 2017-09-29 09:54:01

utilisant L'instruction de cas SQL avec les paquets dplyr et sqldf:

Données

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

Sortie

    idnat     idbp   idnat2
1  french mainland mainland
2  french   colony overseas
3  french overseas overseas
4 foreign  foreign  foreign
3
répondu mpalanco 2017-02-09 10:31:52

avec données.tableau, les solutions sont:

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

le ifelse est vectorisé. Le if-else ne l'est pas. Ici, DT est:

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

Cela donne:

   idnat     idbp   idnat2
1:  french mainland mainland
2:  french   colony overseas
3:  french overseas overseas
4: foreign  foreign  foreign
1
répondu Sun Bee 2016-09-19 09:22:52
# 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
répondu Azul 2018-08-28 08:30:02