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?
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 facteurf
en approximativement son chiffre original valeurs,as.numeric(levels(f))[f]
est recommandé et légèrement plus efficace queas.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
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
.
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"
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.
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.
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