trabajo - fuerza de choque formula
Niveles de factor de caída en un marco de datos subcontratados (12)
Aquí hay otra forma, que creo que es equivalente al enfoque del 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"
Tengo un marco de datos que contiene un factor. Cuando creo un subconjunto de este marco de datos utilizando un subset()
u otra función de indexación, se crea un nuevo marco de datos. Sin embargo, la variable factor conserva todos sus niveles originales, incluso cuando no existen en el nuevo marco de datos.
Esto crea dolores de cabeza al realizar trazados de facetas o usar funciones que dependen de los niveles de factores.
¿Cuál es la forma más sucinta de eliminar niveles de un factor en mi nuevo marco 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"
Aquí hay una manera de hacer eso.
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
Desde la versión R 2.12, hay una función droplevels()
.
levels(droplevels(subdf$letters))
Es un problema conocido y drop.levels()
proporciona un posible remedio en el paquete gdata donde su ejemplo se convierte en
> 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 está la función dropUnusedLevels
en el paquete Hmisc . Sin embargo, solo funciona modificando el operador del subconjunto [
y no se aplica aquí.
Como corolario, un enfoque directo por columna es un simple as.factor(as.character(data))
:
> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"
Escribí funciones de utilidad para hacer esto. Ahora que sé acerca de los niveles de caída de gdata, parece bastante similar. Aquí están (desde here ):
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
}
Esto es odioso. Así es como normalmente lo hago, para evitar cargar otros paquetes:
levels(subdf$letters)<-c("a","b","c",NA,NA)
lo que te pone
> subdf$letters
[1] a b c
Levels: a b c
Tenga en cuenta que los nuevos niveles reemplazarán lo que ocupe su índice en los niveles anteriores (subdf $ letters), por lo que algo como:
levels(subdf$letters)<-c(NA,"a","c",NA,"b")
no funcionará
Obviamente, esto no es ideal cuando tienes muchos niveles, pero para unos pocos, es rápido y fácil.
Hilo muy interesante, me gustó especialmente la idea de factorizar la subselección nuevamente. Tuve un problema similar antes y me convertí en personaje y luego volví al factor.
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))
Mirando el código de los métodos de las personas en la fuente R, puede ver que se ajusta a la función de factor
. Eso significa que básicamente puedes recrear la columna con la función de factor
.
Debajo de la tabla data.table para eliminar 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"
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)
Para completar, ahora también hay fct_drop
en el paquete forcats
http://forcats.tidyverse.org/reference/fct_drop.html .
Se diferencia de las droplevels
en la forma en que trata con 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
Si no quieres este comportamiento, no uses factores, usa vectores de caracteres. Creo que esto tiene más sentido que arreglar las cosas después. Intente lo siguiente antes de cargar sus datos con read.table
o read.csv
:
options(stringsAsFactors = FALSE)
La desventaja es que estás restringido al orden alfabético. (reordenar es tu amigo para parcelas)
Todo lo que debe hacer es aplicar factor () a su variable de nuevo después de subcontratar:
> 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
Del ejemplo de la página de factores:
factor(ff) # drops the levels that do not occur
Para eliminar niveles de todas las columnas de factores en un marco de datos, puede usar:
subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)