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)
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)
had*_*ley 41
如果您不想要此行为,请不要使用因子,而是使用字符向量.我认为这比后续修补更有意义.在使用read.table或加载数据之前,请尝试以下操作read.csv:
options(stringsAsFactors = FALSE)
Run Code Online (Sandbox Code Playgroud)
缺点是您只能按字母顺序排序.(重新排序是您的情节的朋友)
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)
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_drop在forcats包装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)
查看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)
这是令人讨厌的.这是我通常这样做,以避免加载其他包:
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)
不行.
当你有很多关卡时,这显然不是很理想,但对于少数关卡来说,它很快捷.
我写了实用程序函数来做到这一点.现在我知道了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)
这是一种方法
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
363230 次 |
| 最近记录: |