Tho*_*mas 5 aggregate r counting na
我有以下 data.frame:
x <- data.frame(A = c("Y", "Y", "Z", NA),
B = c(NA, TRUE, FALSE, TRUE),
C = c(TRUE, TRUE, NA, FALSE))
Run Code Online (Sandbox Code Playgroud)
我需要计算下表xtabs:
A B C
Y 1 2
Z 0 0
<NA> 1 0
Run Code Online (Sandbox Code Playgroud)
我被告知要使用na.action = NULL,它确实返回了我需要的表:
xtabs(formula = cbind(B, C) ~ A,
data = x,
addNA = TRUE,
na.action = NULL)
A B C
Y 1 2
Z 0 0
<NA> 1 0
Run Code Online (Sandbox Code Playgroud)
但是,na.action = na.pass返回一个不同的表:
xtabs(formula = cbind(B, C) ~ A,
data = x,
addNA = TRUE,
na.action = na.pass)
A B C
Y 2
Z 0
<NA> 1 0
Run Code Online (Sandbox Code Playgroud)
但是文档xtabs说:
na.action
当它是 na.pass 并且公式具有左侧(带计数)时,使用 sum(*, na.rm = TRUE) 而不是 sum(*) 来计算计数。
使用aggregate,na.action = na.pass返回预期结果(还有na.action = NULL):
aggregate(formula = cbind(B, C) ~ addNA(A),
data = x,
FUN = sum,
na.rm = TRUE,
na.action = na.pass) # same result with na.action = NULL
addNA(A) B C
1 Y 1 2
2 Z 0 0
3 <NA> 1 0
Run Code Online (Sandbox Code Playgroud)
虽然我得到了我需要的表xtabs,但我不了解文档中的na.actionin行为xtabs。所以我的问题是:
na.action在xtabs与文档保持一致?除非我遗漏了什么,否则na.action = na.pass不会导致sum(*, na.rm = TRUE).na.action = NULL某处有记录吗?xtabs源代码中有na.rm <- identical(naAct, quote(na.omit)) || identical(naAct, na.omit) || identical(naAct, "na.omit"). 但我什么也没看到na.action = na.pass和na.action = NULL。如何做na.action = na.pass和na.action = NULL工作?在不描述xtabs工作原理的情况下,很难给出规范的答案。如果我们逐步了解其源代码的要点,我们就会清楚地看到发生了什么。
在进行一些基本的类型检查之后,调用 以xtabs首先使用stats::model.frame为公式中包含的所有变量创建一个数据框,从而在内部工作,并且正是为此na.action传递了参数。
它这样做的方式非常聪明。xtabs首先复制您通过 拨打的电话match.call,如下所示:
m <- match.call(expand.dots = FALSE)
Run Code Online (Sandbox Code Playgroud)
然后它去掉不需要传递的参数,stats::model.frame如下所示:
m$... <- m$exclude <- m$drop.unused.levels <- m$sparse <- m$addNA <- NULL
Run Code Online (Sandbox Code Playgroud)
正如帮助文件中所承诺的,如果addNA是TRUE和na.action缺失,它现在将默认为na.pass:
if (addNA && missing(na.action))
m$na.action <- quote(na.pass)
Run Code Online (Sandbox Code Playgroud)
然后它将要调用的函数更改xtabs为stats::model.frame如下所示:
m[[1L]] <- quote(stats::model.frame)
Run Code Online (Sandbox Code Playgroud)
所以这个对象m是一个调用(也是一个独立的 reprex),在你的情况下看起来像这样:
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)
Run Code Online (Sandbox Code Playgroud)
请注意,您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>
Run Code Online (Sandbox Code Playgroud)
请注意,这与您通过时获得的结果相同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>
Run Code Online (Sandbox Code Playgroud)
但是,如果您通过了na.action = na.omit,您将只剩下一行,因为只有第 2 行没有NA值。
在任何情况下,“模型框架”结果都存储在变量 中mf。然后将其拆分为自变量(在您的情况下为 A 列)和响应变量(在您的情况下)cbind(B, C)。
响应存储在 中y,变量存储在by:
i <- attr(attr(mf, "terms"), "response")
by <- mf[-i]
y <- mf[[i]]
Run Code Online (Sandbox Code Playgroud)
现在,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]
})
Run Code Online (Sandbox Code Playgroud)
现在我们来到了症结。所述na.action被再次使用,以确定如何NA在响应变量值将被计算。在您的情况下,由于您通过了na.action = NULL,您将看到naAct将获得存储在 中的值getOption("na.action"),如果您从未更改过它,则应将其设置为na.omit。这反过来会导致变量的值na.rm,是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")
Run Code Online (Sandbox Code Playgroud)
请注意,如果您通过了na.action = na.pass,那么如果您跟踪这段代码,na.rm就会是FALSE。
最后,我们来到xtabs使用suminside a构建表的部分tapply,它本身在 a 中lapply。
m <- match.call(expand.dots = FALSE)
Run Code Online (Sandbox Code Playgroud)
您可以看到该na.rm变量用于确定是否NA在尝试对列求和之前从列中删除s。然后将其结果lapply强制到最终的交叉表中。
那么这如何回答你的问题呢?
当文档说如果您不传递 时na.action,它将默认为na.pass。但是,na.action用在两个地方:一次在调用中model.frame,一次用于确定 的值na.rm。从源代码中可以清楚地看出,如果na.action是na.pass,na.rm则将是FALSE,因此您将错过任何包含NA值的响应组的计数。这与帮助文件中所写的相反。
解决这个问题的唯一方法是传递na.action = NULL,因为这将允许model.frame保留NA值,但也会导致sum函数默认为na.rm。
TL;DRxtabs在这一点上的文档是错误的。