Dans R, quel est le moyen le plus efficace/idiomatique de compter le nombre de valeurs TRUE
dans un vecteur logique? Je peux penser de deux manières:
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 encore mieux?
Il existe certains problèmes lorsque le vecteur logique contient les valeurs NA
.
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)
Donc, je pense 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 la solution table
est moins efficace (regardez le code de la fonction table
.).
De plus, vous devriez faire attention avec la solution "table", au cas où il n'y aurait pas de valeur TRUE dans le vecteur logique. Supposons que z <- c(NA, FALSE, NA)
ou simplement z <- c(FALSE, FALSE)
, puis table(z)["TRUE"]
vous donne NA
dans les deux cas.
Une autre option qui n’a pas été mentionnée est d’utiliser which
:
length(which(z))
Juste pour fournir un contexte à la question "qui est plus rapide", il est toujours plus facile de se tester. 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
Si clairement utiliser sum
est la meilleure approche dans ce cas. Vous pouvez également vouloir vérifier les valeurs de NA
comme suggéré par Marek.
Juste pour ajouter une note concernant les valeurs NA et la fonction which
:
> 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.
Une autre façon 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 aussi simple, cela ne fait pas vraiment la différence ...
S'il s'agit d'un grand vecteur, vous devriez probablement utiliser la solution la plus rapide, sum(z)
. length(z[z==TRUE])
est environ 10x 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.
Une autre option consiste à utiliser la fonction de résumé. Il résume les Ts, les F et les NA.
> summary(hival)
Mode FALSE TRUE NA's
logical 4367 53 2076
>
which
est une bonne alternative, en particulier lorsque vous utilisez des matrices (vérifiez ?which
et notez l'argument arr.ind
.). Mais je vous suggère de vous en tenir à sum
, en raison de l'argument de na.rm
pouvant gérer les NA
en 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 NA
, mais si vous passez na.rm = TRUE
à la fonction sum
, vous obtiendrez le résultat souhaité.
> 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?
J'ai fait quelque chose de similaire il y a quelques semaines. Voici une solution possible, écrite à partir de rien, donc 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.frame
qui contient les données recueillies à partir du questionnaire, et le second est un vecteur numérique avec des réponses correctes (ceci ne s'applique que pour le questionnaire à choix unique). Vous pouvez également ajouter un troisième argument renvoyant 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 faire cela d'une manière plus élégante avec une fonction * ply. Notez que je n'ai pas mis l'argument na.rm
... le fera
# 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 data.frame modifié. Je vais essayer de réparer celui-ci ... J'espère que ça aide!
Je viens d'avoir un problème particulier où je devais compter le nombre d'énoncés vrais à partir d'un vecteur logique et cela fonctionnait mieux pour moi ...
length(grep(TRUE, (gene.rep.matrix[i,1:6] > 1))) > 5
Donc, cela prend un sous-ensemble de l'objet gene.rep.matrix et applique un test logique, renvoyant un vecteur logique. Ce vecteur est mis en argument de grep, qui renvoie les emplacements de toutes les entrées TRUE. Longueur calcule ensuite le nombre d'entrées trouvées par grep, donnant ainsi le nombre d'entrées VRAIES.