Comment compter les valeurs TRUE dans un vecteur logique


160

Dans R, quelle est la manière la plus efficace / idiomatique de compter le nombre de TRUEvaleurs dans un vecteur logique? Je peux penser à deux façons:

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

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

Lequel préfères-tu? Y a-t-il quelque chose d'encore mieux?

Réponses:


174

Il y a quelques problèmes lorsque le vecteur logique contient des NAvaleurs.
Voir par exemple:

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)

Je pense donc que le plus sûr est d'utiliser na.rm = TRUE:

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

(ce qui donne 1). Je pense que cette tablesolution est moins efficace (regardez le code de tablefonction).

De plus, vous devez être prudent avec la solution "table", au cas où il n'y aurait pas de valeurs VRAIES dans le vecteur logique. Supposons z <- c(NA, FALSE, NA)ou simplement z <- c(FALSE, FALSE), puis table(z)["TRUE"]vous donne NApour les deux cas.


table(c(FALSE))["TRUE"]donne NA, pas 0.
Yossi Farjoun

@YossiFarjoun Oui, et c'est dans ma réponse. Ce sont des exemples pour lesquels cela ne fonctionnera pas. Ma sollution estsum(z, na.rm = TRUE)
Marek

84

Une autre option qui n'a pas été mentionnée est d'utiliser which:

length(which(z))

Juste pour donner un peu de contexte sur la question "qui est le plus rapide", il est toujours plus facile de se tester. J'ai agrandi le vecteur pour comparaison:

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 

L'utilisation sumest donc clairement la meilleure approche dans ce cas. Vous pouvez également vérifier les NAvaleurs comme Marek l'a suggéré.

Juste pour ajouter une note concernant les valeurs NA et la whichfonction:

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

Notez ce qui ne vérifie que la logique TRUE, donc il ignore essentiellement les valeurs non logiques.


BTW, il y avait une belle astuce avec le timing dans la réponse de Dirk: stackoverflow.com/questions/1748590/revolution-for-r/...
Marek

12

Une autre façon est

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

Bien que ce sum(z) soit beau et court, pour moi, length(z[z==TRUE])c'est plus explicite. Cependant, je pense qu'avec une tâche simple comme celle-ci, cela ne fait pas vraiment de différence ...

S'il s'agit d'un grand vecteur, vous devriez probablement opter pour la solution la plus rapide, à savoir sum(z). length(z[z==TRUE])est environ 10 fois plus lent et table(z)[TRUE]environ 200 fois plus lent que sum(z).

En résumé, sum(z)c'est le plus rapide à taper et à exécuter.


6

whichest une bonne alternative, surtout lorsque vous opérez sur des matrices (vérifiez ?whichet notez l' arr.indargument). Mais je suggère que vous vous en teniez sum, à cause de l' na.rmargument qui peut gérer NAles dans un vecteur logique. Par exemple:

# 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 vous tapez sum(x)vous obtiendrez NAcomme résultat, mais si vous passez na.rm = TRUEen sumfonction, vous obtiendrez le résultat que vous voulez.

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

Votre question est-elle strictement théorique ou vous avez un problème pratique concernant les vecteurs logiques?


J'essayais de noter un quiz. Faire quelque chose comme sum (youranswer == rightanswer) dans une application.
Jyotirmoy Bhattacharya

Ma réponse est trop longue, j'ai donc posté une nouvelle réponse, car elle diffère de la précédente.
aL3xa

6

Une autre option consiste à utiliser la fonction de résumé. Il donne un résumé des Ts, F et NA.

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

1
De plus, pour obtenir uniquement les résultats "TRUE" (qui seront affichés sous forme de chaîne, mais qui incluent également "TRUE" en sortie) summary(hival)["TRUE"]:;
michael

0

J'ai fait quelque chose de similaire il y a quelques semaines. Voici une solution possible, c'est écrit à partir de zéro, donc c'est une sorte de version bêta ou quelque chose comme ça. Je vais essayer de l'améliorer en supprimant les boucles du code ...

L'idée principale est d'écrire une fonction qui prendra 2 (ou 3) arguments. Le premier est un data.framequi contient les données recueillies à partir du questionnaire, et le second est un vecteur numérique avec des réponses correctes (cela n'est applicable que pour le questionnaire à choix unique). Vous pouvez également ajouter un troisième argument qui renverra un vecteur numérique avec le score final, ou data.frame avec un score intégré.

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

Je vais essayer de le faire d'une manière plus élégante avec une fonction * ply. Remarquez que je n'ai pas na.rmargumenté ...

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

Maintenant, appliquez une fonction:

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

Si vous passez l'argument data.frame, il renverra data.frame modifié. Je vais essayer de réparer celui-ci ... J'espère que cela aide!


6
One-liner: rowSums(t(t(d)==sol), na.rm=TRUE). Vecteur de recyclage R pour comparaison. Si vous détiez une matrice avec des cas dans des colonnes, cela se simplifie en rowSums(d==sol, na.rm=TRUE).
Marek

0

J'ai juste eu un problème particulier où je devais compter le nombre de déclarations vraies à partir d'un vecteur logique et cela a fonctionné mieux pour moi ...

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

Cela prend donc un sous-ensemble de l'objet gene.rep.matrix, et applique un test logique, renvoyant un vecteur logique. Ce vecteur est mis comme argument à grep, qui renvoie les emplacements de toutes les entrées TRUE. Length calcule ensuite le nombre d'entrées que grep trouve, donnant ainsi le nombre d'entrées TRUE.


0

Il existe également un package appelé bitqui est spécialement conçu pour les opérations booléennes rapides. C'est particulièrement utile si vous avez des vecteurs volumineux ou si vous devez effectuer de nombreuses opérations booléennes.

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
})
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.