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

时间:2009-07-28 18:21:48

标签: r dataframe r-factor r-faq

我有一个包含因子的数据框。当我使用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    

## but the levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"

15 个答案:

答案 0 :(得分:472)

自R版本2.12起,有一个droplevels()函数。

levels(droplevels(subdf$letters))

答案 1 :(得分:375)

您需要做的就是在子集化后再次将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

修改

从因子页面示例:

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)

答案 2 :(得分:39)

如果您不想要此行为,请不要使用因子,而是使用字符向量。我认为这比后来修补更有意义。在使用read.tableread.csv

加载数据之前,请尝试以下操作
options(stringsAsFactors = FALSE)

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

答案 3 :(得分:34)

这是一个已知问题,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

gdata包中还有[个功能。但是,它只能通过更改子集运算符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"

{{1}}

答案 4 :(得分:20)

使用dplyr

执行相同操作的另一种方法
library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

修改:

也有效!感谢agenis

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

答案 5 :(得分:12)

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

答案 6 :(得分:12)

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

它与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

答案 7 :(得分:7)

查看[[$1|[$2]],[$2|[$1]] = [[$1,$2],[$2,$1]] = ["12","21"]方法code in the R source you can see,它包含在droplevels函数中。这意味着您基本上可以使用factor函数重新创建列 在data.table方式下面,从所有因子列中删除级别。

factor

答案 8 :(得分:6)

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

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

让你:

> subdf$letters
[1] a b c
Levels: a b c

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

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

无效。

当你有很多关卡时,这显然不是理想的选择,但是对于少数关卡来说,这很简单。

答案 9 :(得分:6)

这是一种方法

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

答案 10 :(得分:5)

我写了实用程序函数来执行此操作。现在我知道了gdata的drop.levels,它看起来非常相似。他们在这里(来自here):

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
}

答案 11 :(得分:3)

非常有趣的主题,我特别喜欢只想再次选择子选择的想法。之前我遇到过类似的问题,我只是转换为角色然后回到因素。

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

答案 12 :(得分:0)

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

答案 13 :(得分:0)

在我看来,即使不是全部,但这里都尝试过大多数示例,但似乎都没有用。 在奋斗了一段时间之后,我尝试在factor列上使用 as.character()将其更改为带有字符串的col,看来效果很好。

不确定性能问题。

答案 14 :(得分:-1)

一个真正的 droplevels 函数比 const data = '{{qs_json}}' const rdata = JSON.parse(data.replace(/&quot;/g, '"')) console.log(rdata) const input = document.getElementById('search_here') console.log(input) let filteredArr = [] input.addEventListener('keyup', (e) => { box.innerHTML = "" filteredArr = rdata.filter(store => store['title'].includes(e.target.value)) console.log(filteredArr) if (filteredArr.length > 0) { filteredArr.map(store => { box.innerHTML += ` <div class="col-4 mb-3"> <div class="card h-100"> <a href="${store['get_absolute_url']}"> <embed src="${store['image']['url']}" class="card-img-top" alt="..."/> </a> <div class="card-body"> <h5 class="card-title">${store['title']}</h5> <p class="card-text"> ${store['description'] ?? ''} </p> </div> <div class="card-footer"> <small class="text-muted">${store['timestamp']}</small> </div> </div> </div>`; }) } else { box.innerHTML = "<b>No results found...</b>" } }) 快得多并且不执行任何类型的不必要的匹配或值列表是 droplevels。示例:

collapse::fdroplevels