96 votes

Les facteurs dans R : plus qu'une gêne ?

L'un des types de données de base de R est le facteur. D'après mon expérience, les facteurs sont une vraie plaie et je ne les utilise jamais. Je les convertis toujours en caractères. J'ai curieusement l'impression de manquer quelque chose.

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

49voto

Vince Points 3980

Vous devriez utiliser des facteurs. Oui, ils peuvent être pénibles, mais ma théorie est que 90% de la raison pour laquelle ils sont pénibles est qu'en read.table y read.csv l'argument stringsAsFactors = TRUE par défaut (et la plupart des utilisateurs passent à côté de cette subtilité). Je dis qu'ils sont utiles car les paquets d'ajustement de modèles comme lme4 utilisent les facteurs et les facteurs ordonnés pour ajuster les modèles de manière différentielle et déterminer le type de contrastes à utiliser. Et les paquets graphiques les utilisent également pour effectuer des regroupements. ggplot et la plupart des fonctions d'ajustement de modèles transforment les vecteurs de caractères en facteurs, le résultat est donc le même. 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  

Warning message:
In model.matrix.default(mt, mf, contrasts) :
  variable 'Species' converted to a factor
> 

Une chose délicate est l'ensemble drop=TRUE bit. Dans les vecteurs, cela fonctionne bien pour supprimer les niveaux des facteurs qui ne sont 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 les cadres de données, le comportement de [.data.frame() est différent : voir cet e-mail o ?[.data.frame (entre guillemets, ce que StackOverflow ne me laisse pas faire). En utilisant drop=TRUE sur les cadres de données ne fonctionne pas comme vous l'imaginez :

> 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 faire tomber les facteurs facilement avec :

> x <- subset(iris, Species == 'setosa', drop=TRUE)
> levels(x$Species)
[1] "setosa"     "versicolor" "virginica" 
> x$Species <- factor(x$Species)
> levels(x$Species)
[1] "setosa"

ou :

> x$Species <- x$Species[drop=TRUE]
> levels(x$Species)
[1] "setosa"

C'est la façon d'éviter que les niveaux que vous avez sélectionnés n'apparaissent dans les légendes de ggplot.

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

30voto

mdsumner Points 13001

Les facteurs commandés sont géniaux, s'il se trouve que j'aime les oranges et que je déteste les pommes mais que le raisin ne me dérange 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", ]

19voto

Brian Diggs Points 22433

A factor est le plus analogue à un type énuméré dans d'autres langages. Son utilisation appropriée est celle d'une variable qui ne peut prendre qu'une seule des valeurs prescrites. Dans ce cas, toutes les valeurs autorisées ne sont pas présentes dans un ensemble particulier de données et les niveaux "vides" le reflètent précisément.

Prenons quelques exemples. Pour certaines données qui ont été collectées dans tous les États-Unis, l'État doit être enregistré comme un facteur. Dans ce cas, le fait qu'aucun cas n'ait été collecté dans un état particulier est pertinent. Il aurait pu y avoir des données provenant de cet état, mais il se trouve (pour une raison quelconque, qui peut être une raison d'intérêt) qu'il n'y en a pas eu. Si des cas ont été collectés dans cet État, cela ne serait pas un facteur. Il n'existe pas d'ensemble préétabli de villes d'origine possibles. Si les données étaient collectées dans trois villes plutôt qu'au niveau national, la ville serait un facteur : il y a trois choix qui 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 factor tels que la possibilité de donner un ordre de tri arbitraire à un ensemble de chaînes de caractères, sont des caractéristiques secondaires utiles de l'outil de gestion de l'information. factor mais ne sont pas la raison de leur existence.

13voto

Farrel Points 2153

Les facteurs sont fantastiques lorsque l'on fait une analyse statistique et que l'on explore réellement les données. Cependant, avant cela, lorsque l'on lit, nettoie, dépanne, fusionne et manipule généralement les données, les facteurs sont une véritable plaie. Plus récemment, au cours des 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 très ennuyeux d'avoir 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 modifier les étiquettes, et qu'il existe également de merveilleuses façons de réorganiser les niveaux. Mon cerveau n'arrive pas à s'en souvenir et je dois les réapprendre chaque fois que je les utilise. Le recodage devrait être beaucoup plus facile qu'il ne l'est.

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

6voto

Joshua Ulrich Points 68776

Quel titre sarcastique !

Je crois que de nombreuses fonctions d'estimation vous permettent d'utiliser des facteurs pour définir facilement des variables fictives... mais je ne les utilise pas pour cela.

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X