在子集数据框中放置因子水平

我有一个数据框包含一个因素。 当我使用subset()或其他索引函数创build此数据框的subset() ,会创build一个新的数据框。 但是,因素variables保留了所有的原始级别 – 即使它们不存在于新的数据框架中。

这在创build多面绘图或使用依赖于因子级别的函数时会产生麻烦。

在我的新数据框架中从一个因素中删除层次的最简洁的方法是什么?

这是我的例子:

 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 ## but the levels are still there! levels(subdf$letters) ## [1] "a" "b" "c" "d" "e" 

你所要做的就是在子集化之后再将factor()应用到你的variables中:

 > subdf$letters [1] abc Levels: abcde subdf$letters <- factor(subdf$letters) > subdf$letters [1] abc Levels: abc 

编辑

从因素页面示例:

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

这是一个已知的问题,一个可能的补救措施是由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))的直接方法是简单的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.tableread.csv加载数据之前,请尝试以下read.csv

 options(stringsAsFactors = FALSE) 

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

这是另一种方式,我相信这是相当于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" 

另一种方式,但与dplyr相同

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

编辑:

也工作! 感谢agenis

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

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

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

这让你:

 > subdf$letters [1] abc Levels: abc 

请注意,新的级别将取代旧的级别(subdf $字母)中占据其索引的任何东西,例如:

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

将无法工作。

当你有很多关卡的时候,这显然不是很理想,但是有一些关卡很简单。

这是一个这样做的方法

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

看看R源中的droplevels方法代码,你可以看到它包裹factorfunction。 这意味着你基本上可以用factor函数重新创build列。
在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" 

我写了实用function来做到这一点。 现在我知道了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 } 

非常有趣的线程,我特别喜欢的主意,只是再次推翻子选举。 我之前有类似的问题,我只是转换为字符,然后回到因素。

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

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

它与处理NA的方式不同:

 f <- factor(c("a", "b", NA), exclude = NULL) droplevels(f) # [1] ab <NA> # Levels: ab <NA> forcats::fct_drop(f) # [1] ab <NA> # Levels: ab