variable transformar numerica factor example data convertir r casting r-faq

transformar - factor to numeric r



¿Cómo convertir un factor a entero / numérico sin pérdida de información? (6)

Cada respuesta en este post no generó resultados para mí, las NA se estaban generando.

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 de valor numérico a números, es para convertir factores categóricos a sus números de nivel correspondientes.

Cuando convierto un factor a un número 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?


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"


Puedes usar hablar::convert si tienes un marco de datos. La sintaxis es fácil:

Muestra df

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

Solución

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

te dio:

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

O si quieres que una columna sea entera y una numérica:

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

resultados en:

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


R tiene una serie de funciones de conveniencia (no documentadas) para los factores de conversión:

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

Pero de manera molesta, 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 principio de su script, o incluso mejor en su archivo .Rprofile .


Solo es posible en el caso en que las etiquetas de los factores coincidan con los valores originales. Lo explicaré con un ejemplo.

Supongamos que los datos son vectores x :

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

Ahora voy a crear un factor con cuatro etiquetas:

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

1) x es con el tipo doble, f es con el tipo entero. Esta es la primera pérdida inevitable de información. 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 solo contiene valores enteros 1, 2, 3, 4 y dos atributos: la lista de etiquetas ("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 etiquetas se hayan definido para todos los valores posibles en los datos originales.

Así que si necesitas los valores originales, debes mantenerlos. De lo contrario, existe una alta probabilidad de que no sea posible volver a ellos solo desde un factor.


Vea la sección de Advertencia del ?factor

En particular, as.numeric aplicado a un factor no tiene sentido, y puede ocurrir por coacció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 as.numeric(as.character(f)) .

El FAQ en R tiene consejos similares .

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

as.numeric(as.character(f)) es efectivamente as.numeric(levels(f)[f]) , por lo que está realizando la conversión a valores numéricos en length(x) , en lugar de en 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. Sin embargo, si realiza 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