187 votes

Comment compter les valeurs VRAIES dans un vecteur logique ?

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

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

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

Lequel préférez-vous ? Y a-t-il quelque chose d'encore mieux ?

208voto

Marek Points 18000

Le moyen le plus sûr est d'utiliser sum con na.rm = TRUE :

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

ce qui donne 1.

Il y a quelques problèmes avec les autres solutions lorsque le vecteur logique contient NA valeurs.

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)

En outre, table est moins efficace (regardez le code de l'application table fonction).

Il faut également faire attention à la solution "tableau", au cas où il n'y aurait pas de valeurs VRAIES dans le vecteur logique. Voir par exemple :

z <- c(FALSE, FALSE)
table(z)["TRUE"] # gives you `NA`

o

z <- c(NA, FALSE)
table(z)["TRUE"] # gives you `NA`

0 votes

table(c(FALSE))["TRUE"] donne NA, et non 0.

0 votes

@YossiFarjoun Oui, et c'est dans ma réponse. Ce sont des exemples pour lesquels ça ne marchera pas. Ma solution est la suivante sum(z, na.rm = TRUE)

0 votes

Hm... na.rm n'est pas disponible dans RStudio ?! il y a na.actuion, exclude, fail etc.... mais pas rm. Quelle pourrait en être la raison ?

93voto

Shane Points 40885

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

length(which(z))

Afin de fournir un contexte à la question "lequel est le plus rapide", il est toujours plus facile de faire un test par soi-même. J'ai fait le vecteur beaucoup plus grand pour la 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 

Il est donc clair qu'en utilisant sum est la meilleure approche dans ce cas. Vous pouvez également vérifier si NA comme l'a suggéré Marek.

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

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

Notez que ce qui vérifie seulement la logique TRUE Il ignore donc essentiellement les valeurs non logiques.

0 votes

BTW, il y avait un joli truc avec le timing dans la réponse de Dirk : stackoverflow.com/questions/1748590/revolution-pour-r/

13voto

f3lix Points 13634

Un autre moyen est

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

Alors que sum(z) est agréable et court, pour moi length(z[z==TRUE]) 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] est environ 200x plus lent que sum(z) .

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

6voto

aL3xa Points 10236

which est une bonne alternative, surtout lorsque l'on opère sur des matrices (voir ?which et remarquez le arr.ind ). Mais je vous suggère de vous en tenir à sum en raison de na.rm qui peut traiter NA 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 dans sum(x) vous obtiendrez NA en conséquence, mais si vous passez na.rm = TRUE sur sum vous obtiendrez le résultat que vous souhaitez.

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

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

0 votes

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

0 votes

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

0voto

aL3xa Points 10236

J'ai fait quelque chose de similaire il y a quelques semaines. Voici une solution possible, elle est écrite à partir de zéro, donc c'est une sorte de beta-release 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.frame qui contient les données recueillies dans le questionnaire, et le second est un vecteur numérique avec les réponses correctes (ceci n'est applicable que pour les questionnaires à choix unique). Vous pouvez également ajouter un troisième argument qui renverra un vecteur numérique avec le score final, ou un data.frame avec le 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 mis na.rm argument... Je le ferai.

# 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 retournera le data.frame modifié. Je vais essayer de corriger cela... J'espère que cela vous aidera !

6 votes

Un seul mot : rowSums(t(t(d)==sol), na.rm=TRUE) . R recyclé à titre de comparaison. Si votre d sont des matrices avec des cas en colonnes, alors cela se simplifie en rowSums(d==sol, na.rm=TRUE) .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X