在R中的因子上使用sapply的意外行为

时间:2016-03-14 15:24:37

标签: r sapply

使用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一个{{1}}。

我的实际问题是:为什么会这样? (到现在为止,我非常确定这是一个因素相关的功能,我不知道......)

1 个答案:

答案 0 :(得分: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(lapply(F, function(x) if (x=="C") x else NA)[[1]])
#[1] "logical"

这意味着来自help("unlist")的两个引号是相关的:

  

特别对待因素。如果x的所有非列表元素都是   因素(或有序因素)然后结果将是一个因素   按顺序级别元素的级别集合的并集   级别出现在元素的级别集中(这意味着如果   所有元素都具有相同的级别集,即级别集   结果)。

  

在可能的情况下,列表元素被强制转换为共同模式   不列表,结果通常最终成为一个字符向量。   矢量将被强制转换为最高类型的组件   层次结构NULL&lt;原始&lt;逻辑&lt;整数&lt;双&lt;复杂&lt;   字符&lt;列表&lt;表达式:pairlists被视为列表。

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

如果您想确保从sapply获取因子向量,请在NA条件中创建因子else

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