Como contar valores TRUE em um vetor lógico


160

Em R, qual é a maneira mais eficiente / idiomática de contar o número de TRUEvalores em um vetor lógico? Eu posso pensar em duas maneiras:

z <- sample(c(TRUE, FALSE), 1000, rep = TRUE)
sum(z)
# [1] 498

table(z)["TRUE"]
# TRUE 
#  498 

Qual você prefere? Existe algo ainda melhor?

Respostas:


174

Existem alguns problemas quando o vetor lógico contém NAvalores.
Veja por exemplo:

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)

Então, acho que o mais seguro é usar na.rm = TRUE:

sum(z, na.rm = TRUE) # best way to count TRUE values

(que fornece 1). Eu acho que a tablesolução é menos eficiente (veja o código da tablefunção).

Além disso, você deve ter cuidado com a solução "table", caso não haja valores TRUE no vetor lógico. Suponha z <- c(NA, FALSE, NA)ou simplesmente z <- c(FALSE, FALSE), em seguida, table(z)["TRUE"]fornece os NAdois casos.


table(c(FALSE))["TRUE"]dá NA, não 0.
Yossi Farjoun 21/05/19

@YossiFarjoun Sim, e está na minha resposta. Estes são exemplos de porque não funcionará. Minha solução ésum(z, na.rm = TRUE)
Marek #

84

Outra opção que não foi mencionada é usar which:

length(which(z))

Apenas para realmente fornecer algum contexto sobre a "pergunta mais rápida", é sempre mais fácil apenas testar a si mesmo. Eu fiz o vetor muito maior para comparação:

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 

Tão claramente usando sumé a melhor abordagem neste caso. Você também pode querer verificar os NAvalores, como sugerido por Marek.

Apenas para adicionar uma observação sobre os valores de NA e a whichfunção:

> which(c(T, F, NA, NULL, T, F))
[1] 1 4
> which(!c(T, F, NA, NULL, T, F))
[1] 2 5

Observe o que apenas verifica a lógica TRUE, portanto, basicamente ignora valores não lógicos.


BTW, não foi um bom truque com o tempo em resposta Dirk: stackoverflow.com/questions/1748590/revolution-for-r/...
Marek

12

Outra maneira é

> length(z[z==TRUE])
[1] 498

Embora sum(z) seja agradável e curto, para mim length(z[z==TRUE])é mais autoexplicativo. No entanto, acho que com uma tarefa simples como essa, realmente não faz diferença ...

Se for um vetor grande, provavelmente você deve optar pela solução mais rápida sum(z). length(z[z==TRUE])é cerca de 10x mais lento e table(z)[TRUE]é cerca de 200x mais lento que sum(z).

Resumindo, sum(z)é o mais rápido a digitar e executar.


6

whiché uma boa alternativa, especialmente quando você opera em matrizes (verifique ?whiche observe o arr.indargumento). Mas eu sugiro que você continue sum, por causa do na.rmargumento que pode lidar com NAs no vetor lógico. Por exemplo:

# 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

Se você digitar sum(x)você terá NAcomo resultado, mas se você passar na.rm = TRUEna sumfunção, você poderá obter o resultado que você deseja.

> sum(x)
[1] NA
> sum(x, na.rm=TRUE)
[1] 43

Sua pergunta é estritamente teórica ou você tem algum problema prático em relação a vetores lógicos?


Eu estava tentando avaliar um teste. Fazendo algo como soma (youranswer == rightanswer) dentro de uma aplicação.
Jyotirmoy Bhattacharya

Minha resposta é longa demais, por isso postei uma nova resposta, pois ela difere da anterior.
aL3xa

6

Outra opção é usar a função de resumo. Ele fornece um resumo dos Ts, Fs e NAs.

> summary(hival)
   Mode   FALSE    TRUE    NA's 
logical    4367      53    2076 
> 

1
Além disso, para obter apenas os resultados "TRUE" (que serão exibidos como uma string, mas também incluem "TRUE" na saída) summary(hival)["TRUE"]:;
22616 Michael

0

Eu venho fazendo algo semelhante há algumas semanas atrás. Aqui está uma solução possível, que foi escrita do zero, portanto é uma versão beta ou algo parecido. Vou tentar melhorá-lo removendo loops do código ...

A idéia principal é escrever uma função que aceite 2 (ou 3) argumentos. O primeiro é aquele data.frameque contém os dados coletados no questionário e o segundo é um vetor numérico com respostas corretas (isso só é aplicável ao questionário de escolha única). Como alternativa, você pode adicionar o terceiro argumento que retornará o vetor numérico com a pontuação final ou data.frame com a pontuação incorporada.

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)
}

Vou tentar fazer isso de uma maneira mais elegante com alguma função * dobra. Observe que eu não coloquei na.rmargumento ... Farei isso

# 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))

Agora aplique uma função:

> fscore(d, sol)
 [1] 6 4 2 4 4 3 3 6 2 6

Se você passar o argumento data.frame, ele retornará data.frame modificado. Vou tentar consertar este ... Espero que ajude!


6
One-liner: rowSums(t(t(d)==sol), na.rm=TRUE). R reciclar vetor para comparação. Se você dfosse uma matriz com casos em colunas, será simplificado para rowSums(d==sol, na.rm=TRUE).
Marek

0

Acabei de ter um problema específico em que tive que contar o número de declarações verdadeiras de um vetor lógico e isso funcionou melhor para mim ...

length(grep(TRUE, (gene.rep.matrix[i,1:6] > 1))) > 5

Portanto, isso pega um subconjunto do objeto gene.rep.matrix e aplica um teste lógico, retornando um vetor lógico. Esse vetor é colocado como argumento para grep, que retorna os locais de quaisquer entradas TRUE. Length calcula quantas entradas o grep encontra, fornecendo, assim, o número de entradas TRUE.


0

Há também um pacote chamado bitque é projetado especificamente para operações booleanas rápidas. É especialmente útil se você tiver vetores grandes ou precisar executar muitas operações booleanas.

z <- sample(c(TRUE, FALSE), 1e8, rep = TRUE)

system.time({
  sum(z) # 0.170s
})

system.time({
  bit::sum.bit(z) # 0.021s, ~10x improvement in speed
})
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.