2010-08-05 21 views
422

Cuando convierto un factor en numérico o entero, obtengo los códigos de nivel subyacentes, no los valores como números.¿Cómo convertir un factor a un entero numérico sin pérdida de información?

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?

+0

Los niveles de un factor se almacenan como datos de tipo carácter de todos modos ('atributos (f)'), así que no creo que haya nada mal con 'as.numeric (pegar (f))'. Tal vez sería mejor pensar por qué (en el contexto específico) está obteniendo un factor en primer lugar, e intentar detenerlo. Por ejemplo, ¿el argumento 'dec' en' read.table' está configurado correctamente? – Bazz

Respuesta

510

Consulte la sección de Advertencia ?factor:

In particular, as.numeric applied to a factor is meaningless, and may happen by implicit coercion. To transform a factor f to approximately its original numeric values, as.numeric(levels(f))[f] is recommended and slightly more efficient than as.numeric(as.character(f)) .

El FAQ en I has similar advice.


¿Por qué es más eficiente que as.numeric(levels(f))[f]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 los valores numéricos en length(x), en lugar de en nlevels(x) valores. 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 temporizaciones

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 
+1

Para ver los tiempos, consulte esta respuesta: http://stackoverflow.com/questions/6979625/arithmetic-operations-on-r-factors/6980780#6980780 –

+1

Muchas gracias por su solución. ¿Puedo preguntar por qué el as.numeric (niveles (f)) [f] es más preciso y más rápido? Gracias. – Sam

+6

@Sam as.character (f) requiere una "búsqueda primitiva" para encontrar la función as.character.factor(), que se define como as.numeric (levels (f)) [f]. – Jonathan

62

R tiene un número de funciones de confort (sin papeles) para los factores de conversión:

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

Pero molesto, no hay nada para manejar el factor -> numérico conversión. Como una extensión de la respuesta de Joshua Ulrich, sugeriría para 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 se puede almacenar en el comienzo de la secuencia de comandos, o incluso mejor en su archivo .Rprofile.

+10

No hay nada que maneje la conversión de factor a entero (o numérico) porque se espera que 'como.entero (factor) 'devuelve los códigos enteros subyacentes (como se muestra en la sección de ejemplos de'? factor'). Probablemente esté bien definir esta función en su entorno global, pero puede causar problemas si realmente lo registra como un método S3. –

+0

Ese es un buen punto y estoy de acuerdo: una redefinición completa de la conversión factorial> numérica probablemente arruine muchas cosas. Me encontré escribiendo la pesada conversión 'factor-> numeric ** ** mucho antes de darme cuenta de que es de hecho una deficiencia de R: alguna función de conveniencia * debería * estar disponible ... llamándola' as.numeric.factor 'tiene sentido para mí, pero YMMV. – Jealie

+2

Si te encuentras haciendo ** mucho **, entonces debes hacer algo antes para evitarlo todo junto. –

7

Es posible solo en el caso en que las etiquetas de factor coincidan con los valores originales. Lo explicaré con un ejemplo.

asumir los datos es vector 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 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) que tiene solamente f disponible. Podemos ver que f contiene solo 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.

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.

15

La forma más fácil sería utilizar unfactor función del paquete de 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" 
+0

La función 'unfactor' primero se convierte en tipo de datos de caracteres y luego se convierte de nuevo a numérica. Escriba 'unfactor' en la consola y puede verlo en el medio de la función. Por lo tanto, realmente no ofrece una solución mejor que la que ya tenía el asker. – Bazz

+0

Una vez dicho esto, los niveles de un factor son de tipo de carácter de todos modos, por lo que no se pierde nada con este enfoque. – Bazz

+0

La función 'unfactor' se encarga de las cosas que no se pueden convertir a numéricas. Compruebe los ejemplos en 'help (" unfactor ")' –

7

Cada respuesta en este post no ha podido 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 -

library(magrittr) 
unclass(y2) %>% as.numeric 
    [1] 1 2 3 4 1 
+0

¿Está seguro de que tuvo un factor? Mire este ejemplo .y <-factor (c ("5", "15", "20", "2")); unclass (y)%>% as.numeric' Esto devuelve 4,1,3,2, no 5,15,20,2. Esto parece información incorrecta. – MrFlick

+0

Ok, esto es similar a lo que estaba tratando de hacer hoy: - y2 <-factor (c ("A", "B", "C", "D", "A")); as.numeric (niveles (y2)) [y2] [1] NA NA NA NA NA mensaje Advertencia: AN introducido por coacción mientras que unclass (y2)%>% as.numeric me dieron los resultados que necesario. – Indi

+0

Permítanme actualizar mi escenario en la respuesta que proporcioné – Indi

Cuestiones relacionadas