Come contare i Veri valori in un vettore logico


160

In R, qual è il modo più efficiente / idiomatico per contare il numero di TRUEvalori in un vettore logico? Posso pensare a due modi:

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

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

Quale preferisci? C'è qualcosa di ancora meglio?

Risposte:


174

Ci sono alcuni problemi quando il vettore logico contiene NAvalori.
Vedi ad esempio:

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)

Quindi penso che il più sicuro sia usare na.rm = TRUE:

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

(che dà 1). Penso che la tablesoluzione sia meno efficiente (guarda il codice della tablefunzione).

Inoltre, dovresti stare attento con la soluzione "table", nel caso in cui non ci siano Veri valori nel vettore logico. Supponi z <- c(NA, FALSE, NA)o semplicemente z <- c(FALSE, FALSE), quindi table(z)["TRUE"]ti dà NAper entrambi i casi.


table(c(FALSE))["TRUE"]dà NA, non 0.
Yossi Farjoun,

@YossiFarjoun Sì, ed è nella mia risposta. Ecco alcuni esempi per cui non funzionerà. Il mio sollution èsum(z, na.rm = TRUE)
Marek,

84

Un'altra opzione che non è stata menzionata è quella di utilizzare which:

length(which(z))

Solo per fornire effettivamente un contesto sulla "domanda più veloce", è sempre più semplice testare te stesso. Ho reso il vettore molto più grande per il confronto:

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 

Quindi chiaramente l'utilizzo sumè l'approccio migliore in questo caso. Puoi anche verificare i NAvalori come suggerito da Marek.

Solo per aggiungere una nota relativa ai valori NA e alla whichfunzione:

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

Notare ciò che verifica solo la logica TRUE, quindi essenzialmente ignora i valori non logici.


A proposito, c'è stato un bel trucco con i tempi nella risposta di Dirk: stackoverflow.com/questions/1748590/revolution-for-r/…
Marek

12

Un altro modo è

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

Mentre sum(z) è bello e breve, per me length(z[z==TRUE])è più autoesplicativo. Tuttavia, penso che con un compito semplice come questo non faccia davvero la differenza ...

Se è un vettore di grandi dimensioni, probabilmente dovresti scegliere la soluzione più veloce, ovvero sum(z). length(z[z==TRUE])è circa 10 volte più lento ed table(z)[TRUE]è circa 200 volte più lento di sum(z).

Riassumendo, sum(z)è il più veloce da digitare ed eseguire.


6

whichè una buona alternativa, specialmente quando si opera su matrici (controllare ?whiche notare l' arr.indargomento). Ma ti suggerisco di restare fedele sum, a causa na.rmdell'argomento che può gestire NAle cose nel vettore logico. Per esempio:

# 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 si digita, sum(x)si otterrà NAdi conseguenza, ma se si passa na.rm = TRUEin sumfunzione, si otterrà il risultato desiderato.

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

La tua domanda è strettamente teorica o hai qualche problema pratico relativo ai vettori logici?


Stavo cercando di valutare un quiz. Fare qualcosa come sum (youranswer == rightanswer) all'interno di una domanda.
Jyotirmoy Bhattacharya,

La mia risposta è troppo lunga, quindi ho pubblicato una nuova risposta, poiché differisce dalla precedente.
aL3xa,

6

Un'altra opzione è utilizzare la funzione di riepilogo. Fornisce un riepilogo di Ts, Fs e NA.

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

1
Inoltre, per ottenere solo i risultati "TRUE" (che verranno emessi come stringa, ma include anche "TRUE" nell'output) summary(hival)["TRUE"]:;
michael,

0

Ho fatto qualcosa di simile alcune settimane fa. Ecco una possibile soluzione, è stata scritta da zero, quindi è una specie di versione beta o qualcosa del genere. Proverò a migliorarlo rimuovendo i loop dal codice ...

L'idea principale è quella di scrivere una funzione che prenderà 2 (o 3) argomenti. Il primo è uno data.frameche contiene i dati raccolti dal questionario, e il secondo è un vettore numerico con risposte corrette (questo è applicabile solo al questionario a scelta singola). In alternativa, puoi aggiungere un terzo argomento che restituirà il vettore numerico con il punteggio finale o data.frame con il punteggio incorporato.

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

Proverò a farlo in un modo più elegante con qualche funzione * ply. Nota che non ho messo in na.rmdiscussione ... Lo farò

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

Ora applica una funzione:

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

Se si passa l'argomento data.frame, verrà restituito data.frame modificato. Proverò a risolvere questo ... Spero che sia d'aiuto!


6
One-liner: rowSums(t(t(d)==sol), na.rm=TRUE). R ricicla il vettore per il confronto. Se tu dfossi matrice con casi in colonne, allora la sua semplifica rowSums(d==sol, na.rm=TRUE).
Marek,

0

Ho appena avuto un problema particolare in cui ho dovuto contare il numero di affermazioni vere da un vettore logico e questo ha funzionato meglio per me ...

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

Quindi questo prende un sottoinsieme dell'oggetto gene.rep.matrix e applica un test logico, restituendo un vettore logico. Questo vettore viene inserito come argomento in grep, che restituisce le posizioni di qualsiasi VERA voce. Lunghezza quindi calcola quante voci trova grep, fornendo così il numero di VERE voci.


0

C'è anche un pacchetto chiamato bitappositamente progettato per operazioni booleane veloci. È particolarmente utile se hai grandi vettori o hai bisogno di fare molte operazioni booleane.

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
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.