Aqui está um exemplo de quadro de dados:
d <- data.frame(
x = runif(90),
grp = gl(3, 30)
)
Eu quero que o subconjunto d
contenha as linhas com os 5 principais valores de x
para cada valor de grp
.
Usando a base R, minha abordagem seria algo como:
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
Usando dplyr
, eu esperava que isso funcionasse:
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
head(n = 5)
mas ele retorna apenas as 5 primeiras linhas gerais.
Trocar head
por top_n
retorna o todo d
.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
top_n(n = 5)
Como obtenho o subconjunto correto?
fonte
Muito fácil com
data.table
também ...Ou
Ou (deve ser mais rápido para o conjunto de big data porque evita chamar
.SD
para cada grupo)Editar: Veja como se
dplyr
compara adata.table
(se alguém estiver interessado)Adicionando uma
data.table
solução marginalmente mais rápida :saída de tempo:
fonte
data.table
método que deve ser um pouco mais rápido:dt <- setorder(setDT(dd), grp, -x); dt[dt[, .I[seq_len(.N) <= 5L], grp]$V1]
data.table
método mais fácil:setDT(d)[order(-x),x[1:5],keyby = .(grp)]
:
vai baterhead
setorder
mais rápido do queorder
Você precisa encerrar
head
uma chamada parado
. No código a seguir,.
representa o grupo atual (veja a descrição...
nado
página de ajuda).Conforme mencionado por akrun,
slice
é uma alternativa.Embora eu não tenha perguntado isso, para completar, uma
data.table
versão possível é (graças a @Arun pela correção):fonte
setDT(d)[order(-x), head(.SD, 5L), by=grp]
~
e usararrange
e emgroup_by
vez dearrange_
egroup_by_
Minha abordagem na base R seria:
E usando dplyr, a abordagem com
slice
é provavelmente mais rápida, mas você também pode usar ofilter
que provavelmente será mais rápido do que usardo(head(., 5))
:benchmark dplyr
fonte
filter
requer uma função adicional, enquanto suaslice
versão não ...data.table
aqui;)top_n (n = 1) ainda retornará várias linhas para cada grupo se a variável de ordenação não for exclusiva dentro de cada grupo. Para selecionar precisamente uma ocorrência para cada grupo, adicione uma variável única a cada linha:
fonte
Mais uma
data.table
solução para destacar sua sintaxe concisa:fonte