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"
¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top