¿Cómo convertir un factor a entero \ numérico sin pérdida de información?

Cuando convierto un factor en numérico o entero, obtengo los códigos de nivel subyacentes, no los valores como números.

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 

Tengo que recurrir a paste para obtener los valores reales:

 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 

¿Hay una mejor manera de convertir un factor a numérico?

Ver la sección de Advertencia de ?factor :

En particular, as.numeric aplicación as.numeric a un factor no tiene sentido, y puede suceder por coerción implícita. Para transformar un factor f en aproximadamente sus valores numéricos originales, se as.numeric(levels(f))[f] y un poco más eficiente que como as.numeric(as.character(f)) .

Las preguntas frecuentes sobre R tienen consejos similares .


¿Por qué es tan as.numeric(levels(f))[f] más eficiente que como as.numeric(as.character(f)) ?

as.numeric(as.character(f)) es efectivamente como as.numeric(levels(f)[f]) , por lo que está realizando la conversión a valores numéricos de length(x) , en lugar de valores de nlevels(x) . La diferencia de velocidad será más evidente para vectores largos con pocos niveles. Si los valores son en su mayoría únicos, no habrá mucha diferencia en la velocidad. Independientemente de cómo realice la conversión, es poco probable que esta operación sea el cuello de botella en su código, así que no se preocupe demasiado por ello.


Algunos tiempos

 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 tiene varias funciones de conveniencia (indocumentadas) para factores de conversión:

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

Pero molestamente, no hay nada para manejar el factor -> conversión numérica . Como una extensión de la respuesta de Joshua Ulrich, sugeriría superar esta omisión con la definición de su propia función idiomática:

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

que puede almacenar al comienzo de su script, o incluso mejor en su archivo .Rprofile .

La forma más fácil sería usar la función unfactor del paquete varhandle

 unfactor(your_factor_variable) 

Este ejemplo puede ser un comienzo rápido:

 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" 

Cada respuesta en esta publicación no logró generar resultados para mí, se generaron NA.

 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 

Lo que funcionó para mí es esto:

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

Nota: esta respuesta particular no es para convertir factores numéricos en numéricos, sino para convertir factores categóricos en sus correspondientes números de nivel.

Es posible solo en el caso cuando las tags de los factores coinciden con los valores originales. Lo explicaré con un ejemplo.

Supongamos que los datos son vector x :

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

Ahora crearé un factor con cuatro tags:

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

1) x es con tipo double, f es con tipo entero. Esta es la primera pérdida de información inevitable. Los factores siempre se almacenan como enteros.

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

2) No es posible volver a los valores originales (10, 20, 30, 40) teniendo solo f disponible. Podemos ver que f contiene solo valores enteros 1, 2, 3, 4 y dos atributos: la lista de tags ("A", "B", "C", "D") y el atributo de clase "factor". Nada mas.

 > 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" 

Para volver a los valores originales, debemos conocer los valores de los niveles utilizados para crear el factor. En este caso c(10, 20, 30, 40) . Si conocemos los niveles originales (en el orden correcto), podemos volver a los valores originales.

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

Y esto funcionará solo en caso de que las tags se hayan definido para todos los valores posibles en los datos originales.

Entonces, si necesita los valores originales, debe conservarlos. De lo contrario, hay una gran probabilidad de que no sea posible volver a ellos solo por un factor.