Wie man WAHRE Werte in einem logischen Vektor zählt

160

Was ist in R die effizienteste / idiomatischste Methode, um die Anzahl der TRUEWerte in einem logischen Vektor zu zählen? Ich kann mir zwei Möglichkeiten vorstellen:

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

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

Was bevorzugen Sie? Gibt es noch etwas besseres?

Jyotirmoy Bhattacharya
quelle

Antworten:

174

Es gibt einige Probleme, wenn der logische Vektor NAWerte enthält .
Siehe zum Beispiel:

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)

Daher denke ich, dass es am sichersten ist, Folgendes zu verwenden na.rm = TRUE:

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

(was 1 ergibt). Ich denke, dass die tableLösung weniger effizient ist (siehe tableFunktionscode).

Sie sollten auch mit der "Tabellen" -Lösung vorsichtig sein, falls der logische Vektor keine TRUE-Werte enthält. Angenommen z <- c(NA, FALSE, NA)oder einfach z <- c(FALSE, FALSE), dann table(z)["TRUE"]gibt es Sie NAfür beide Fälle.

Marek
quelle
table(c(FALSE))["TRUE"]gibt NA, nicht 0.
Yossi Farjoun
@ YossiFarjoun Ja, und es ist in meiner Antwort. Dies sind Beispiele, warum es nicht funktioniert. Meine Absicht istsum(z, na.rm = TRUE)
Marek
84

Eine andere Option, die nicht erwähnt wurde, ist die Verwendung von which:

length(which(z))

Nur um tatsächlich einen Kontext für die "was schneller ist" -Frage bereitzustellen, ist es immer am einfachsten, sich selbst zu testen. Ich habe den Vektor zum Vergleich viel größer gemacht:

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 

sumIn diesem Fall ist die Verwendung eindeutig der beste Ansatz. Möglicherweise möchten Sie auch nach NAWerten suchen, wie von Marek vorgeschlagen.

Nur um einen Hinweis zu den NA-Werten und der whichFunktion hinzuzufügen :

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

Beachten Sie, dass nur TRUElogische Werte überprüft werden , sodass nicht logische Werte im Wesentlichen ignoriert werden.

Shane
quelle
Übrigens gab es einen schönen Trick mit Timing in Dirk Antwort: stackoverflow.com/questions/1748590/revolution-for-r/…
Marek
12

Ein anderer Weg ist

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

Während sum(z) es nett und kurz ist, ist es für mich length(z[z==TRUE])selbsterklärender. Ich denke jedoch, dass es bei einer einfachen Aufgabe wie dieser keinen wirklichen Unterschied macht ...

Wenn es sich um einen großen Vektor handelt, sollten Sie sich wahrscheinlich für die schnellste Lösung entscheiden sum(z). length(z[z==TRUE])ist ungefähr 10x langsamer und table(z)[TRUE]ist ungefähr 200x langsamer als sum(z).

Zusammenfassend sum(z)ist es am schnellsten zu tippen und auszuführen.

f3lix
quelle
6

whichist eine gute Alternative, insbesondere wenn Sie mit Matrizen arbeiten (überprüfen ?whichund beachten Sie das arr.indArgument). Aber ich schlage vor, dass Sie bei bleiben sum, wegen na.rmArgumenten, die NAin logischen Vektoren behandeln können. Zum Beispiel:

# 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

Wenn Sie in geben sum(x)Sie erhalten NAals Ergebnis, aber wenn Sie passieren na.rm = TRUEin sumFunktion, werden Sie das Ergebnis erhalten , die Sie wollen.

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

Ist Ihre Frage streng theoretisch oder haben Sie ein praktisches Problem mit logischen Vektoren?

aL3xa
quelle
Ich habe versucht, ein Quiz zu bewerten. Tun Sie so etwas wie sum (youranswer == rightanswer) innerhalb einer Bewerbung.
Jyotirmoy Bhattacharya
Meine Antwort ist einfach zu lang, daher habe ich eine neue Antwort gepostet, da sie sich von der vorherigen unterscheidet.
aL3xa
6

Eine weitere Option ist die Verwendung der Zusammenfassungsfunktion. Es gibt eine Zusammenfassung der Ts, Fs und NAs.

> summary(hival)
   Mode   FALSE    TRUE    NA's 
logical    4367      53    2076 
> 
Ramrad
quelle
1
Um nur die "TRUE" -Ergebnisse zu erhalten (die als Zeichenfolge ausgegeben werden, aber auch "TRUE" in der Ausgabe enthalten) : summary(hival)["TRUE"];
Michael
0

Ich habe vor ein paar Wochen etwas Ähnliches gemacht. Hier ist eine mögliche Lösung, die von Grund auf neu geschrieben wurde. Es handelt sich also um eine Art Beta-Version oder ähnliches. Ich werde versuchen, es zu verbessern, indem ich Schleifen aus dem Code entferne ...

Die Hauptidee besteht darin, eine Funktion zu schreiben, die 2 (oder 3) Argumente akzeptiert. Die erste ist eine data.frame, die die aus dem Fragebogen gesammelten Daten enthält, und die zweite ist ein numerischer Vektor mit korrekten Antworten (dies gilt nur für Single-Choice-Fragebogen). Alternativ können Sie ein drittes Argument hinzufügen, das einen numerischen Vektor mit der endgültigen Bewertung oder data.frame mit der eingebetteten Bewertung zurückgibt.

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

Ich werde versuchen, dies auf elegantere Weise mit einer * Ply-Funktion zu tun. Beachten Sie, dass ich kein na.rmArgument vorgebracht habe ... Ich werde das tun

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

Wenden Sie nun eine Funktion an:

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

Wenn Sie das Argument data.frame übergeben, wird das geänderte data.frame zurückgegeben. Ich werde versuchen, dieses Problem zu beheben ... Hoffe, es hilft!

aL3xa
quelle
6
Einzeiler : rowSums(t(t(d)==sol), na.rm=TRUE). R Recycling-Vektor zum Vergleich. Wenn Sie deine Matrix mit Fällen in Spalten waren, wird dies vereinfacht rowSums(d==sol, na.rm=TRUE).
Marek
0

Ich hatte gerade ein bestimmtes Problem, bei dem ich die Anzahl der wahren Aussagen aus einem logischen Vektor zählen musste, und das funktionierte am besten für mich ...

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

Dies nimmt also eine Teilmenge des Objekts gene.rep.matrix und wendet einen logischen Test an, der einen logischen Vektor zurückgibt. Dieser Vektor wird als Argument für grep verwendet, das die Positionen aller TRUE-Einträge zurückgibt. Die Länge berechnet dann, wie viele Einträge grep findet, und gibt so die Anzahl der WAHREN Einträge an.

A_Skelton73
quelle
0

Es gibt auch ein Paket namens bit, das speziell für schnelle boolesche Operationen entwickelt wurde. Dies ist besonders nützlich, wenn Sie große Vektoren haben oder viele boolesche Operationen ausführen müssen.

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
quelle