studio - superponer graficas en r
Obteniendo los mejores valores por grupo (6)
Aquí hay un marco de datos de muestra:
d <- data.frame(
x = runif(90),
grp = gl(3, 30)
)
Quiero el subconjunto de
d
contiene las filas con los 5 valores principales de
x
para cada valor de
grp
.
Usando base-R, mi enfoque sería 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
, esperaba que esto funcionara:
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
head(n = 5)
pero solo devuelve las 5 filas superiores generales.
Cambiar la
head
por
top_n
devuelve todo
d
.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
top_n(n = 5)
¿Cómo obtengo el subconjunto correcto?
Bastante fácil con
data.table
también ...
library(data.table)
setorder(setDT(d), -x)[, head(.SD, 5), keyby = grp]
O
setorder(setDT(d), grp, -x)[, head(.SD, 5), by = grp]
O (debe ser más rápido para el conjunto de datos grandes porque evita llamar a
.SD
para cada grupo)
setorder(setDT(d), grp, -x)[, indx := seq_len(.N), by = grp][indx <= 5]
Editar: así
es como se compara
data.table
con
data.table
(si alguien está interesado)
set.seed(123)
d <- data.frame(
x = runif(1e6),
grp = sample(1e4, 1e6, TRUE))
library(dplyr)
library(microbenchmark)
library(data.table)
dd <- copy(d)
microbenchmark(
top_n = {d %>%
group_by(grp) %>%
top_n(n = 5, wt = x)},
dohead = {d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
do(head(., n = 5))},
slice = {d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
slice(1:5)},
filter = {d %>%
arrange(desc(x)) %>%
group_by(grp) %>%
filter(row_number() <= 5L)},
data.table1 = setorder(setDT(dd), -x)[, head(.SD, 5L), keyby = grp],
data.table2 = setorder(setDT(dd), grp, -x)[, head(.SD, 5L), grp],
data.table3 = setorder(setDT(dd), grp, -x)[, indx := seq_len(.N), grp][indx <= 5L],
times = 10,
unit = "relative"
)
# expr min lq mean median uq max neval
# top_n 24.246401 24.492972 16.300391 24.441351 11.749050 7.644748 10
# dohead 122.891381 120.329722 77.763843 115.621635 54.996588 34.114738 10
# slice 27.365711 26.839443 17.714303 26.433924 12.628934 7.899619 10
# filter 27.755171 27.225461 17.936295 26.363739 12.935709 7.969806 10
# data.table1 13.753046 16.631143 10.775278 16.330942 8.359951 5.077140 10
# data.table2 12.047111 11.944557 7.862302 11.653385 5.509432 3.642733 10
# data.table3 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10
Agregar una solución
data.table
marginalmente más
data.table
:
set.seed(123L)
d <- data.frame(
x = runif(1e8),
grp = sample(1e4, 1e8, TRUE))
setDT(d)
setorder(d, grp, -x)
dd <- copy(d)
library(microbenchmark)
microbenchmark(
data.table3 = d[, indx := seq_len(.N), grp][indx <= 5L],
data.table4 = dd[dd[, .I[seq_len(.N) <= 5L], grp]$V1],
times = 10L
)
salida de tiempo:
Unit: milliseconds
expr min lq mean median uq max neval
data.table3 826.2148 865.6334 950.1380 902.1689 1006.1237 1260.129 10
data.table4 729.3229 783.7000 859.2084 823.1635 966.8239 1014.397 10
De
?top_n
, sobre el argumento
wt
:
La variable a usar para ordenar [...] por defecto es la última variable en el tbl ".
La última variable en su conjunto de datos es "grp", que no es la variable que desea clasificar, y es por eso que su intento de
top_n
"devuelve la totalidad de d".
Por lo tanto, si desea clasificar por "x" en su conjunto de datos, debe especificar
wt = x
.
set.seed(123)
d <- data.frame(
x = runif(90),
grp = gl(3, 30))
d %>%
group_by(grp) %>%
top_n(n = 5, wt = x)
# x grp
# 1 0.9404673 1
# 2 0.9568333 1
# 3 0.8998250 1
# 4 0.9545036 1
# 5 0.9942698 1
# 6 0.9630242 2
# 7 0.9022990 2
# 8 0.8578277 2
# 9 0.7989248 2
# 10 0.8950454 2
# 11 0.8146400 3
# 12 0.8123895 3
# 13 0.9849570 3
# 14 0.8930511 3
# 15 0.8864691 3
Mi enfoque en la base R sería:
d %>%
arrange(desc(x)) %>%
group_by(grp) %>%
filter(row_number() <= 5L)
Y usando dplyr, el enfoque con el
slice
es probablemente más rápido, pero también podría usar un
filter
que probablemente será más rápido que usar
do(head(., 5))
:
set.seed(123)
d <- data.frame(
x = runif(1e6),
grp = sample(1e4, 1e6, TRUE))
library(microbenchmark)
microbenchmark(
top_n = {d %>%
group_by(grp) %>%
top_n(n = 5, wt = x)},
dohead = {d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
do(head(., n = 5))},
slice = {d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
slice(1:5)},
filter = {d %>%
arrange(desc(x)) %>%
group_by(grp) %>%
filter(row_number() <= 5L)},
times = 10,
unit = "relative"
)
Unit: relative
expr min lq median uq max neval
top_n 1.042735 1.075366 1.082113 1.085072 1.000846 10
dohead 18.663825 19.342854 19.511495 19.840377 17.433518 10
slice 1.000000 1.000000 1.000000 1.000000 1.000000 10
filter 1.048556 1.044113 1.042184 1.180474 1.053378 10
dplyr benchmark
set.seed(123)
d <- data.frame(
x = runif(90),
grp = gl(3, 30))
d %>%
mutate(rn = row_number()) %>%
group_by(grp) %>%
top_n(n = 1, wt = rn)
Necesita envolver la
head
en una llamada para
do
.
En el siguiente código,.
representa el grupo actual (consulte la descripción de
...
en la página de ayuda).
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
do(head(., n = 5))
Como mencionó akrun, el
slice
es una alternativa.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
slice(1:5)
Aunque no pregunté esto, para completar, una posible versión de
data.table
es (gracias a @Arun por la solución):
ordered <- d[order(d$x, decreasing = TRUE), ]
ordered[ave(d$x, d$grp, FUN = seq_along) <= 5L,]
Una solución más de
data.table
para resaltar su sintaxis concisa:
setDT(d) d[order(-x), .SD[1:5], grp]
top_n (n = 1) aún devolverá varias filas para cada grupo si la variable de pedido no es única dentro de cada grupo. Para seleccionar con precisión una ocurrencia para cada grupo, agregue una variable única a cada fila:
set.seed(123) d <- data.frame( x = runif(90), grp = gl(3, 30)) d %>% mutate(rn = row_number()) %>% group_by(grp) %>% top_n(n = 1, wt = rn)