如果不描述如何进行,很难给出规范的答案xtabs
作品。如果我们逐步查看其源代码的要点,我们就会清楚地看到发生了什么。
经过一些基本的类型检查后,调用xtabs
在内部工作,首先使用以下命令创建公式中包含的所有变量的数据框stats::model.frame
,正是针对这一点,na.action
参数已传递。
它这样做的方式非常聪明。xtabs
首先复制您通过以下方式拨打的电话match.call
, 像这样:
m <- match.call(expand.dots = FALSE)
然后它去掉不需要传递给的参数stats::model.frame
像这样:
m$... <- m$exclude <- m$drop.unused.levels <- m$sparse <- m$addNA <- NULL
正如帮助文件中所承诺的,如果addNA
is TRUE
and na.action
缺失,现在默认为na.pass
:
if (addNA && missing(na.action))
m$na.action <- quote(na.pass)
然后它更改要调用的函数xtabs
to stats::model.frame
像这样:
m[[1L]] <- quote(stats::model.frame)
所以对象m
是一个调用(也是一个独立的代表),在您的情况下看起来像这样:
stats::model.frame(formula = cbind(B, C) ~ A, data = list(A = structure(c(1L,
1L, 2L, NA), .Label = c("Y", "Z"), class = "factor"), B = c(NA, TRUE, FALSE, TRUE),
C = c(TRUE, TRUE, NA, FALSE)), na.action = NULL)
请注意,您的na.action = NULL
已传递给此调用。这具有保留所有NA
框架中的值。当评估上述调用时,它会给出以下数据框:
eval(m)
#> cbind(B, C).B cbind(B, C).C A
#> 1 NA TRUE Y
#> 2 TRUE TRUE Y
#> 3 FALSE NA Z
#> 4 TRUE FALSE <NA>
请注意,如果您通过了,这与您得到的结果相同na.action = na.pass
:
stats::model.frame(formula = cbind(B, C) ~ A, data = list(A = structure(c(1L,
1L, 2L, NA), .Label = c("Y", "Z"), class = "factor"), B = c(NA, TRUE, FALSE, TRUE),
C = c(TRUE, TRUE, NA, FALSE)), na.action = na.pass)
#> cbind(B, C).B cbind(B, C).C A
#> 1 NA TRUE Y
#> 2 TRUE TRUE Y
#> 3 FALSE NA Z
#> 4 TRUE FALSE <NA>
然而,如果你通过了na.action = na.omit
,您将只留下一行,因为只有第 2 行没有NA
values.
无论如何,“模型框架”结果存储在变量中mf
。然后将其分为自变量(在您的情况下为 A 列)和响应变量(在您的情况下)cbind(B, C)
.
响应存储在y
和变量by
:
i <- attr(attr(mf, "terms"), "response")
by <- mf[-i]
y <- mf[[i]]
Now, by
进行处理以确保每个自变量都是一个因素,并且任何NA
如果您已指定,值将转换为因子水平addNA = TRUE
:
by <- lapply(by, function(u) {
if (!is.factor(u))
u <- factor(u, exclude = exclude)
else if (has.exclude)
u <- factor(as.character(u), levels = setdiff(levels(u),
exclude), exclude = NULL)
if (addNA)
u <- addNA(u, ifany = TRUE)
u[, drop = drop.unused.levels]
})
现在我们来到了症结所在。这na.action
再次用于确定如何NA
响应变量中的值将被计算在内。就你而言,自从你通过了na.action = NULL
,你会看到naAct
将得到存储在的值getOption("na.action")
,如果您从未更改过它,则应设置为na.omit
。这反过来会导致变量的值na.rm,
to be TRUE
:
naAct <- if (!is.null(m$na.action)) {
m$na.action
}else {getOption("na.action", default = quote(na.omit))}
na.rm <- identical(naAct, quote(na.omit)) || identical(naAct,
na.omit) || identical(naAct, "na.omit")
请注意,如果您已经通过na.action = na.pass
, then na.rm
将会FALSE
如果你跟踪这段代码。
最后,我们来到了您的部分xtabs
表是使用构建的sum
里面一个tapply
,它本身就在一个lapply
.
lapply(as.data.frame(y), tapply, by, sum, na.rm = na.rm, default = 0L)
您可以看到na.rm
变量用于判断是否删除NA
在尝试对列求和之前先从列中取出 s。这样的结果lapply
然后强制进入最终的交叉表。
那么这如何回答你的问题呢?
当文档说如果你没有通过na.action
,它将默认为na.pass
。但是,那na.action
用于两个地方:一次在调用中model.frame
并一次确定值na.rm
。从源码中可以很清楚的看出,如果na.action
is na.pass
, then na.rm
将FALSE
,因此您将错过包含以下内容的任何响应组的计数NA
价值观。这与帮助文件中的内容相反。
解决这个问题的唯一方法就是通过na.action = NULL
,因为这将允许model.frame
保留NA
值,但也会导致sum
函数默认为na.rm
.
TL;DR的文档xtabs
在这一点上是错误的。