Nettoyage des niveaux de facteur (réduction de plusieurs niveaux / étiquettes)

Quelle est la manière la plus efficace (c.-à-d. Efficace / appropriée) de nettoyer un facteur contenant plusieurs niveaux qui doivent être réduits? C’est-à-dire comment combiner deux ou plusieurs niveaux de facteurs en un seul.

Voici un exemple où les deux niveaux “Oui” et “Y” doivent être réduits à “Oui”, et “Non” et “N” réduits à “Non”:

## Given: x <- c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA ## expectedOutput [1] Yes Yes Yes No No  Levels: Yes No # <~~ NOTICE ONLY **TWO** LEVELS 

Une option est bien sûr de nettoyer les cordes avant la main en utilisant sub et amis.

Une autre méthode consiste à autoriser les étiquettes en double, puis à les déposer

 ## Duplicate levels ==> "Warning: deprecated" xf <- factor(x, levels=c("Y", "Yes", "No", "N"), labels=c("Yes", "Yes", "No", "No")) ## the above line can be wrapped in either of the next two lines factor(xf) droplevels(xf) 

Cependant, existe-t-il un moyen plus efficace ?


Bien que je sache que les arguments de levels et d’ labels devraient être des vecteurs, j’ai expérimenté des listes, des listes nommées et des vecteurs nommés pour voir ce qui se passe. Inutile de dire que

  factor(x, levels=list(c("Yes", "Y"), c("No", "N")), labels=c("Yes", "No")) factor(x, levels=c("Yes", "No"), labels=list(c("Yes", "Y"), c("No", "N"))) factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Y="Yes", Yes="Yes", No="No", N="No")) factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Yes="Y", Yes="Yes", No="No", No="N")) factor(x, levels=c("Yes", "No"), labels=c(Y="Yes", Yes="Yes", No="No", N="No")) 

Utilisez la fonction levels et transmettez-lui une liste nommée, les noms étant les noms souhaités des niveaux et les éléments étant les noms actuels à renommer.

 x <- c("Y", "Y", "Yes", "N", "No", "H") x <- factor(x) levels(x) <- list(Yes=c("Y", "Yes"), No=c("N", "No")) x ## [1] Yes Yes Yes No No  ## Levels: Yes No 

Comme mentionné dans la documentation des levels ; voir aussi les exemples là.

value: Pour la méthode ‘factor’, un vecteur de chaînes de caractères dont la longueur a au moins le nombre de niveaux de ‘x’, ou une liste nommée spécifiant comment renommer les niveaux.

Cela peut également être fait en une seule ligne, comme le fait Marek ici: https://stackoverflow.com/a/10432263/210673 ; les levels<- sorcellerie est expliquée ici https://stackoverflow.com/a/10491881/210673 .

 > `levels<-`(factor(x), list(Yes=c("Y", "Yes"), No=c("N", "No"))) [1] Yes Yes Yes No No  Levels: Yes No 

Comme la question s’intitule Nettoyer les niveaux de facteur (réduire plusieurs niveaux / étiquettes) , le package forcats doit également être mentionné ici, dans un souci d’exhaustivité. forcats est apparu sur CRAN en août 2016.

Plusieurs fonctions pratiques sont disponibles pour nettoyer les niveaux de facteur:

 x <- c("Y", "Y", "Yes", "N", "No", "H") library(forcats) 

Réduire les niveaux de facteur en groupes définis manuellement

 fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H") #[1] Yes Yes Yes No No  #Levels: No Yes 

Changer les niveaux de facteur à la main

 fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H") #[1] Yes Yes Yes No No  #Levels: No Yes 

Ré-étiqueter automatiquement les niveaux de facteur, réduire si nécessaire

 fun <- function(z) { z[z == "Y"] <- "Yes" z[z == "N"] <- "No" z[!(z %in% c("Yes", "No"))] <- NA z } fct_relabel(factor(x), fun) #[1] Yes Yes Yes No No  #Levels: No Yes 

Notez que fct_relabel() fonctionne avec les niveaux de facteur, il attend donc un facteur comme premier argument. Les deux autres fonctions, fct_collapse() et fct_recode() , acceptent également un vecteur de caractères qui est une fonctionnalité non documentée.

Réorganiser les niveaux de facteur par première apparition

Le résultat attendu du PO est

 [1] Yes Yes Yes No No  Levels: Yes No 

Ici, les niveaux sont classés comme ils apparaissent dans x ce qui est différent de la valeur par défaut ( ?factor : les niveaux d'un facteur sont sortingés par défaut ).

Pour être en ligne avec la sortie attendue, cela peut être réalisé en utilisant fct_inorder() avant de fct_inorder() les niveaux:

 fct_collapse(fct_inorder(x), Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H") fct_recode(fct_inorder(x), Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H") 

Les deux renvoient la sortie attendue avec les niveaux dans le même ordre, maintenant.

Peut-être qu’un vecteur nommé en tant que clé pourrait être utile:

 > factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x])) [1] Yes Yes Yes No No  Levels: No Yes 

Cela ressemble beaucoup à votre dernier essai … mais celui-ci fonctionne 🙂

Une autre méthode consiste à créer une table contenant le mappage:

 # stacking the list from Aaron's answer fmap = stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))) fmap$ind[ match(x, fmap$values) ] # [1] Yes Yes Yes No No  # Levels: No Yes # or... library(data.table) setDT(fmap)[x, on=.(values), ind ] # [1] Yes Yes Yes No No  # Levels: No Yes 

Je préfère de cette façon, car il laisse un object facilement inspecté résumant la carte; et le code data.table ressemble à n’importe quelle autre jointure de cette syntaxe.


Bien sûr, si vous ne voulez pas qu’un object comme fmap résume le changement, cela peut être un “one-liner”:

 library(data.table) setDT(stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))))[x, on=.(values), ind ] # [1] Yes Yes Yes No No  # Levels: No Yes 

Je ne connais pas votre véritable cas d’utilisation, mais serais- strsortingm utile ici …

 factor( strsortingm( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) ) #[1] Yes Yes Yes No No  #Levels: Yes No 

Semblable à l’approche de @ Aaron, mais légèrement plus simple serait:

 x <- c("Y", "Y", "Yes", "N", "No", "H") x <- factor(x) # levels(x) # [1] "H" "N" "No" "Y" "Yes" # NB: the offending levels are 1, 2, & 4 levels(x)[c(1,2,4)] <- c(NA, "No", "Yes") x # [1] Yes Yes Yes No No  # Levels: No Yes 

Vous pouvez utiliser la fonction ci-dessous pour combiner / réduire plusieurs facteurs:

 combofactor <- function(pattern_vector, replacement_vector, data) { levels <- levels(data) for (i in 1:length(pattern_vector)) levels[which(pattern_vector[i] == levels)] <- replacement_vector[i] levels(data) <- levels data } 

Exemple:

Initialiser x

 x <- factor(c(rep("Y",20),rep("N",20),rep("y",20), rep("yes",20),rep("Yes",20),rep("No",20))) 

Vérifier la structure

 str(x) # Factor w/ 6 levels "N","No","y","Y",..: 4 4 4 4 4 4 4 4 4 4 ... 

Utilisez la fonction:

 x_new <- combofactor(c("Y","N","y","yes"),c("Yes","No","Yes","Yes"),x) 

Revérifier la structure:

 str(x_new) # Factor w/ 2 levels "No","Yes": 2 2 2 2 2 2 2 2 2 2 ...