首页 文章

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

提问于
浏览
452

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

13 回答

  • 18

    查看 droplevels 方法code in the R source you can see它包装到 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"
    
  • 34

    使用 dplyr 的另一种方法

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

    Edit:

    还有效!感谢agenis

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

    如果您不使用因子,请改用字符向量 . 我认为这比后来修补更有意义 . 在使用 read.tableread.csv 加载数据之前,请尝试以下操作:

    options(stringsAsFactors = FALSE)
    

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

  • 354

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

    EDIT

    从因子页面示例:

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

    我写了实用程序函数来执行此操作 . 现在我知道了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
    }
    
  • 7

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

    > levels(subdf$letters)
    [1] "a" "b" "c" "d" "e"
    > subdf$letters <- as.factor(as.character(subdf$letters))
    > levels(subdf$letters)
    [1] "a" "b" "c"
    
  • 10

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

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

    不行 .

    当你有很多级别时,这显然不是理想的,但对于少数,它是快速和简单的 .

  • 6

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

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

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

    这是一种方法

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

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

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

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

    levels(droplevels(subdf$letters))
    
  • 5

    当我使用 data.frame 时,我现在在脚本的开头使用 options(stringsAsFactors = FALSE) . 因此,字符仍然是字符 . 因为,我不再有任何问题的问题:)

相关问题