R 删除子集合数据帧中未使用的因子级别

R 删除子集合数据帧中未使用的因子级别,r,dataframe,r-factor,r-faq,R,Dataframe,R Factor,R Faq,我有一个包含因子的数据框。当我使用subset或其他索引函数创建此数据帧的子集时,将创建一个新的数据帧。但是,因子变量保留其所有原始级别,即使新数据帧中不存在这些级别 在进行分面打印或使用依赖于因子级别的函数时,这会导致问题 在新的数据帧中,从因子中删除级别的最简洁的方法是什么 下面是一个例子: df <- data.frame(letters=letters[1:5], numbers=seq(1:5)) levels(df$letters) #

我有一个包含
因子的数据框。当我使用
subset
或其他索引函数创建此数据帧的子集时,将创建一个新的数据帧。但是,
因子
变量保留其所有原始级别,即使新数据帧中不存在这些级别

在进行分面打印或使用依赖于因子级别的函数时,这会导致问题

在新的数据帧中,从因子中删除级别的最简洁的方法是什么

下面是一个例子:

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"

df这是一个已知的问题,一个可能的补救方法是在示例所在的包中提供的
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"
包中还有
dropUnusedLevels
功能。但是,它仅通过更改子集运算符
[
起作用,在这里不适用

作为推论,基于每列的直接方法是简单的
As.factor(As.character(data))

>级别(subdf$字母)
[1] “a”“b”“c”“d”“e”
>subdf$字母级别(subdf$字母)
[1] “a”“b”“c”

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

levels(subdf$letters)<-c("a","b","c",NA,NA)
请注意,新级别将替换旧级别(subdf$字母)中索引中的任何内容,如下所示:

levels(subdf$letters)<-c(NA,"a","c",NA,"b")

levels(subdf$letters)您只需在子集设置后将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
要从数据帧中的所有因子列中删除级别,可以使用:

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

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

options(stringsAsFactors = FALSE)

缺点是只能按字母顺序排列。(重新排序是绘图的好方法)

这里有另一种方法,我认为这相当于
因子(…)
方法:

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

>df subdf我编写了实用函数来实现这一点。现在我了解了gdata的drop.levels,它看起来非常相似。它们如下所示:


present\u levels自R版本2.12以来,有一个
droplevels()
函数

levels(droplevels(subdf$letters))

这里有一种方法

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]

varFactor非常有趣的线程,我特别喜欢再次将子选择因子化的想法。我以前也遇到过类似的问题,我只是转换为字符,然后再转换回因子

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

df使用
dplyr执行相同操作的另一种方法

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
库(dplyr)
subdf%过滤器(数量%droplevels()
str(subdf)
编辑:

也有用!谢谢你

subdf%过滤器(数字%droplevels
级别(subdf$字母)

查看它包装到
factor
函数的
droplevels
方法。这意味着您基本上可以使用
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"

库(data.table)
dt=数据。表格(字母=系数(字母[1:5]),数字=序号(1:5))
级别(dt$字母)
#[1] “a”“b”“c”“d”“e”

subdt=dt[numbers为了完整起见,现在在
forcats
包中还有
fct_drop

它在处理
NA
的方式上与
droplevels
不同:

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
f不幸的是,当使用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)

我已经尝试了这里的大多数例子,如果不是所有的,但没有一个在我的情况下起作用。 经过一段时间的努力,我尝试在factor列上使用as.character()将其更改为一个带有字符串的col,看起来效果不错


不确定是否存在性能问题。

真正的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
库(折叠)
图书馆(微基准)
#wlddev数据在崩溃中提供,iso3c是一个因素

对于一次性的数据来说这很好,但是在一个包含大量列的data.frame中,您可以在每一列上都这样做,这是一个因素…导致需要一个函数,比如gdata中的drop.levels()。我明白了…但是从用户的角度来看,很快就可以编写类似subdf[]感谢Stephen&Dirk-我对这篇文章中的一个因子的caes表示赞许,但希望大家能够阅读这些评论,以了解您关于清理整个因子数据框的建议。作为一种副作用,函数会将数据框转换为列表,因此
mydf也:此方法确实保留了变量的顺序<代码>重新排序
drop.levels
函数的参数值得一提:如果必须保留因子的原始顺序,请将其与
FALSE
值一起使用。仅对drop.levels使用gdata会产生“启用gdata:read.xls对'xls'(Excel 97-2004)文件的支持”“gdata:无法加载read.xls()所需的perl库”“gdata:支持'XLSX'(Excel 2007+)文件”“gdata:运行函数'installXLSXsupport()'”“gdata:自动下载并安装perl”“。使用baseR()中的droplevels”事情会随着时间的推移而发生。你正在评论我九年前写的一个答案。因此,让我们把这作为一个提示,让我们普遍倾向于基本R解决方案,因为这些解决方案使用的功能从现在起仍将是N年左右。与使用
factor()相比,这种方法的优点是
是指无需修改原始数据帧或创建新的持久数据帧。我可以包装
subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
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"
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
# 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)
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