Трудно дать канонический ответ, не описав, как работает xtabs
. Если мы пройдемся по основным пунктам его исходного кода, мы ясно увидим, что происходит.
После некоторой базовой проверки типа c вызов 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
равно TRUE
и na.action
отсутствует, теперь по умолчанию будет na.pass
:
if (addNA && missing(na.action))
m$na.action <- quote(na.pass)
Затем он изменит Функция должна вызываться с xtabs
до 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
.
В любом случае, результат "рамки модели" сохраняется в переменной mf
. Затем он разделяется на независимую (ые) переменную (и), - в вашем случае, столбец A и переменную ответа - в вашем случае cbind(B, C)
.
Ответ сохраняется в y
, а переменная в by
:
i <- attr(attr(mf, "terms"), "response")
by <- mf[-i]
y <- mf[[i]]
Теперь 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,
будет 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
, тогда 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
равно na.pass
, то na.rm
будет FALSE
, поэтому вы пропустите число групп ответов, содержащих значения NA
. Это противоположно тому, что написано в файле справки.
Единственный способ обойти это - передать na.action = NULL
, так как это позволит model.frame
сохранить значения NA
, но также вызовет sum
Функция по умолчанию na.rm
.
TL; DR Документация для xtabs
неверна в этом пункте.