对 R 中的一个因子使用 sapply 的意外行为

Unexpected behaviour using sapply on a factor in R

使用 R,我最近对 ​​sapply 在因子上使用时的输出感到惊讶。看看以下内容:

> F <- as.factor(c("A", "B", "C", "D", "E", "F"))

> sapply(F, function(x) x)
[1] A B C D E F
Levels: A B C D E F

> sapply(F, function(x) (x=="C"))
[1] FALSE FALSE  TRUE FALSE  FALSE  FALSE

到目前为止一切顺利,两个输出都符合预期。但是现在,越来越奇怪了:

> sapply(F, function(x) if (TRUE) x else NA)
[1] A B C D E F
Levels: A B C D E F

> sapply(F, function(x) if (x=="C") x else NA)
[1] NA NA  3 NA NA NA

> sapply(F, function(x) {if (x=="C") foo <- "bar"; x})
[1] A B C D E F
Levels: A B C D E F

在这三种情况下,第一个和最后一个结果都符合我的预期。第二个有点奇怪:我希望得到类似 [1] NA NA "C" NA NA NA.

的东西

我的第一个猜测是比较 (x=="C") 对 if 子句中的 x 值有一些影响。 (不在子句之外,否则我们会在上面的最后一个案例中得到另一个结果。)可能 x 被视为子句内部的索引。

然而,这个猜测与以下两个观察结果不相符:

> sapply(F, function(x) if (x==x) x else NA)
[1] A B C D E F
Levels: A B C D E F

> sapply(F, function(x) if (x=="C") F[x] else NA)
[1] NA NA  3 NA NA NA

在这里,(x==x) 似乎根本没有任何影响,如果 x 是它在子句中的索引,我们会取回 "C" 3.

我真正的问题是:为什么会这样? (到目前为止,我很确定这是一些我不知道的与因素相关的特征...)

sapply 基本上是 lapply 后跟 simplify2array,在这种情况下只是对 unlist.

的调用

首先让我们检查该行为是否由 lapply:

引起
lapply(F, function(x) if (x=="C") x else NA)
#[[1]]
#[1] NA
#
#[[2]]
#[1] NA
#
#[[3]]
#[1] C
#Levels: A B C D E F
#
#[[4]]
#[1] NA
#
#[[5]]
#[1] NA
#
#[[6]]
#[1] NA

如你所见,第三个元素仍然是一个因素。但是,NA 值是 class "logical":

class(lapply(F, function(x) if (x=="C") x else NA)[[1]])
#[1] "logical"

这意味着 help("unlist") 中的两个引用是相关的:

Factors are treated specially. If all non-list elements of x are factors (or ordered factors) then the result will be a factor with levels the union of the level sets of the elements, in the order the levels occur in the level sets of the elements (which means that if all the elements have the same level set, that is the level set of the result).

Where possible the list elements are coerced to a common mode during the unlisting, and so the result often ends up as a character vector. Vectors will be coerced to the highest type of the components in the hierarchy NULL < raw < logical < integer < double < complex < character < list < expression: pairlists are treated as lists.

第二个引用描述了这里发生的事情;因子的共模(在内部是具有属性的整数向量)和逻辑值是整数。这就是你得到的。

如果您想确保从 sapply 中获得一个因子向量,请在您的 else 条件中创建一个因子 NA 值:

sapply(F, function(x) if (x=="C") x else {is.na(x) <- TRUE; x})
#[1] <NA> <NA> C    <NA> <NA> <NA>
#Levels: A B C D E F