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?
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")
)
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 .
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.
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")
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
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
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
# 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)