我有一个包含因子的数据帧。当我使用子集或其他索引函数创建这个数据帧的子集时,就创建了一个新的数据帧。然而,因子变量保留其所有原始水平,即使它们不存在于新的数据框架中。

这在绘制面图或使用依赖于因子级别的函数时会导致问题。

在新的数据框架中从一个因子中移除级别最简洁的方法是什么?

这里有一个例子:

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"

当前回答

你所要做的就是在子集设置后再次应用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

EDIT

因子页的例子如下:

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)

其他回答

从R版本2.12开始,就有了一个droplevels()函数。

levels(droplevels(subdf$letters))

一个真正的droplevels函数是collapse::fdroplevels,它比droplevels快得多,并且不执行任何不必要的匹配或数值制表。例子:

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

这是令人讨厌的。我通常是这样做的,以避免加载其他包:

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")

不能工作。

当你有很多关卡时,这显然不太理想,但对于少数关卡来说,这是快速而简单的。

查看R源代码中的droplevels方法代码,您可以看到它包装为因子函数。这意味着你基本上可以用因子函数重新创建列。 下面是数据。从所有因子列中删除级别的表方式。

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"

另一种方法,但使用dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

编辑:

同样有效!感谢agenis

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)