为什么使用as.factor()而不仅仅是因子()

时间:2016-09-01 19:13:23

标签: r r-factor

我最近看到Matt Dowle用as.factor()编写了一些代码,特别是

for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))
<{3>}中的

我使用了这个片段,但是我需要明确设置因子级别以确保级别以我想要的顺序出现,所以我不得不改变

as.factor(dt[[col]])

factor(dt[[col]], levels = my_levels)

这让我想到:使用as.factor()与仅使用factor()有什么好处(如果有的话)?

1 个答案:

答案 0 :(得分:59)

as.factorfactor的包装器,但如果输入向量已经是一个因素,它允许快速返回:

function (x) 
{
    if (is.factor(x)) 
        x
    else if (!is.object(x) && is.integer(x)) {
        levels <- sort(unique.default(x))
        f <- match(x, levels)
        levels(f) <- as.character(levels)
        if (!is.null(nx <- names(x))) 
        names(f) <- nx
        class(f) <- "factor"
        f
    }
else factor(x)
}

来自Frank的评论:它不仅仅是一个包装器,因为这个&#34;快速返回&#34;将保留因子水平,而factor()则不会:

f = factor("a", levels = c("a", "b"))
#[1] a
#Levels: a b

factor(f)
#[1] a
#Levels: a

as.factor(f)
#[1] a
#Levels: a b

两年后扩大答案,包括以下内容:

  • 手册说什么?
  • 效果:as.factor&gt;输入是因素时factor
  • 效果:as.factor&gt;输入为整数时factor
  • 未使用的级别或NA级别
  • 使用R&amp; group-by功能时的注意事项:注意未使用或NA级别

手册说什么?

?factor的文档提及以下内容:

‘factor(x, exclude = NULL)’ applied to a factor without ‘NA’s is a
 no-operation unless there are unused levels: in that case, a
 factor with the reduced level set is returned.

 ‘as.factor’ coerces its argument to a factor.  It is an
 abbreviated (sometimes faster) form of ‘factor’.

效果:as.factor&gt;输入是因子时的factor

单词&#34; no-operation&#34;有点暧昧。不要把它作为&#34;什么都不做&#34 ;;事实上,它意味着做很多事情,但基本上没有改变任何事情&#34;。这是一个例子:

set.seed(0)
## a randomized long factor with 1e+6 levels, each repeated 10 times
f <- sample(gl(1e+6, 10))

system.time(f1 <- factor(f))  ## default: exclude = NA
#   user  system elapsed 
#  7.640   0.216   7.887 

system.time(f2 <- factor(f, exclude = NULL))
#   user  system elapsed 
#  7.764   0.028   7.791 

system.time(f3 <- as.factor(f))
#   user  system elapsed 
#      0       0       0 

identical(f, f1)
#[1] TRUE

identical(f, f2)
#[1] TRUE

identical(f, f3)
#[1] TRUE

as.factor可以快速返回,但factor不是真正的&#34; no-op&#34;。让我们的个人资料factor看看它做了什么。

Rprof("factor.out")
f1 <- factor(f)
Rprof(NULL)
summaryRprof("factor.out")[c(1, 4)]
#$by.self
#                      self.time self.pct total.time total.pct
#"factor"                   4.70    58.90       7.98    100.00
#"unique.default"           1.30    16.29       4.42     55.39
#"as.character"             1.18    14.79       1.84     23.06
#"as.character.factor"      0.66     8.27       0.66      8.27
#"order"                    0.08     1.00       0.08      1.00
#"unique"                   0.06     0.75       4.54     56.89
#
#$sampling.time
#[1] 7.98

首先sort输入向量unique的{​​{1}}值,然后将f转换为字符向量,最后使用f来强制字符矢量回到一个因素。以下是factor的源代码供确认。

factor

因此,函数function (x = character(), levels, labels = levels, exclude = NA, ordered = is.ordered(x), nmax = NA) { if (is.null(x)) x <- character() nx <- names(x) if (missing(levels)) { y <- unique(x, nmax = nmax) ind <- sort.list(y) levels <- unique(as.character(y)[ind]) } force(ordered) if (!is.character(x)) x <- as.character(x) levels <- levels[is.na(match(levels, exclude))] f <- match(x, levels) if (!is.null(nx)) names(f) <- nx nl <- length(labels) nL <- length(levels) if (!any(nl == c(1L, nL))) stop(gettextf("invalid 'labels'; length %d should be 1 or %d", nl, nL), domain = NA) levels(f) <- if (nl == nL) as.character(labels) else paste0(labels, seq_along(levels)) class(f) <- c(if (ordered) "ordered", "factor") f } 实际上是设计用于处理字符向量,并将factor应用于其输入以确保这一点。我们至少可以从上面学习两个与绩效相关的问题:

  1. 对于数据框as.characterDF比类型转换的lapply(DF, as.factor)快得多,如果很多列很容易因素。
  2. 这个函数lapply(DF, factor)很慢可以解释为什么一些重要的R函数很慢,比如factorR: table function suprisingly slow
  3. 效果:table&gt;输入为整数时为as.factor

    因子变量是整数变量的近亲。

    factor

    这意味着将整数转换为因子比将数字/字符转换为因子更容易。 unclass(gl(2, 2, labels = letters[1:2])) #[1] 1 1 2 2 #attr(,"levels") #[1] "a" "b" storage.mode(gl(2, 2, labels = letters[1:2])) #[1] "integer" 只是处理这件事。

    as.factor

    未使用的级别或NA级别

    现在让我们看一些关于x <- sample.int(1e+6, 1e+7, TRUE) system.time(as.factor(x)) # user system elapsed # 4.592 0.252 4.845 system.time(factor(x)) # user system elapsed # 22.236 0.264 22.659 factor对因子水平的影响的例子(如果输入已经是一个因素)。 Frank已经提供了一个未使用的因子级别,我将提供一个as.factor级别。

    NA

    有一个(通用)函数f <- factor(c(1, NA), exclude = NULL) #[1] 1 <NA> #Levels: 1 <NA> as.factor(f) #[1] 1 <NA> #Levels: 1 <NA> factor(f, exclude = NULL) #[1] 1 <NA> #Levels: 1 <NA> factor(f) #[1] 1 <NA> #Levels: 1 可用于删除未使用的因子级别。但默认情况下不能删除droplevels级别。

    NA

    使用R&amp; group-by功能时的注意事项:注意未使用或NA级别

    R函数执行分组操作,例如## "factor" method of `droplevels` droplevels.factor #function (x, exclude = if (anyNA(levels(x))) NULL else NA, ...) #factor(x, exclude = exclude) droplevels(f) #[1] 1 <NA> #Levels: 1 <NA> droplevels(f, exclude = NA) #[1] 1 <NA> #Levels: 1 split期望我们将因子变量提供为&#34; by&#34;变量。但通常我们只提供字符或数字变量。所以在内部,这些函数需要将它们转换为因子,并且可能大多数函数首先使用tapply(至少as.factorsplit.default是这样的。 tapply函数看似异常,我在内部发现了table而不是factor。当我检查其源代码时,可能会有一些特殊的考虑因素对我来说是不可能的。

    由于大多数分组R功能使用as.factor,如果它们被赋予了未使用或as.factor级别的因子,则此类组将出现在结果中。

    NA

    有趣的是,虽然x <- c(1, 2) f <- factor(letters[1:2], levels = letters[1:3]) split(x, f) #$a #[1] 1 # #$b #[1] 2 # #$c #numeric(0) tapply(x, f, FUN = mean) # a b c # 1 2 NA 不依赖于table,但它也会保留这些未使用的级别:

    as.factor

    有时这种行为可能是不受欢迎的。一个典型的例子是table(f) #a b c #1 1 0

    enter image description here

    如果这确实不受欢迎,我们需要使用barplot(table(f))NA从我们的因子变量中手动删除未使用的或droplevels级别。

    <强> 提示:

    1. factor有一个参数split,默认为drop,因此使用了FALSE;取而代之的是使用as.factor函数drop = TRUE
    2. factor依赖于aggregate,因此它也有一个split参数,默认为drop
    3. TRUE没有tapply,但它也依赖于drop。特别是文档split?tapply是(始终)使用的。