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