Quel est le moyen le plus efficace (c.-à-d. Efficace/approprié) de nettoyer un facteur contenant plusieurs niveaux à réduire? 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" à "Non":
## Given:
x <- c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA
## expectedOutput
[1] Yes Yes Yes No No <NA>
Levels: Yes No # <~~ NOTICE ONLY **TWO** LEVELS
Une option consiste bien entendu à nettoyer les chaînes avant d'utiliser manuellement sub
et ses amis.
Une autre méthode consiste à autoriser les étiquettes en double, puis à les supprimer.
## Duplicate levels ==> "Warning: deprecated"
x.f <- 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(x.f)
droplevels(x.f)
Cependant, existe-t-il un moyen plus efficace ?
Bien que je sache que les arguments levels
et labels
devraient être des vecteurs, j'ai essayé des listes et des listes nommées ainsi que des vecteurs nommés pour voir ce qui se passe.
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, avec les noms correspondant aux noms souhaités des niveaux et les éléments correspondant aux 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 <NA>
## Levels: Yes No
Comme mentionné dans la documentation levels
; voir aussi les exemples ici.
valeur: pour la méthode 'factor', a vecteur de chaînes de caractères dont la longueur est au moins égale au nombre des niveaux de 'x', ou une liste nommée spécifiant comment renommer les niveaux.
Cela peut également être fait en une ligne, comme Marek le fait ici: https://stackoverflow.com/a/10432263/210673 ; la sorcerie levels<-
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 <NA>
Levels: Yes No
La question étant intitulée Niveaux de facteur de nettoyage (regroupant plusieurs niveaux/étiquettes), le paquetage forcats
doit également être mentionné ici, par souci d'exhaustivité. forcats
est apparu sur CRAN en août 2016.
Il existe plusieurs fonctions pratiques pour nettoyer les niveaux de facteurs:
x <- c("Y", "Y", "Yes", "N", "No", "H")
library(forcats)
fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
#[1] Yes Yes Yes No No <NA>
#Levels: No Yes
fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
#[1] Yes Yes Yes No No <NA>
#Levels: No Yes
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 <NA>
#Levels: No Yes
Notez que fct_relabel()
fonctionne avec des niveaux de facteurs, il attend donc un facteur comme premier argument. Les deux autres fonctions, fct_collapse()
et fct_recode()
, acceptent également un vecteur character qui est une fonctionnalité non documentée.
Le résultat attendu donné par le PO est
[1] Yes Yes Yes No No <NA>
Levels: Yes No
Ici, les niveaux sont classés comme ils apparaissent dans x
, ce qui diffère de la valeur par défaut (?factor
: Les niveaux d'un facteur sont triés par défaut).
Pour être conforme au résultat attendu, vous pouvez utiliser fct_inorder()
before en réduisant 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 maintenant la sortie attendue avec les niveaux dans le même ordre.
Peut-être qu'un vecteur nommé comme clé pourrait être utile:
> factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x]))
[1] Yes Yes Yes No No <NA>
Levels: No Yes
Cela ressemble beaucoup à votre dernière tentative ... mais celle-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 <NA>
# Levels: No Yes
# or...
library(data.table)
setDT(fmap)[x, on=.(values), ind ]
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
Je préfère cette façon, car elle laisse derrière elle un objet facilement inspecté qui résume la carte; et le code data.table ressemble à n'importe quelle autre jointure dans cette syntaxe.
Bien sûr, si vous ne voulez pas qu'un objet 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 <NA>
# Levels: No Yes
Je ne connais pas votre cas d'utilisation réel, mais strtrim
serait-il utile ici ...
factor( strtrim( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) )
#[1] Yes Yes Yes No No <NA>
#Levels: Yes No
J'ajoute cette réponse pour démontrer que la réponse acceptée fonctionne sur un facteur spécifique dans un cadre de données, car ce n'était pas évident pour moi au départ (bien que cela aurait probablement dû l'être).
levels(df$var1)
# "0" "1" "Z"
summary(df$var1)
# 0 1 Z
# 7012 2507 8
levels(df$var1) <- list("0"=c("Z", "0"), "1"=c("1"))
levels(df$var1)
# "0" "1"
summary(df$var1)
# 0 1
# 7020 2507
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 <NA>
# 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érifiez 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 ...
Notons d'abord que dans ce cas particulier, nous pouvons utiliser une correspondance partielle:
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c("Yes","No")
x <- factor(y[pmatch(x,y,duplicates.ok = TRUE)])
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
Dans un cas plus général j'irais avec dplyr::recode
:
library(dplyr)
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c(Y="Yes",N="No")
x <- recode(x,!!!y)
x <- factor(x,y)
# [1] Yes Yes Yes No No <NA>
# Levels: Yes No
Légèrement modifié si le point de départ est un facteur:
x <- factor(c("Y", "Y", "Yes", "N", "No", "H"))
y <- c(Y="Yes",N="No")
x <- recode_factor(x,!!!y)
x <- factor(x,y)
# [1] Yes Yes Yes No No <NA>
# Levels: Yes No