dplyr filtre: Obtenir des lignes avec un minimum de variable, mais seulement la première si minima multiples
Je veux faire un groupe de filtre à l'aide de dplyr
, de manière que, dans chaque groupe, seulement cette ligne est renvoyée, qui a la valeur minimale de la variable x
.
Mon problème est le suivant: Comme prévu, dans le cas de plusieurs minima tous lignes avec la valeur minimale sont retournés. Mais dans mon cas, je veux seulement la première ligne si plusieurs minima sont présents.
Voici un exemple:
df <- data.frame(
A=c("A", "A", "A", "B", "B", "B", "C", "C", "C"),
x=c(1, 1, 2, 2, 3, 4, 5, 5, 5),
y=rnorm(9)
)
library(dplyr)
df.g <- group_by(df, A)
filter(df.g, x == min(x))
Comme prévu, tous les minima sont de retour:
Source: local data frame [6 x 3]
Groups: A
A x y
1 A 1 -1.04584335
2 A 1 0.97949399
3 B 2 0.79600971
4 C 5 -0.08655151
5 C 5 0.16649962
6 C 5 -0.05948012
Avec ddply, j'aurais approche de la tâche de cette façon:
library(plyr)
ddply(df, .(A), function(z) {
z[z$x == min(z$x), ][1, ]
})
... qui fonctionne:
A x y
1 A 1 -1.04584335
2 B 2 0.79600971
3 C 5 -0.08655151
Q: Est-il une façon d'aborder cette question dans dplyr? (Pour des raisons de vitesse)
filter(df.g, rank(x) == 1)
?- Merci. Je savais qu'il y aurait une solution facile 😉
- ne
rank(x)==1
donner les résultats escomptés? - il nécessite
ties=first
argument. FelixS, si vous allez pour la vitesse,rank
est une mauvaise idée de calcul de plus en plus exigeants quemin
(ou)which.min
. - En fait, vous voulez probablement
min_rank()
. @Arun: dplyr fournit une mise en œuvre interne demin_rank()
qui devrait être beaucoup assez rapide. - 1) je ne pense pas que
min_rank
aide ici. Il a besoin de la première valeur min (regardezplyr
solution). 2) quel que soit le langage de programmation que vous écrivez, la complexité algorithmique derank
(liens=min, max, le premier, etc..) sera plus grand que juste l'informatiquemin
. - prématuré d'optimisation est ...
- pas de vous suivre.
- Vrai, seulement
rank(x, ties.method="first")==1
œuvres, comme min et min_rank ne font pas de distinction entre plusieurs minima. - la droite. Mais c'est assez cher (parce que c'est de trier toutes les valeurs pour chaque groupe). Vous devriez être en utilisant
which.min
. Mais je ne sais pas d'unedplyr
solution. Unplyr
cas serait:ddply(df, .(A), function(z) z[which.min(z$x), ])
- c'est en supposant qu'un temps O(n lg n) algorithme effectuez beaucoup plus de mal qu'un algorithme O(n) sans plus d'informations sur le domaine
- Désolé, j'étais en train de penser de
row_number()
- c'est l'équivalent derank(ties = "first")
mais être mis en œuvre de manière plus efficace en C++. - Je ne vois pas comment cela vous fait envisager
which.min
être prématuré d'optimisation. Autant que je sache, c'est un choix naturel, agréable à lire, facile à comprendre, rapide, comme il arrive à O(n) trop.
Vous devez vous connecter pour publier un commentaire.
Mise à jour
Avec dplyr >= 0.3, vous pouvez utiliser le
slice
fonction en combinaison avecwhich.min
, qui serait mon approche préférée pour cette tâche:Réponse originale à cette question
Pour l'échantillon de données, il est également possible d'utiliser deux
filter
les uns après les autres:do(head)
plus facile à lire,df %>% group_by(A) %>% filter(x == min(x)) %>% do(head(.,1))
Error: expecting a single value
) - savez-vous pourquoi?dplyr_0.2, magrittr_1.0.0
top_n
ici, mais en raison des liens de cette méthode est probablement le gagnant clair, certainement en termes de performance (par rapport à l'arrange %>% slice
).Juste pour être complet: Voici la dernière
dplyr
solution, dérivée à partir des commentaires de @hadley et @Arun:Pour ce que ça vaut, voici un
data.table
solution, pour ceux qui pourraient être intéressés:Ceci peut être accompli en utilisant
row_number
combiné avecgroup_by
.row_number
poignées des liens par l'attribution d'un grade non seulement par la valeur, mais aussi par l'ordre relatif dans le vecteur. Pour obtenir la première ligne de chaque groupe avec la valeur minimale dex
:Pour plus d'informations, voir la dplyr vignette sur les fonctions de la fenêtre.
J'aime sqldf pour sa simplicité..
De sortie:
Une autre façon de faire:
Résultat:
Pourrait également être facilement adapté pour l'obtention de la ligne de chaque groupe avec une valeur maximale.
Venu ici à la recherche d'un moyen de le faire avec plus d'un. Cela donnera à la partie inférieure de dix, rompre les liens de la dernière, je crois