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

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

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

这里有一个例子:

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"

当前回答

为了完整起见,现在在forcats包http://forcats.tidyverse.org/reference/fct_drop.html中也有fct_drop。

它与液滴处理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

其他回答

不幸的是,factor()在使用RevoScaleR的rxDataStep时似乎不起作用。我分两步做: 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)

为了完整起见,现在在forcats包http://forcats.tidyverse.org/reference/fct_drop.html中也有fct_drop。

它与液滴处理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

查看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"

这是一个已知的问题,您的示例所在的gdata包中的drop.levels()提供了一个可能的补救措施

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

如果你不想要这种行为,不要使用因子,而是使用字符向量。我觉得这比事后修补要好得多。在用read加载数据之前,请尝试以下操作。表或read.csv:

options(stringsAsFactors = FALSE)

缺点是你只能按字母排序。(重新排序是你的朋友情节)