Caída de los niveles de factor en una trama de datos con subjuegos
Pregunta
Tengo una trama de datos que contiene un factor. Cuando creo un subconjunto de esta trama de datos utilizando subset()
u otra función de indexación, se crea un nuevo marco de datos. Sin embargo, la variable de factor conserva todas sus niveles originales -. Incluso cuando no existan en la nueva trama de datos
Esto crea dolores de cabeza cuando se hace el trazado facetas o utilizando las funciones que dependen de los niveles de los factores.
¿Cuál es la forma más sucinta para eliminar los niveles de un factor en mi nueva trama de datos?
Aquí está mi ejemplo:
df <- data.frame(letters=letters[1:5],
numbers=seq(1:5))
levels(df$letters)
## [1] "a" "b" "c" "d" "e"
subdf <- subset(df, numbers <= 3)
## letters numbers
## 1 a 1
## 2 b 2
## 3 c 3
## but the levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"
Solución
Todo lo que tiene que hacer es aplicar el factor () a la variable de nuevo después de subconjuntos de:
> subdf$letters
[1] a b c
Levels: a b c d e
subdf$letters <- factor(subdf$letters)
> subdf$letters
[1] a b c
Levels: a b c
Editar
A partir del ejemplo factor de la página:
factor(ff) # drops the levels that do not occur
Para disminución de los niveles de todas las columnas de factores en una trama de datos, puede utilizar:
subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
Otros consejos
Desde R versión 2.12, hay una función droplevels()
.
levels(droplevels(subdf$letters))
Si no desea este comportamiento, no utilice factores, utilizar vectores de caracteres en su lugar. Creo que esto tiene más sentido que hacer las paces después. Intente lo siguiente antes de cargar sus datos con read.table
o read.csv
:
options(stringsAsFactors = FALSE)
La desventaja es que usted está limitado a ordenar por orden alfabético. (Reordenación es su amigo para las parcelas)
Es un problema conocido, y un remedio posible se proporciona por drop.levels()
en el paquete en el que su ejemplo se convierte en gdata
> drop.levels(subdf)
letters numbers
1 a 1
2 b 2
3 c 3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"
También existe la función dropUnusedLevels
en el Hmisc paquete . Sin embargo, sólo funciona alterando la [
operador de subgrupo y no es aplicable en este caso.
Como corolario, un enfoque directo sobre la función de cada columna es un as.factor(as.character(data))
simple:
> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"
Otra forma de hacer lo mismo pero con dplyr
library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
Editar
También funciona! Gracias a agenis
subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
En aras de la exhaustividad, ahora también hay fct_drop
en el paquete forcats
http: // forcats .tidyverse.org / referencia / fct_drop.html .
Se diferencia de droplevels
en la forma en que se ocupa de NA
:
f <- factor(c("a", "b", NA), exclude = NULL)
droplevels(f)
# [1] a b <NA>
# Levels: a b <NA>
forcats::fct_drop(f)
# [1] a b <NA>
# Levels: a b
Aquí hay otra manera, que creo que es equivalente al enfoque factor(..)
:
> df <- data.frame(let=letters[1:5], num=1:5)
> subdf <- df[df$num <= 3, ]
> subdf$let <- subdf$let[ , drop=TRUE]
> levels(subdf$let)
[1] "a" "b" "c"
En cuanto a los métodos droplevels
código en la fuente R se puede ver que envuelve a la función factor
. Eso significa que, básicamente, puede volver a crear la columna con la función factor
.
A continuación el camino data.table a caer los niveles de todas las columnas de factores.
library(data.table)
dt = data.table(letters=factor(letters[1:5]), numbers=seq(1:5))
levels(dt$letters)
#[1] "a" "b" "c" "d" "e"
subdt = dt[numbers <= 3]
levels(subdt$letters)
#[1] "a" "b" "c" "d" "e"
upd.cols = sapply(subdt, is.factor)
subdt[, names(subdt)[upd.cols] := lapply(.SD, factor), .SDcols = upd.cols]
levels(subdt$letters)
#[1] "a" "b" "c"
Esto es desagradable. Así es como yo suelo hacerlo, para evitar cargar otros paquetes:
levels(subdf$letters)<-c("a","b","c",NA,NA)
que se obtiene:
> subdf$letters
[1] a b c
Levels: a b c
Tenga en cuenta que los nuevos niveles reemplazarán lo ocupa su índice en los viejos niveles (letras subdf $), así que algo como:
levels(subdf$letters)<-c(NA,"a","c",NA,"b")
no va a funcionar.
Esto obviamente no es ideal cuando usted tiene un montón de niveles, pero para unos pocos, es rápido y fácil.
escribí funciones de utilidad para hacer esto. Ahora que sé acerca de drop.levels GData, se ve bastante similar. Aquí están (de aquí ):
present_levels <- function(x) intersect(levels(x), x)
trim_levels <- function(...) UseMethod("trim_levels")
trim_levels.factor <- function(x) factor(x, levels=present_levels(x))
trim_levels.data.frame <- function(x) {
for (n in names(x))
if (is.factor(x[,n]))
x[,n] = trim_levels(x[,n])
x
}
Esta es una manera de hacer eso
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
hilo muy interesante, me gusta especialmente la idea de solo factor de subselección de nuevo. Tuve el mismo problema antes y Acabo de convertir al personaje y luego de nuevo a los factores.
df <- data.frame(letters=letters[1:5],numbers=seq(1:5))
levels(df$letters)
## [1] "a" "b" "c" "d" "e"
subdf <- df[df$numbers <= 3]
subdf$letters<-factor(as.character(subdf$letters))
Por desgracia, no parece el factor () para funcionar cuando se utiliza rxDataStep de RevoScaleR. Lo hago en dos pasos: 1) Convertir al carácter y la tienda en la trama de datos externo temporal (.xdf). 2) Convertir de nuevo a los factores y tienda en la trama de datos externa definitiva. Esto elimina cualquier niveles de los factores no utilizados, sin cargar todos los datos en la memoria.
# Step 1) Converts to character, in temporary xdf file:
rxDataStep(inData = "input.xdf", outFile = "temp.xdf", transforms = list(VAR_X = as.character(VAR_X)), overwrite = T)
# Step 2) Converts back to factor:
rxDataStep(inData = "temp.xdf", outFile = "output.xdf", transforms = list(VAR_X = as.factor(VAR_X)), overwrite = T)
¿Ha probado la mayoría de los ejemplos aquí, si no todos, pero ninguno parece estar trabajando en mi caso. Después de luchar durante bastante tiempo he intentado usar as.character () en la columna del factor de cambiarlo a una col con las cadenas que parece trabajar muy bien.
No está seguro de los problemas de rendimiento.