web-dev-qa-db-fra.com

Facteurs en R: plus qu'une gêne?

L'un des types de données de base dans R est les facteurs. Dans mon expérience, les facteurs sont essentiellement une douleur et je ne les utilise jamais. Je convertis toujours en personnages. Je sens étrangement que je manque quelque chose. 

Existe-t-il des exemples importants de fonctions qui utilisent des facteurs en tant que variables de regroupement où le type de données de facteur devient nécessaire? Existe-t-il des circonstances spécifiques dans lesquelles je devrais utiliser des facteurs?

92
JD Long

Vous devriez utiliser des facteurs. Oui, cela peut être une douleur, mais ma théorie est que 90% de leur raison est parce que dans read.table et read.csv, l'argument stringsAsFactors = TRUE par défaut (et la plupart des utilisateurs manquent cette subtilité). Je dis qu’ils sont utiles car les packages d’ajustement de modèle tels que lme4 utilisent des facteurs et des facteurs ordonnés pour ajuster différemment les modèles et déterminer le type de contraste à utiliser. Et les packages graphiques les utilisent également pour grouper par. ggplot et la plupart des fonctions d'ajustement de modèle contraignent les vecteurs de caractère à des facteurs. Le résultat est donc identique. Cependant, vous vous retrouvez avec des avertissements dans votre code:

lm(Petal.Length ~ -1 + Species, data=iris)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  

iris.alt <- iris
iris.alt$Species <- as.character(iris.alt$Species)
lm(Petal.Length ~ -1 + Species, data=iris.alt)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris.alt)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  

Message d'avertissement: Dans model.matrix.default(mt, mf, contrasts):

variable Species convertie en factor

Une chose délicate est le drop=TRUE entier. Dans les vecteurs, cela fonctionne bien pour supprimer les niveaux de facteurs qui ne figurent pas dans les données. Par exemple:

s <- iris$Species
s[s == 'setosa', drop=TRUE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa
s[s == 'setosa', drop=FALSE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica

Cependant, avec data.frames, le comportement de [.data.frame() est différent: voir cet email ou ?"[.data.frame". Utiliser drop=TRUE sur data.frames ne fonctionne pas comme vous pouvez l'imaginer:

x <- subset(iris, Species == 'setosa', drop=TRUE)  # susbetting with [ behaves the same way
x$Species
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica

Heureusement, vous pouvez facilement supprimer des facteurs avec droplevels() pour supprimer les niveaux de facteurs inutilisés pour un facteur individuel ou pour chaque facteur dans un data.frame (à partir de R 2.12):

x <- subset(iris, Species == 'setosa')
levels(x$Species)
# [1] "setosa"     "versicolor" "virginica" 
x <- droplevels(x)
levels(x$Species)
# [1] "setosa"

Voici comment garder les niveaux que vous avez sélectionnés dans les légendes ggplot.

En interne, factors sont des entiers avec un vecteur de caractère de niveau d'attribut (voir attributes(iris$Species) et class(attributes(iris$Species)$levels)), qui est propre. Si vous deviez modifier un nom de niveau (et que vous utilisiez des chaînes de caractères), ce serait une opération moins efficace beaucoup. Et je change beaucoup de noms de niveaux, en particulier pour les légendes ggplot. Si vous simulez des facteurs avec des vecteurs de caractère, vous risquez de ne modifier qu'un élément et de créer accidentellement un nouveau niveau.

47
Vince

les facteurs ordonnés sont géniaux, si j'aime les oranges et déteste les pommes, mais que les raisins ne me dérangent pas, je n'ai pas besoin de gérer un indice bizarre pour le dire: 

d <- data.frame(x = rnorm(20), f = sample(c("apples", "oranges", "grapes"), 20, replace = TRUE, prob = c(0.5, 0.25, 0.25)))
d$f <- ordered(d$f, c("apples", "grapes", "oranges"))
d[d$f >= "grapes", ]
29
mdsumner

Une factor est très analogue à un type énuméré dans d'autres langues. Son utilisation appropriée est pour une variable qui ne peut prendre qu’un des ensembles de valeurs prescrits. Dans ces cas, toutes les valeurs autorisées possibles ne peuvent pas être présentes dans un ensemble de données particulier et les niveaux "vides" le reflètent avec précision.

Considérez quelques exemples. Pour certaines données qui ont été collectées à travers les États-Unis, l'état doit être enregistré en tant que facteur. Dans ce cas, le fait qu'aucun cas n'ait été collecté auprès d'un État particulier est pertinent. Il aurait pu y avoir des données de cet état, mais il est arrivé (pour une raison quelconque, qui peut être une raison d'intérêt) de ne pas l'être. Si la ville natale était collectée, cela ne serait pas un facteur. Il n'y a pas un ensemble prédéfini de villes natales possibles. Si les données étaient collectées dans trois villes plutôt qu'au niveau national, la ville serait un facteur: trois choix ont été donnés au départ et si aucun cas/donnée pertinent n’a été trouvé dans l’une de ces trois villes, c’est pertinent.

D'autres aspects de factors, tels que la possibilité de donner un ordre de tri arbitraire à un ensemble de chaînes, sont des caractéristiques secondaires utiles de factors, mais ne sont pas la raison de leur existence. 

18
Brian Diggs

Les facteurs sont fantastiques quand on fait une analyse statistique et qu'on explore réellement les données. Cependant, avant cela, quand on lit, nettoie, dépanne, fusionne et manipule généralement les données, les facteurs sont une douleur totale. Plus récemment, comme ces dernières années, de nombreuses fonctions ont été améliorées pour mieux gérer les facteurs. Par exemple, Rbind joue bien avec eux. Je trouve toujours comme une nuisance totale d’avoir laissé des niveaux vides après une fonction de sous-ensemble. 

#drop a whole bunch of unused levels from a whole bunch of columns that are factors using gdata
require(gdata)
drop.levels(dataframe)

Je sais qu'il est facile de recoder les niveaux d'un facteur et de réorganiser les étiquettes. Il existe également de merveilleuses façons de réorganiser les niveaux. Mon cerveau ne peut tout simplement pas s'en souvenir et je dois le réapprendre chaque fois que je l'utilise. Le recodage devrait être beaucoup plus simple que ça. 

Les fonctions de chaîne de R sont assez faciles et logiques à utiliser. Ainsi, lors de la manipulation, je préfère généralement les personnages aux facteurs. 

12
Farrel

Quel titre sournois!

Je crois que de nombreuses fonctions d’estimation vous permettent d’utiliser des facteurs pour définir facilement des variables muettes ...

Je les utilise lorsque j'ai de très gros vecteurs de caractères avec peu d'observations uniques. Cela peut réduire la consommation de mémoire, en particulier si les chaînes du vecteur de caractères sont plus longues.

PS - Je plaisante à propos du titre. J'ai vu ton Tweet. ;-)

7
Joshua Ulrich

Les facteurs constituent un excellent moteur de création de badges "à cas unique". Je l'ai recréé si souvent, et malgré quelques rides de temps en temps, elles sont extrêmement puissantes. 

library(dplyr)
d <- tibble(x = sample(letters[1:10], 20, replace = TRUE))

## normalize this table into an indexed value across two tables
id <- tibble(x_u = sort(unique(d$x))) %>% mutate(x_i = row_number())
di <- tibble(x_i = as.integer(factor(d$x)))


## reconstruct d$x when needed
d2 <- inner_join(di, id) %>% transmute(x = x_u)
identical(d, d2)
## [1] TRUE

S'il y a une meilleure façon de faire cette tâche, j'aimerais bien la voir, je ne vois pas cette capacité de factor discutée. 

0
mdsumner