子集化数据帧中的丢弃因子级别

med*_*oll 510 r dataframe r-faq r-factor

我有一个包含因子的数据框.当我使用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"
Run Code Online (Sandbox Code Playgroud)

Rom*_*rik 478

自R版本2.12起,就有了一个droplevels()功能.

levels(droplevels(subdf$letters))
Run Code Online (Sandbox Code Playgroud)

  • 这种方法优于使用`factor()`的一个优点是没有必要修改原始数据帧或创建新的持久数据帧.我可以围绕子集化数据框包装`droplevels`并将其用作格函数的数据参数,并且将正确处理组. (5认同)

hat*_*rix 393

您需要做的就是在子集化后再次将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
Run Code Online (Sandbox Code Playgroud)

编辑

从因子页面示例:

factor(ff)      # drops the levels that do not occur
Run Code Online (Sandbox Code Playgroud)

要从数据框中的所有因子列中删除级别,您可以使用:

subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
Run Code Online (Sandbox Code Playgroud)

  • 这对于一次性来说很好,但是在具有大量列的data.frame中,你可以在每个列上做这个因素...导致需要一个函数,如drop.levels()来自gdata. (21认同)
  • 作为副作用,函数将数据帧转换为列表,因此下面的RomanLuštrik和Tommy O'Dell建议的`mydf < - droplevels(mydf)`解决方案更可取. (8认同)
  • 我看到......但从用户的角度来看,很快就会编写类似subdf [] < - lapply(subdf,function(x)if(is.factor(x))factor(x)else x)... drop.levels()在大数据集的计算或更高效率上更高效?(我猜想,必须在一个巨大数据帧的for循环中重写上面的行.) (6认同)

had*_*ley 41

如果您不想要此行为,请不要使用因子,而是使用字符向量.我认为这比后续修补更有意义.在使用read.table或加载数据之前,请尝试以下操作read.csv:

options(stringsAsFactors = FALSE)
Run Code Online (Sandbox Code Playgroud)

缺点是您只能按字母顺序排序.(重新排序是您的情节的朋友)

  • 你也可以做read.csv(file ='foo.csv',as.is = T). (8认同)

Dir*_*tel 35

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

> drop.levels(subdf)
  letters numbers
1       a       1
2       b       2
3       c       3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

  • `drop.levels`函数的`reorder`参数值得一提:如果你必须保留你的因子的原始顺序,那么使用它为'FALSE`值. (4认同)

Prr*_*dep 22

另一种做同样但有的方法 dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
Run Code Online (Sandbox Code Playgroud)

编辑:

还有效!感谢agenis

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
Run Code Online (Sandbox Code Playgroud)


Aur*_*èle 15

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

它与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
Run Code Online (Sandbox Code Playgroud)


ars*_*ars 13

这是另一种方式,我认为这相当于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"
Run Code Online (Sandbox Code Playgroud)


jan*_*cki 7

查看R源代码中droplevels方法代码,您可以看到它包含在factor函数中.这意味着您基本上可以使用factor函数重新创建列.
在data.table方式下面,从所有因子列中删除级别.

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"
Run Code Online (Sandbox Code Playgroud)


Mat*_*ker 6

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

levels(subdf$letters)<-c("a","b","c",NA,NA)
Run Code Online (Sandbox Code Playgroud)

哪个让你:

> subdf$letters
[1] a b c
Levels: a b c
Run Code Online (Sandbox Code Playgroud)

请注意,新级别将替换在旧级别(subdf $ letters)中占用其索引的任何内容,因此类似于:

levels(subdf$letters)<-c(NA,"a","c",NA,"b")
Run Code Online (Sandbox Code Playgroud)

不行.

当你有很多关卡时,这显然不是很理想,但对于少数关卡来说,它很快捷.


Bre*_*nor 5

我写了实用程序函数来做到这一点.现在我知道了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
}
Run Code Online (Sandbox Code Playgroud)


Dio*_*ogo 5

这是一种方法

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
Run Code Online (Sandbox Code Playgroud)

  • 这是5年前发布的[this](/sf/answers/83856181/)答案的虚假形式。 (2认同)