web-dev-qa-db-fra.com

Comment convertir un facteur en entier\numérique sans perte d'information?

Lorsque je convertis un facteur en numérique ou en entier, j'obtiens les codes de niveau sous-jacents et non les valeurs sous forme de nombres.

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218

as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

Je dois recourir à paste pour obtenir les vraies valeurs:

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

Existe-t-il un meilleur moyen de convertir un facteur en numérique?

507
Adam SO

Voir la section Avertissement de ?factor :

En particulier, as.numeric s’applique à un facteur n'a pas de sens et peut se produire par la contrainte implicite. À transformer un facteur f en approximativement son chiffre original valeurs, as.numeric(levels(f))[f] est recommandé et légèrement plus efficace que as.numeric(as.character(f)).

La FAQ sur R a le même conseil .


Pourquoi as.numeric(levels(f))[f] est-il plus efficace que as.numeric(as.character(f))?

as.numeric(as.character(f)) est effectivement as.numeric(levels(f)[f]); vous effectuez donc la conversion en numérique sur les valeurs length(x), plutôt que sur nlevels(x). La différence de vitesse sera plus apparente pour les vecteurs longs avec peu de niveaux. Si les valeurs sont pour la plupart uniques, il n'y aura pas beaucoup de différence de vitesse. Quelle que soit la façon dont vous effectuez la conversion, il est peu probable que cette opération constitue le goulot d'étranglement dans votre code, alors ne vous en faites pas.


Quelques timings

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05
610
Joshua Ulrich

R dispose d’un certain nombre de fonctions pratiques (non documentées) pour la conversion de facteurs:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor
  • ...

Mais ennuyeusement, rien ne permet de gérer la conversion factor -> numérique. Dans le prolongement de la réponse de Joshua Ulrich, je suggérerais de surmonter cette omission en définissant votre propre fonction idiomatique:

as.numeric.factor <- function(x) {as.numeric(levels(x))[x]}

que vous pouvez stocker au début de votre script, ou mieux dans votre fichier .Rprofile .

74
Jealie

Le moyen le plus simple consiste à utiliser la fonction unfactor du package varhandle

unfactor(your_factor_variable)

Cet exemple peut être un début rapide:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)

class(x)  # -> "character"
class(y)  # -> "numeric"

x <- factor(x)
y <- factor(y)

class(x)  # -> "factor"
class(y)  # -> "factor"

library(varhandle)
x <- unfactor(x)
y <- unfactor(y)

class(x)  # -> "character"
class(y)  # -> "numeric"
29
Mehrad Mahmoudian

Chaque réponse dans cet article n'a pas réussi à générer des résultats pour moi, des NA ont été générés.

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

Ce qui a fonctionné pour moi est ceci - 

as.integer(y2)
# [1] 1 2 3 4 1

Remarque: cette réponse est pas pour convertir les facteurs numériques en valeurs numériques, mais pour convertir les facteurs catégoriels en nombres correspondants.

16
Indi

Il est possible seulement dans le cas où les étiquettes de facteurs correspondent aux valeurs d'origine. Je vais l'expliquer avec un exemple.

Supposons que les données sont le vecteur x:

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

Maintenant, je vais créer un facteur avec quatre étiquettes:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) x est de type double, f est de type entier. C’est la première perte inévitable d’informations. Les facteurs sont toujours stockés sous forme d'entiers.

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) Il n'est pas possible de revenir aux valeurs d'origine (10, 20, 30, 40) lorsque seul f est disponible. Nous pouvons voir que f ne contient que les valeurs entières 1, 2, 3, 4 et deux attributs - la liste des étiquettes ("A", "B", "C", "D") et l'attribut de classe "facteur". Rien de plus.

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

Pour revenir aux valeurs d'origine, nous devons connaître les valeurs des niveaux utilisés pour créer le facteur. Dans ce cas, c(10, 20, 30, 40). Si nous connaissons les niveaux d'origine (dans le bon ordre), nous pouvons revenir aux valeurs d'origine.

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

Et cela ne fonctionnera que dans le cas où des étiquettes ont été définies pour toutes les valeurs possibles des données d'origine.

Donc, si vous avez besoin des valeurs d'origine, vous devez les conserver. Sinon, il y a de fortes chances qu'il ne soit pas possible d'y revenir uniquement d'un facteur.

8
djhurio

Vous pouvez utiliser hablar::convert si vous avez un cadre de données. La syntaxe est simple:

Exemple df

library(hablar)
library(dplyr)

df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

Solution

df %>% 
  convert(num(a, b))

vous donne:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

Ou si vous voulez une colonne entière et une numérique:

df %>% 
  convert(int(a),
          num(b))

résulte en:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30
0
davsjob