그룹별 최상위 값 가져오기
다음은 샘플 데이터 프레임입니다.
d <- data.frame(
x = runif(90),
grp = gl(3, 30)
)
다음의 서브셋을 원합니다.d
값이 상위 5개인 행 포함x
각각의 가치에 대하여grp
.
Base-R을 사용하면 다음과 같은 접근 방식을 사용할 수 있습니다.
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
사용.dplyr
나는 이것이 효과가 있을 것으로 예상했습니다.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
head(n = 5)
그러나 전체 상위 5개 행만 반환합니다.
스와핑head
의 전액을 반환하기 위해d
.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
top_n(n = 5)
올바른 하위 집합을 얻으려면 어떻게 해야 합니까?
dplyr 1.0.0부터, "slice_min()
그리고.slice_max()
변수의 최소값 또는 최대값이 있는 행을 선택하여 혼동에서 이어받습니다.top_n().
"
d %>% group_by(grp) %>% slice_max(order_by = x, n = 5)
# # A tibble: 15 x 2
# # Groups: grp [3]
# x grp
# <dbl> <fct>
# 1 0.994 1
# 2 0.957 1
# 3 0.955 1
# 4 0.940 1
# 5 0.900 1
# 6 0.963 2
# 7 0.902 2
# 8 0.895 2
# 9 0.858 2
# 10 0.799 2
# 11 0.985 3
# 12 0.893 3
# 13 0.886 3
# 14 0.815 3
# 15 0.812 3
사전dplyr 1.0.0
사용.top_n
:
부터?top_n
에 관하여wt
인수:
[...] 순서를 지정하는 데 사용할 변수는 기본적으로 tbl"의 마지막 변수입니다.
데이터 집합의 마지막 변수는 "grp"이며, 이는 순위를 매길 변수가 아니며, 이 때문에 다음과 같은 변수가 있습니다.top_n
"d 전체를 공격"하려고 시도합니다.따라서 데이터 집합에서 "x"로 순위를 매길 경우 다음과 같이 지정해야 합니다.wt = x
.
d %>%
group_by(grp) %>%
top_n(n = 5, wt = x)
데이터:
set.seed(123)
d <- data.frame(
x = runif(90),
grp = gl(3, 30))
매우 간단합니다.data.table
너무...
library(data.table)
setorder(setDT(d), -x)[, head(.SD, 5), keyby = grp]
또는
setorder(setDT(d), grp, -x)[, head(.SD, 5), by = grp]
또는 (호출을 방지하기 때문에 빅데이터 세트의 경우 속도가 빨라야 함).SD
각 그룹에 대해)
setorder(setDT(d), grp, -x)[, indx := seq_len(.N), by = grp][indx <= 5]
편집: 방법은 다음과 같습니다.dplyr
와 비교하여data.table
(관심 있는 사람이 있는 경우)
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
조금 더 빠르게 추가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
)
타이밍 출력:
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
포장이 필요합니다.head
…을 방문하여다음 코드에서,.
현재 그룹을 나타냅니다(설명 참조)....
에서do
도움말 페이지).
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
do(head(., n = 5))
아크룬이 언급했듯이,slice
대안입니다.
d %>%
arrange_(~ desc(x)) %>%
group_by_(~ grp) %>%
slice(1:5)
내가 이것을 묻지는 않았지만, 완성도를 위해, 가능성.data.table
버전은 다음과 같습니다(수정을 위해 @Arun 덕분).
setDT(d)[order(-x), head(.SD, 5), by = grp]
기본 R에 대한 제 접근 방식은 다음과 같습니다.
ordered <- d[order(d$x, decreasing = TRUE), ]
ordered[ave(d$x, d$grp, FUN = seq_along) <= 5L,]
그리고 dplyr을 사용하는 접근법은slice
아마도 가장 빠르겠지만, 당신은 또한 사용할 수 있습니다.filter
사용하는 것보다 더 빠를 것 같습니다.do(head(., 5))
:
d %>%
arrange(desc(x)) %>%
group_by(grp) %>%
filter(row_number() <= 5L)
dplyr 벤치마크
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
top_n(n = 1)은 순서 변수가 각 그룹 내에서 고유하지 않은 경우에도 각 그룹에 대해 여러 행을 반환합니다.각 그룹에 대해 정확하게 하나의 발생을 선택하려면 각 행에 고유 변수를 추가합니다.
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)
하나 더data.table
간결한 구문을 강조하는 솔루션:
setDT(d)
d[order(-x), .SD[1:5], grp]
언급URL : https://stackoverflow.com/questions/27766054/getting-the-top-values-by-group
'programing' 카테고리의 다른 글
MySQL/Maria에서만 명령을 프로시저로 제한DB (0) | 2023.06.15 |
---|---|
typescript tserver는 무엇에 관한 것입니까? (0) | 2023.06.15 |
Vuexgetter를 사용하여 하나의 요소를 검색하는 동안 오류가 발생했습니다. (0) | 2023.06.15 |
oEmbed Wordpress 표시 오류 (0) | 2023.06.15 |
pow()는 여기서 하나씩 나온 것 같습니다. (0) | 2023.06.15 |