Voici un exemple de cadre de données:
d <- data.frame(
x = runif(90),
grp = gl(3, 30)
)
Je veux que le sous-ensemble d
contienne les lignes avec les 5 premières valeurs de x
pour chaque valeur de grp
.
En utilisant base-R, mon approche serait quelque chose comme:
ordered <- d[order(d$x, decreasing = TRUE), ]
splits <- split(ordered, ordered$grp)
heads <- lapply(splits, head)
do.call(rbind, heads)
## x grp
## 1.19 0.8879631 1
## 1.4 0.8844818 1
## 1.12 0.8596197 1
## 1.26 0.8481809 1
## 1.18 0.8461516 1
## 1.29 0.8317092 1
## 2.31 0.9751049 2
## 2.34 0.9269764 2
## 2.57 0.8964114 2
## 2.58 0.8896466 2
## 2.45 0.8888834 2
## 2.35 0.8706823 2
## 3.74 0.9884852 3
## 3.73 0.9837653 3
## 3.83 0.9375398 3
## 3.64 0.9229036 3
## 3.69 0.8021373 3
## 3.86 0.7418946 3
En utilisant dplyr
, je m'attendais à ce que cela fonctionne:
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
head(n = 5)
mais il ne renvoie que les 5 premières lignes globales.
L'échange head
pour top_n
renvoie la totalité de d
.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
top_n(n = 5)
Comment obtenir le sous-ensemble correct?
la source
Assez facile avec
data.table
aussi ...Ou
Ou (devrait être plus rapide pour l'ensemble de données volumineuses car éviter d'appeler
.SD
pour chaque groupe)Edit: voici comment se
dplyr
compare àdata.table
(si quelqu'un est intéressé)Ajout d'une
data.table
solution légèrement plus rapide :sortie de synchronisation:
la source
data.table
méthode qui devrait être légèrement plus rapide:dt <- setorder(setDT(dd), grp, -x); dt[dt[, .I[seq_len(.N) <= 5L], grp]$V1]
data.table
méthode plus facile:setDT(d)[order(-x),x[1:5],keyby = .(grp)]
:
battrahead
setorder
plus rapide queorder
Vous devez terminer
head
un appel àdo
. Dans le code suivant,.
représente le groupe actuel (voir la description de...
dans lado
page d'aide).Comme mentionné par akrun,
slice
est une alternative.Bien que je ne l'ai pas demandé, pour être complet, une
data.table
version possible est (merci à @Arun pour le correctif):la source
setDT(d)[order(-x), head(.SD, 5L), by=grp]
~
et utilisezarrange
etgroup_by
au lieu dearrange_
etgroup_by_
Mon approche en base R serait:
Et en utilisant dplyr, l'approche avec
slice
est probablement la plus rapide, mais vous pouvez également utiliserfilter
ce qui sera probablement plus rapide que d'utiliserdo(head(., 5))
:benchmark dplyr
la source
filter
nécessite une fonction supplémentaire, alors que votreslice
version ne le fait pas ...data.table
ici;)top_n (n = 1) retourne toujours plusieurs lignes pour chaque groupe , si la commande variable ne soit pas unique dans chaque groupe. Afin de sélectionner précisément une occurrence pour chaque groupe, ajoutez une variable unique à chaque ligne:
la source
Une autre
data.table
solution pour mettre en évidence sa syntaxe concise:la source