Как подсчитать ИСТИННЫЕ значения в логическом векторе
В R, каков наиболее эффективный/идиоматический способ подсчета числа значений TRUE
в логическом векторе? Я могу думать о двух путях:
z <- sample(c(TRUE, FALSE), 1000, rep = TRUE)
sum(z)
# [1] 498
table(z)["TRUE"]
# TRUE
# 498
Что вы предпочитаете? Есть ли что-то еще лучше?
Ответы
Ответ 1
Есть некоторые проблемы, когда логический вектор содержит значения NA
.
Смотрите, например:
z <- c(TRUE, FALSE, NA)
sum(z) # gives you NA
table(z)["TRUE"] # gives you 1
length(z[z == TRUE]) # f3lix answer, gives you 2 (because NA indexing returns values)
Поэтому я думаю, что самым безопасным является использование na.rm = TRUE
:
sum(z, na.rm = TRUE) # best way to count TRUE values
(который дает 1). Я думаю, что table
решение менее эффективно (посмотрите на код table
функции).
Кроме того, вы должны быть осторожны с решением "таблица", если в логическом векторе нет значений ИСТИНА. Предположим, что z <- c(NA, FALSE, NA)
или просто z <- c(FALSE, FALSE)
, тогда table(z)["TRUE"]
дает вам NA
для обоих случаев.
Ответ 2
Другой вариант, который не упоминался, заключается в использовании which
:
length(which(z))
Просто для того, чтобы на самом деле предоставить некоторый контекст на тему "который является более быстрым", всегда проще просто проверить себя. Я сделал вектор намного большим для сравнения:
z <- sample(c(TRUE,FALSE),1000000,rep=TRUE)
system.time(sum(z))
user system elapsed
0.03 0.00 0.03
system.time(length(z[z==TRUE]))
user system elapsed
0.75 0.07 0.83
system.time(length(which(z)))
user system elapsed
1.34 0.28 1.64
system.time(table(z)["TRUE"])
user system elapsed
10.62 0.52 11.19
Таким образом, использование sum
в этом случае является наилучшим подходом. Вы также можете проверить значения NA
, как предположил Марек.
Просто добавьте примечание относительно значений NA и функции which
:
> which(c(T, F, NA, NULL, T, F))
[1] 1 4
> which(!c(T, F, NA, NULL, T, F))
[1] 2 5
Обратите внимание, что проверяет только логический TRUE
, поэтому он по существу игнорирует нелогичные значения.
Ответ 3
Другой способ -
> length(z[z==TRUE])
[1] 498
В то время как sum(z)
приятный и короткий, для меня length(z[z==TRUE])
больше объясняется я. Хотя, я думаю, что с простой задачей, подобной этой, это действительно не имеет значения...
Если это большой вектор, вы, вероятно, должны пойти с самым быстрым решением, которое sum(z)
. length(z[z==TRUE])
примерно в 10 раз медленнее, а table(z)[TRUE]
примерно на 200x медленнее, чем sum(z)
.
Подводя итоги, sum(z)
является самым быстрым для ввода и выполнения.
Ответ 4
which
является хорошей альтернативой, особенно когда вы работаете с матрицами (отметьте ?which
и обратите внимание на аргумент arr.ind
). Но я предлагаю вам придерживаться sum
из-за аргумента na.rm
, который может обрабатывать NA
в логическом векторе.
Например:
# create dummy variable
set.seed(100)
x <- round(runif(100, 0, 1))
x <- x == 1
# create NA's
x[seq(1, length(x), 7)] <- NA
Если вы введете sum(x)
, вы получите NA
в результате, но если вы пройдете na.rm = TRUE
в sum
, вы получите результат, который вы хотите.
> sum(x)
[1] NA
> sum(x, na.rm=TRUE)
[1] 43
Является ли ваш вопрос строго теоретическим, или у вас есть практическая проблема с логическими векторами?
Ответ 5
Другой вариант - использовать функцию резюме. Он дает резюме Ts, Fs и NAs.
> summary(hival)
Mode FALSE TRUE NA
logical 4367 53 2076
>
Ответ 6
Я делал что-то подобное несколько недель назад. Здесь возможное решение, написанное с нуля, так что это бета-релиз или что-то в этом роде. Я попытаюсь улучшить его, удалив циклы из кода...
Основная идея - написать функцию, которая будет принимать 2 (или 3) аргумента. Первый - это data.frame
, который содержит данные, собранные из вопросника, а второй - числовой вектор с правильными ответами (это применимо только для вопросника с одним выбором). В качестве альтернативы вы можете добавить третий аргумент, который будет возвращать числовой вектор с конечным результатом, или data.frame со встроенным счетом.
fscore <- function(x, sol, output = 'numeric') {
if (ncol(x) != length(sol)) {
stop('Number of items differs from length of correct answers!')
} else {
inc <- matrix(ncol=ncol(x), nrow=nrow(x))
for (i in 1:ncol(x)) {
inc[,i] <- x[,i] == sol[i]
}
if (output == 'numeric') {
res <- rowSums(inc)
} else if (output == 'data.frame') {
res <- data.frame(x, result = rowSums(inc))
} else {
stop('Type not supported!')
}
}
return(res)
}
Я попытаюсь сделать это более элегантно с помощью некоторой функции * ply. Заметьте, что я не поставил аргумент na.rm
... сделаю это
# create dummy data frame - values from 1 to 5
set.seed(100)
d <- as.data.frame(matrix(round(runif(200,1,5)), 10))
# create solution vector
sol <- round(runif(20, 1, 5))
Теперь примените функцию:
> fscore(d, sol)
[1] 6 4 2 4 4 3 3 6 2 6
Если вы передадите аргумент data.frame, он вернет измененный файл data.frame.
Я попытаюсь исправить это... Надеюсь, это поможет!
Ответ 7
У меня была определенная проблема, когда мне приходилось подсчитывать количество истинных утверждений из логического вектора, и это работало лучше всего для меня...
length(grep(TRUE, (gene.rep.matrix[i,1:6] > 1))) > 5
Итак, это принимает подмножество объекта gene.rep.matrix и применяет логический тест, возвращая логический вектор. Этот вектор помещается как аргумент grep, который возвращает местоположения любых TRUE-записей. Затем длина вычисляет количество записей grep, что дает количество ИСТИННЫХ записей.