Cómo contar valores VERDADEROS en un vector lógico

160

En R, ¿cuál es la forma más eficiente / idiomática de contar el número de TRUEvalores en un vector lógico? Puedo pensar en dos formas:

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

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

¿Cual prefieres? ¿Hay algo aún mejor?

Jyotirmoy Bhattacharya
fuente

Respuestas:

174

Hay algunos problemas cuando el vector lógico contiene NAvalores.
Ver por ejemplo:

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)

Entonces creo que lo más seguro es usar na.rm = TRUE:

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

(que da 1). Creo que esa tablesolución es menos eficiente (mira el código de tablefunción).

Además, debe tener cuidado con la solución de "tabla", en caso de que no haya valores VERDADEROS en el vector lógico. Supongamos z <- c(NA, FALSE, NA)o simplemente z <- c(FALSE, FALSE), luego table(z)["TRUE"]te da NApara ambos casos.

Marek
fuente
table(c(FALSE))["TRUE"]da NA, no 0.
Yossi Farjoun
@YossiFarjoun Sí, y está en mi respuesta. Estos son ejemplos de por qué no funcionará. Mi solución essum(z, na.rm = TRUE)
Marek
84

Otra opción que no se ha mencionado es usar which:

length(which(z))

Solo para proporcionar algo de contexto sobre la "pregunta más rápida", siempre es más fácil ponerse a prueba. Hice el vector mucho más grande para comparar:

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 

Claramente, usar sumes el mejor enfoque en este caso. También es posible que desee verificar los NAvalores como sugirió Marek.

Solo para agregar una nota sobre los valores de NA y la whichfunción:

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

Tenga en cuenta que lo que solo busca es lógico TRUE, por lo que esencialmente ignora los valores no lógicos.

Shane
fuente
Por cierto, hubo un buen truco con el tiempo en la respuesta Dirk: stackoverflow.com/questions/1748590/revolution-for-r/…
Marek
12

Otra forma es

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

Si bien sum(z) es agradable y breve, para mí length(z[z==TRUE])es más autoexplicativo. Sin embargo, creo que con una tarea simple como esta realmente no hace la diferencia ...

Si es un vector grande, probablemente deberías elegir la solución más rápida, que es sum(z). length(z[z==TRUE])es aproximadamente 10 veces más lento y table(z)[TRUE]es aproximadamente 200 veces más lento que sum(z).

En resumen, sum(z)es el más rápido para escribir y ejecutar.

f3lix
fuente
6

whiches una buena alternativa, especialmente cuando opera en matrices (verifique ?whichy observe el arr.indargumento). Pero le sugiero que se quede sum, debido a un na.rmargumento que puede manejar NA's en vector lógico. Por ejemplo:

# 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

Si escribe en el sum(x)que obtendrá NAcomo resultado, pero si pasa na.rm = TRUEen sumla función, obtendrá el resultado que desea.

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

¿Es su pregunta estrictamente teórica o tiene algún problema práctico con respecto a los vectores lógicos?

aL3xa
fuente
Estaba tratando de calificar un cuestionario. Hacer algo como suma (youranswer == rightanswer) dentro de una solicitud.
Jyotirmoy Bhattacharya
Mi respuesta es demasiado larga, así que publiqué una nueva respuesta, ya que difiere de la anterior.
aL3xa
6

Otra opción es usar la función de resumen. Da un resumen de los Ts, Fs y NAs.

> summary(hival)
   Mode   FALSE    TRUE    NA's 
logical    4367      53    2076 
> 
ramrad
fuente
1
Además, para obtener solo los resultados "VERDADEROS" (que se mostrarán como una cadena, pero también incluyen "VERDADERO" en la salida) summary(hival)["TRUE"]:;
michael
0

He estado haciendo algo similar hace unas semanas. Aquí hay una posible solución, está escrita desde cero, por lo que es una especie de versión beta o algo así. Intentaré mejorarlo eliminando bucles del código ...

La idea principal es escribir una función que tome 2 (o 3) argumentos. El primero es el data.frameque contiene los datos recopilados del cuestionario, y el segundo es un vector numérico con respuestas correctas (esto solo es aplicable para el cuestionario de opción única). Alternativamente, puede agregar un tercer argumento que devolverá un vector numérico con puntaje final o data.frame con puntaje incrustado.

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

Intentaré hacer esto de una manera más elegante con alguna función * pliegue. Tenga en cuenta que no puse na.rmargumento ... Hará eso

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

Ahora aplique una función:

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

Si pasa el argumento data.frame, devolverá data.frame modificado. Trataré de arreglar esto ... ¡Espero que ayude!

aL3xa
fuente
66
De una sola línea: rowSums(t(t(d)==sol), na.rm=TRUE). R recicla el vector para comparar. Si su dmatriz fuera con casos en columnas, entonces se simplifica a rowSums(d==sol, na.rm=TRUE).
Marek
0

Acabo de tener un problema particular en el que tuve que contar el número de declaraciones verdaderas de un vector lógico y esto funcionó mejor para mí ...

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

Entonces, esto toma un subconjunto del objeto gene.rep.matrix y aplica una prueba lógica, devolviendo un vector lógico. Este vector se pone como argumento para grep, que devuelve las ubicaciones de las entradas VERDADERAS. La longitud luego calcula cuántas entradas encuentra grep, dando así el número de entradas VERDADERAS.

A_Skelton73
fuente
0

También hay un paquete llamado bitque está diseñado específicamente para operaciones booleanas rápidas. Es especialmente útil si tiene vectores grandes o necesita realizar muchas operaciones 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
})
Daniel Freeman
fuente