variable recodificar numericas numerica medicina factores factor estadistica convertir categoricas r r-factor

recodificar - factores en r



Por qué usar as.factor() en lugar de simplemente factor() (1)

Hace poco vi a Matt Dowle escribir un código con as.factor() , específicamente

for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))

en un comentario a esta respuesta .

Usé este fragmento, pero necesitaba establecer explícitamente los niveles de los factores para asegurarme de que los niveles aparecían en el orden deseado, así que tuve que cambiar

as.factor(dt[[col]])

a

factor(dt[[col]], levels = my_levels)

Esto me hizo pensar: ¿qué (si hay alguno) es el beneficio de usar as.factor() versus solo factor() ?


as.factor es un contenedor para factor , pero permite un retorno rápido si el vector de entrada ya es un factor:

function (x) { if (is.factor(x)) x else if (!is.object(x) && is.integer(x)) { levels <- sort(unique.default(x)) f <- match(x, levels) levels(f) <- as.character(levels) if (!is.null(nx <- names(x))) names(f) <- nx class(f) <- "factor" f } else factor(x) }

Comentario de : no es una mera envoltura, ya que este "retorno rápido" dejará los niveles de factores tal como están, mientras que factor() no:

f = factor("a", levels = c("a", "b")) #[1] a #Levels: a b factor(f) #[1] a #Levels: a as.factor(f) #[1] a #Levels: a b

Respuesta expandida dos años después, que incluye lo siguiente:

  • ¿Qué dice el manual?
  • Rendimiento: as.factor > cuando la entrada es un factor
  • Rendimiento: as.factor > cuando la entrada es un número entero
  • Niveles no utilizados o niveles de NA
  • Precaución al usar las funciones de agrupamiento por R: observe los niveles de NA o no utilizados

¿Qué dice el manual?

La documentación para ?factor menciona lo siguiente:

‘factor(x, exclude = NULL)’ applied to a factor without ‘NA’s is a no-operation unless there are unused levels: in that case, a factor with the reduced level set is returned. ‘as.factor’ coerces its argument to a factor. It is an abbreviated (sometimes faster) form of ‘factor’.

Rendimiento: as.factor > cuando la entrada es un factor

La palabra "no-operación" es un poco ambigua. No lo tome como "no hacer nada"; de hecho, significa "hacer muchas cosas pero básicamente no cambiar nada". Aquí hay un ejemplo:

set.seed(0) ## a randomized long factor with 1e+6 levels, each repeated 10 times f <- sample(gl(1e+6, 10)) system.time(f1 <- factor(f)) ## default: exclude = NA # user system elapsed # 7.640 0.216 7.887 system.time(f2 <- factor(f, exclude = NULL)) # user system elapsed # 7.764 0.028 7.791 system.time(f3 <- as.factor(f)) # user system elapsed # 0 0 0 identical(f, f1) #[1] TRUE identical(f, f2) #[1] TRUE identical(f, f3) #[1] TRUE

as.factor da un retorno rápido, pero el factor no es un verdadero "no-op". Vamos a factor perfil para ver lo que ha hecho.

Rprof("factor.out") f1 <- factor(f) Rprof(NULL) summaryRprof("factor.out")[c(1, 4)] #$by.self # self.time self.pct total.time total.pct #"factor" 4.70 58.90 7.98 100.00 #"unique.default" 1.30 16.29 4.42 55.39 #"as.character" 1.18 14.79 1.84 23.06 #"as.character.factor" 0.66 8.27 0.66 8.27 #"order" 0.08 1.00 0.08 1.00 #"unique" 0.06 0.75 4.54 56.89 # #$sampling.time #[1] 7.98

Primero sort los valores unique del vector de entrada f , luego convierte f en un vector de caracteres, finalmente usa el factor para forzar al vector de caracteres a volver a un factor. Aquí está el código fuente del factor de confirmación.

function (x = character(), levels, labels = levels, exclude = NA, ordered = is.ordered(x), nmax = NA) { if (is.null(x)) x <- character() nx <- names(x) if (missing(levels)) { y <- unique(x, nmax = nmax) ind <- sort.list(y) levels <- unique(as.character(y)[ind]) } force(ordered) if (!is.character(x)) x <- as.character(x) levels <- levels[is.na(match(levels, exclude))] f <- match(x, levels) if (!is.null(nx)) names(f) <- nx nl <- length(labels) nL <- length(levels) if (!any(nl == c(1L, nL))) stop(gettextf("invalid ''labels''; length %d should be 1 or %d", nl, nL), domain = NA) levels(f) <- if (nl == nL) as.character(labels) else paste0(labels, seq_along(levels)) class(f) <- c(if (ordered) "ordered", "factor") f }

Por lo tanto, el factor función está realmente diseñado para funcionar con un vector de caracteres y aplica as.character a su entrada para garantizar eso. Al menos podemos aprender dos problemas relacionados con el rendimiento desde arriba:

  1. Para un marco de datos DF , lapply(DF, as.factor) es mucho más rápido que lapply(DF, factor) para la conversión de tipo, si muchas columnas son factores fácilmente.
  2. Ese factor función es lento puede explicar por qué algunas funciones R importantes son lentas, por ejemplo, table : R: función de tabla sorprendentemente lenta

Rendimiento: as.factor > cuando la entrada es un número entero

Una variable de factor es el pariente más cercano de una variable entera.

unclass(gl(2, 2, labels = letters[1:2])) #[1] 1 1 2 2 #attr(,"levels") #[1] "a" "b" storage.mode(gl(2, 2, labels = letters[1:2])) #[1] "integer"

Esto significa que convertir un número entero a un factor es más fácil que convertir un número / carácter a un factor. as.factor simplemente se ocupa de esto.

x <- sample.int(1e+6, 1e+7, TRUE) system.time(as.factor(x)) # user system elapsed # 4.592 0.252 4.845 system.time(factor(x)) # user system elapsed # 22.236 0.264 22.659

Niveles no utilizados o niveles de NA

Ahora veamos algunos ejemplos sobre el factor y la influencia del factor en los niveles de los factores (si la entrada ya es un factor). ha dado uno con nivel de factor no utilizado, le proporcionaré uno con nivel de NA .

f <- factor(c(1, NA), exclude = NULL) #[1] 1 <NA> #Levels: 1 <NA> as.factor(f) #[1] 1 <NA> #Levels: 1 <NA> factor(f, exclude = NULL) #[1] 1 <NA> #Levels: 1 <NA> factor(f) #[1] 1 <NA> #Levels: 1

Hay una función (genérica) droplevels que se puede utilizar para soltar niveles no utilizados de un factor. Pero los niveles de NA no se pueden eliminar por defecto.

## "factor" method of `droplevels` droplevels.factor #function (x, exclude = if (anyNA(levels(x))) NULL else NA, ...) #factor(x, exclude = exclude) droplevels(f) #[1] 1 <NA> #Levels: 1 <NA> droplevels(f, exclude = NA) #[1] 1 <NA> #Levels: 1

Precaución al usar las funciones de agrupamiento por R: observe los niveles de NA o no utilizados

Las funciones R que realizan operaciones de agrupamiento por split , como split , esperan que proporcionemos variables de factor como variables "por". Pero a menudo simplemente proporcionamos caracteres o variables numéricas. Así que internamente, estas funciones necesitan convertirlas en factores y probablemente la mayoría de ellas usaría como as.factor (al menos esto es así para split.default y tapply ). La función de table ve como una excepción y yo veo factor lugar de as.factor factor en el interior. Puede haber alguna consideración especial que desafortunadamente no sea obvia para mí cuando inspecciono su código fuente.

Como la mayoría de las funciones R as.factor utilizan como as.factor , si se les asigna un factor con niveles NA o no utilizados, dicho grupo aparecerá en el resultado.

x <- c(1, 2) f <- factor(letters[1:2], levels = letters[1:3]) split(x, f) #$a #[1] 1 # #$b #[1] 2 # #$c #numeric(0) tapply(x, f, FUN = mean) # a b c # 1 2 NA

Curiosamente, aunque la table no se basa en as.factor , también conserva esos niveles no utilizados:

table(f) #a b c #1 1 0

Algunas veces este tipo de comportamiento puede ser no deseado. Un ejemplo clásico es barplot(table(f)) :

Si esto no es realmente deseado, tenemos que eliminar manualmente los niveles no utilizados o de NA de nuestra variable de factor, utilizando droplevels o factor .

Insinuación:

  1. split tiene una drop argumento que por defecto es FALSE por as.factor tanto, as.factor se usa el as.factor ; por drop = TRUE factor función drop = TRUE se utiliza en su lugar.
  2. aggregate depende de split , por lo que también tiene un argumento drop y su valor predeterminado es TRUE .
  3. tapply no tiene drop aunque también se basa en split . En particular, la documentación ?tapply dice que as.factor es (siempre) utilizado.