web-dev-qa-db-fra.com

Affectation conditionnelle d'une variable à la valeur de l'une des deux autres variables

Je veux créer une nouvelle variable qui est égale à la valeur de l'une des deux autres variables, conditionnelle aux valeurs d'autres variables encore. Voici un exemple de jouet avec de fausses données.

Chaque ligne du bloc de données représente un élève. Chaque étudiant peut étudier jusqu'à deux matières (subj1 Et subj2), Et peut poursuivre un diplôme ("BA") ou une mineure ("MN") dans chaque matière. Mes données réelles incluent des milliers d'étudiants, plusieurs types de diplômes, environ 50 sujets, et les étudiants peuvent avoir jusqu'à cinq majors/mineurs.

   ID  subj1 degree1  subj2 degree2
1   1    BUS      BA   <NA>    <NA>
2   2    SCI      BA    ENG      BA
3   3    BUS      MN    ENG      BA
4   4    SCI      MN    BUS      BA
5   5    ENG      BA    BUS      MN
6   6    SCI      MN   <NA>    <NA>
7   7    ENG      MN    SCI      BA
8   8    BUS      BA    ENG      MN
...

Maintenant, je veux créer une sixième variable, df$major, Qui équivaut à la valeur de subj1 Si subj1 Est la majeure principale de l'élève, ou la valeur de subj2 si subj2 est le principal majeur. La majeure principale est la première matière avec un degré égal à "BA". J'ai essayé le code suivant:

df$major[df$degree1 == "BA"] = df$subj1
df$major[df$degree1 != "BA" & df$degree2 == "BA"] = df$subj2

Malheureusement, j'ai reçu un message d'erreur:

> df$major[df$degree1 == "BA"] = df$subj1
Error in df$major[df$degree1 == "BA"] = df$subj1 : 
  NAs are not allowed in subscripted assignments

Je suppose que cela signifie qu'une affectation vectorisée ne peut pas être utilisée si l'affectation est évaluée à NA pour au moins une ligne.

Je sens que je dois manquer quelque chose de basique ici, mais le code ci-dessus semblait être la chose évidente à faire et je n'ai pas pu trouver d'alternative.

Dans le cas où il serait utile d'écrire une réponse, voici des exemples de données, créées à l'aide de dput(), au même format que les fausses données répertoriées ci-dessus:

structure(list(ID = 1:20, subj1 = structure(c(3L, NA, 1L, 2L, 
2L, 3L, 2L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 1L, 3L, 3L, 1L, 2L, 1L
), .Label = c("BUS", "ENG", "SCI"), class = "factor"), degree1 = structure(c(2L, 
NA, 1L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L), .Label = c("BA", "MN"), class = "factor"), subj2 = structure(c(1L, 
2L, NA, NA, 1L, NA, 3L, 2L, NA, 2L, 2L, 1L, 3L, NA, 2L, 1L, 1L, 
NA, 2L, 2L), .Label = c("BUS", "ENG", "SCI"), class = "factor"), 
    degree2 = structure(c(2L, 2L, NA, NA, 2L, NA, 1L, 2L, NA, 
    2L, 1L, 1L, 2L, NA, 1L, 2L, 2L, NA, 1L, 2L), .Label = c("BA", 
    "MN"), class = "factor")), .Names = c("ID", "subj1", "degree1", 
"subj2", "degree2"), row.names = c(NA, -20L), class = "data.frame")
23
eipi10

Votre méthode d'affectation d'origine échoue pour au moins deux raisons.

1) Un problème avec l'affectation en indice df$major[df$degree1 == "BA"] <-. L'utilisation de == Peut produire NA, ce qui est à l'origine de l'erreur. From ?"[<-": "Lors du remplacement (c'est-à-dire l'indexation sur les lhs d'une affectation), NA ne sélectionne aucun élément à remplacer. Comme il existe une ambiguïté quant à savoir si un élément du rhs doit être utilisé ou non , cela n'est autorisé que si la valeur rhs est de longueur un (donc les deux interprétations auraient le même résultat). " Il existe plusieurs façons de contourner ce problème, mais je préfère utiliser which:

df$major[which(df$degree1 == "BA")] <-

La différence est que == Renvoie TRUE, FALSE et NA, tandis que which renvoie les indices d'un objet qui sont VRAIS

> df$degree1 == "BA"
 [1] FALSE    NA  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

> which(df$degree1 == "BA")
 [1]  3  4  5  8  9 10 11 12 13 14 15 16 17 18 19 20

2) Lorsque vous effectuez une tâche en indice, le côté droit doit s'insérer sensiblement dans le côté gauche (c'est la façon dont je pense à cela). Cela peut signifier des côtés gauche et droit de longueur égale, ce que votre exemple semble impliquer. Par conséquent, vous devez également sous-définir le côté droit de l'affectation:

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]

J'espère que cela explique pourquoi votre tentative initiale a produit une erreur.

L'utilisation de ifelse, comme suggéré par @DavidRobinson, est un bon moyen de faire ce type d'affectation. Ma vision:

df$major2 <- ifelse(df$degree1 == "BA", df$subj1, ifelse(df$degree2 == "BA",
  df$subj2,NA))

Cela équivaut à

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <- 
  df$subj2[which(df$degree1 != "BA" & df$degree2 == "BA")]

Selon la profondeur des instructions imbriquées ifelse, une autre approche pourrait être meilleure pour vos données réelles.


MODIFIER:

J'allais écrire une troisième raison pour l'échec du code d'origine (à savoir que df$major N'était pas encore attribué), mais cela fonctionne pour moi sans avoir à le faire. C'était un problème dont je me souviens avoir eu dans le passé, cependant. Quelle version de R utilisez-vous? (2.15.0 pour moi.) Cette étape n'est pas nécessaire si vous utilisez l'approche ifelse(). Votre solution est très bien lorsque vous utilisez [, Bien que j'aurais choisi

df$major <- NA

Pour obtenir les valeurs de caractères des sujets, au lieu de l'indice de niveau de facteur, utilisez as.character() (qui pour les facteurs est équivalent à et appelle levels(x)[x]):

df$major[which(df$degree1 == "BA")] <- as.character(df$subj1)[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <- 
  as.character(df$subj2)[which(df$degree1 != "BA" & df$degree2 == "BA")]

Idem pour la méthode ifelse():

df$major2 <- ifelse(df$degree1 == "BA", as.character(df$subj1),
  ifelse(df$degree2 == "BA", as.character(df$subj2), NA))
30
BenBarnes

En général, la fonction ifelse est le bon choix pour ces situations, quelque chose comme:

df$major = ifelse((!is.na(df$degree1) & df$degree1 == "BA") & (is.na(df$degree2) | df$degree1 != "BA"), df$subj1, df$subj2)

Cependant, son utilisation précise dépend de ce que vous faites si les deux df$degree1 et df$degree2 sont "BA".

7
David Robinson