提问人:medriscoll 提问时间:7/29/2009 最后编辑:Henrikmedriscoll 更新时间:12/29/2021 访问量:450142
删除子集数据框中未使用的因子水平
Drop unused factor levels in a subsetted data frame
问:
我有一个包含 .当我使用或其他索引函数创建此数据帧的子集时,将创建一个新的数据框。但是,该变量将保留其所有原始级别,即使它们在新数据帧中不存在。factor
subset
factor
这在执行分面绘图或使用依赖于因子水平的函数时会导致问题。
从新数据帧中的因子中删除水平的最简洁方法是什么?
下面是一个示例:
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
# all levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"
答:
> drop.levels(subdf)
letters numbers
1 a 1
2 b 2
3 c 3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"
Hmisc 包中也有该函数。但是,它只能通过更改子集运算符来工作,在这里不适用。dropUnusedLevels
[
作为推论,基于每列的直接方法很简单: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"
评论
reorder
drop.levels
FALSE
这是令人讨厌的。这是我通常的做法,以避免加载其他包:
levels(subdf$letters)<-c("a","b","c",NA,NA)
这能让你:
> subdf$letters
[1] a b c
Levels: a b c
请注意,新级别将替换旧级别(subdf$letters)中占据其索引的任何内容,因此如下所示:
levels(subdf$letters)<-c(NA,"a","c",NA,"b")
行不通。
当你有很多关卡时,这显然不是理想的,但对于少数人来说,它既快速又简单。
您应该做的就是在子集后再次将 factor() 应用于您的变量:
> 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
编辑
从因子页面示例:
factor(ff) # drops the levels that do not occur
要从数据帧中的所有因子列中删除水平,可以使用:
subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
评论
mydf <- droplevels(mydf)
如果您不希望出现这种行为,请不要使用因子,而是使用字符向量。我认为这比事后修补更有意义。在使用 或 加载数据之前,请尝试以下操作:read.table
read.csv
options(stringsAsFactors = FALSE)
缺点是您只能按字母顺序排列。(重新排序是你的情节朋友)
这是另一种方法,我认为它等同于这种方法: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"
评论
`[.factor`
drop
我编写了实用函数来做到这一点。现在我知道了 gdata 的 drop.levels,它看起来非常相似。他们在这里(从这里):
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
}
从 R 版本 2.12 开始,有一个函数。droplevels()
levels(droplevels(subdf$letters))
评论
factor()
droplevels
这是一种方法
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
评论
非常有趣的线程,我特别喜欢再次考虑子选择的想法。我以前遇到过类似的问题,我只是转换为角色,然后又转换回因子。
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))
评论
factor(as.chracter(...))
factor(...)
做同样事情的另一种方法,但dplyr
library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
编辑:
也有效!感谢 agenis
subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
查看 R 源代码中的方法代码,可以看到它包装到函数。这意味着您基本上可以使用函数重新创建列。
在data.table方法下,从所有因子列中删除水平。droplevels
factor
factor
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"
评论
data.table
for (j in names(DT)[sapply(DT, is.factor)]) set(DT, j = j, value = factor(DT[[j]]))
[.data.table
为了完整起见,现在包里也有 http://forcats.tidyverse.org/reference/fct_drop.html。fct_drop
forcats
它的不同之处在于它处理的方式:droplevels
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
不幸的是,使用 RevoScaleR 的 rxDataStep 时,factor() 似乎不起作用。我分两步完成: 1) 转换为字符并存储在临时外部数据帧 (.xdf) 中。 2)转换回因子并存储在确定的外部数据帧中。这样可以消除任何未使用的因子水平,而无需将所有数据加载到内存中。
# 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)
尝试了这里的大多数示例,如果不是全部,但似乎没有一个在我的情况下有效。 经过很长一段时间的挣扎,我尝试在因子列上使用 as.character() 将其更改为带有字符串的列,这似乎工作得很好。
不确定性能问题。
一个真正的 droplevels 函数比 快得多,并且不执行任何不必要的值匹配或制表,是 。例:droplevels
collapse::fdroplevels
library(collapse)
library(microbenchmark)
# wlddev data supplied in collapse, iso3c is a factor
data <- fsubset(wlddev, iso3c %!in% "USA")
microbenchmark(fdroplevels(data), droplevels(data), unit = "relative")
## Unit: relative
## expr min lq mean median uq max neval cld
## fdroplevels(data) 1.0 1.00000 1.00000 1.00000 1.00000 1.00000 100 a
## droplevels(data) 30.2 29.15873 24.54175 24.86147 22.11553 14.23274 100 b
感谢您发布此问题。但是,上述解决方案都不适合我。我为这个问题做了一个解决方法,分享它,以防其他人偶然发现这个问题:
对于包含具有零值的级别的所有列,可以先将这些列转换为类型,然后再将它们转换回 .factor
character
factors
对于上面发布的问题,只需添加以下代码行:
# Convert into character
subdf$letters = as.character(subdf$letters)
# Convert back into factor
subdf$letters = as.factor(subdf$letters)
# Verify the levels in the subset
levels(subdf$letters)
上一个:使用 ggplot2 的并排绘图
下一个:查找向量中多个元素的所有位置
评论