在子集数据框中放置因子水平
我有一个数据框包含一个因素。 当我使用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.table
或read.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
方法代码,你可以看到它包裹factor
function。 这意味着你基本上可以用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