Как подсчитать ИСТИННЫЕ значения в логическом векторе

В 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, что дает количество ИСТИННЫХ записей.