[R] dplyr 패키지의 윈도우 함수 (1) Ranking and Ordering Window Functions : row_number(), min_rank(), dense_rank(), cume_dist(), percent_rank(), ntile()
R 분석과 프로그래밍/R 데이터 전처리 2016. 10. 10. 00:50R dplyr 패키지에 대해서 연재를 하고 있는데요, 이번 포스팅에서는 Window Function 에 대해서 소개를 하겠습니다.
Window function 은 n개의 input을 받아서 n 개의 output을 반환하는 함수입니다. 순위(rank)를 구하는 함수나 누적합(cumulative sum)을 구하는 함수가 대표적인 Window function입니다.
이와 대비되는 Aggregation function 은 n개의 input을 받아서 1개의 output 을 반환합니다. 요약통계량의 평균(mean), 합계(sum), 개수 세기(count) 등이 Aggregation function 에 속합니다.
[ Window function vs. Aggregation function ]
R의 {dplyr} package에서 사용할 수 있는 Window function에는 (1) Ranking and Ordering, (2) Lead and Lag, (3) Cumulative aggregates, (4) Recycled aggregates 등의 4가지 유형이 있습니다.
[ Types of Window functions in R {dplyr} package ]
차례대로 설명을 할 건데요, 한꺼번에 모두를 소개하기는 양이 꽤 많으니 이번에는 (1) Ranking and Ordering 의 row_number(), min_rank(), dense_rank(), cume_dist(), percent_rank(), ntile() 함수에 대해서만 우선 소개해드리겠습니다.
- row_number(), min_rank(), dense_rank() 의 3개 함수는 순위에 대한 index를 반환하고,
- cume_dist(), percent_rank() 의 2개 함수는 순위에 대한 '0~1' 사이의 비율을 반환하며,
- ntile() 은 n개의 동일한 개수로 데이터셋을 나누어줍니다.
먼저 순위(ranking) 에 대한 index 를 반환해주는 함수 3개를 살펴보겠습니다. 동일한 값이 있을 경우에 이를 처리하는 방법이 함수별로 차이가 있습니다.
말로 일일이 설명하기가 좀 어려운데요, 아래에 c(1, 1, 1, 5, 5, 9, 7) 의 동일한 값 '1'이 3개, '5'가 2개 존재하는 간단한 벡터를 가지고 함수별로 어떤 결과를 반환하는지 비교해보는 예를 준비했습니다. 색깔 칠해놓은 동일 값들을 어떻게 index 처리하는지 유심히 비교해보시고요, 분석 목적에 맞는 함수를 선택해서 사용하시기 바랍니다.
(1-1) row_number() : 순위(ranking) index 반환, 동일값에 대해서는 '1, 2, 3, ...' 처리 |
##-------------------------------------------- library(dplyr) # if there are ties, ties can be handled in several ways # (1-1) row_number()
|
|
(1-2) min_rank() : 순위(ranking) index 반환, 동일값에 대해서는 '1, 1, 1, 4, 4,...' 처리 |
# (1-2) min_rank() min_rank(x)
|
[1] 1 1 1 4 4 7 6
|
(1-3) dense_rank() : 순위(ranking) index 반환, 동일값에 대해서는 '1, 1, 1, 2, 2,...' 처리 |
# (1-3) dense_rank()
dense_rank(x)
|
[1] 1 1 1 2 2 4 3
|
참고로 {base} 패키지의 rank() 함수도 동일한 기능을 제공하는데요, ties.method = c("average", "first", "random", "max", "min") 매개변수 별로 어떤 결과가 나오는 지는 http://rfriend.tistory.com/28 포스팅의 제일 하단 예시를 참고하시기 바랍니다.
아래 두개의 cume_dist(), percent_rank() 함수는 순위(ranking)에 대한 0~1 사이의 비율을 반환하는데요, 둘 사이에 미묘한 차이가 있으니 유심히 살펴보시기 바랍니다.
(1-4) cume_dist() : 현재 값보다 작거나 동일한 값의 순위(ranking) 상의 비율 (0~1) |
# (1-4) cume_dist()
|
[1] 0.4285714 0.4285714 0.4285714 0.7142857 0.7142857 1.0000000 0.8571429
|
위의 결과가 어떻게 나왔냐 하면요, 전체 7개의 숫자 중에서 첫번째 순위에 속하는 '1'이 3개 있으므로 3/7 = 0.4285714 가 나온 것입니다. 두번째 순위에 속하는 '5'는 2개가 있으며, 5보다 작은 수로 첫번째 순위의 '1'이 3개가 있으므로 '5'의 2개와 ''1'의 3개를 합친 5개를 총 개수인 7로 나눈 5/7 = 0.7142857이 나온 것입니다. (말로 설명하기가 참 어렵네요. -_-;)
> 3/7 # ordering like as c(1, 1, 1) [1] 0.4285714 > 5/7 # ordering like as c(1, 1, 1, 5, 5) [1] 0.7142857 > 6/7 # ordering like as c(1, 1, 1, 5, 5, 7) [1] 0.8571429 > 7/7 # ordering like as c(1, 1, 1, 5, 5, 7, 9) [1] 1
|
참고로, "패키지명:::함수명"을 실행하면 R 함수의 내부 소스 코드 (generic function)를 볼 수 있습니다.
cume_dist() 함수는 {base} 패키지의 rank(x, ties.method = "max") 를 가져다고 총 개수로 나누어준 것임을 알 수 있습니다.
> dplyr:::cume_dist function (x) { rank(x, ties.method = "max", na.last = "keep")/sum(!is.na(x)) } <environment: namespace:dplyr>
|
(1-5) percent_rank() : min_rank() 기준의 순위(ranking)에 대한 비율(0~1) |
# (1-5) percent_rank()
percent_rank(x)
|
[1] 0.0000000 0.0000000 0.0000000 0.5000000 0.5000000 1.0000000 0.8333333
|
percent_rank() 함수의 내부 소스 코드를 들여다보면 아래와 같이 dplyr패키지의 min_rank() 값에서 1을 뺀 후에, 이를 총 관측값 개수에서 1을 뺀 값으로 나누었군요. 소스 코드를 보지 않고서는 연산 로직을 알기가 쉽지 않은 경우입니다. (위 결과가 어떻게 나온건지 한참을 고민했네요... -_-;)
이제 왜 첫번째 순위의 '1'이 '0.0000000'이 나왔는지 아시겠지요? min_rank(x) 가
[1] 1 1 1 4 4 7 6
의 값을 반환하므로 분자에 해당하는 (min_rank(x) - 1) = 1 - 1 = 0 이 되어, 전체 값이 '0'이 된 것입니다.
> dplyr:::percent_rank function (x) { (min_rank(x) - 1)/(sum(!is.na(x)) - 1) } <environment: namespace:dplyr>
|
(1-6) ntile() : 동일한 개수를 가진 n개의 sub 데이터로 나누기 |
Cars93 데이터프레임의 가격(Price)를 기준으로 정렬(ordering)이 된 상태에서 동일한 개수를 가진 4개의 sub group으로 나누려고 할 때 ntile() 함수를 쓰면 됩니다. mutate() 함수를 사용해서 quartile 이라는 새로운 칼럼을 생성한 후에 Cars93_quartile 이라는 새로운 데이터프레임을 생성해보는 예제를 만들어보았습니다.
# (1-6) ntile() : divides the data up into n evenly sized buckets library(MASS) # to use Cars93 data frame
Cars93_quartile <- Cars93[ ,c("Manufacturer", "Model", "Type", "Price")] %>%
|
quartile 이라는 새로운 칼럼에 가격(Price)를 기준으로 순위 정렬이 된 상태에서 4등분된 buckets별로 몇 개씩 자동차가 들어가 있는지 한번 세어보겠습니다. (summarise(n = n()), tally(), count() 함수 중에 편한거 사용하면 되겠습니다. ) 1번째 bucket 에는 24개 자동차가 들어가 있고, 나머지 3개 bucket에는 23개씩 동일하게 들어가 있습니다. (총 93개로 정확히 4등분 할 수 없는 경우라서 그렇습니다)
# counting up by quartiles
# A tibble: 4 x 2 quartile n <int> <int> 1 1 24 2 2 23 3 3 23 4 4 23
Cars93_quartile %>% group_by(quartile) %>% tally() # same result
|
이상으로 dplyr 패키지의 Window function 중에서 첫번째로 ranking and ordering 관련 함수들인 row_number(), min_rank(), dense_rank(), cume_dist(), percent_rank(), ntile() 에 대한 소개를 마치겠습니다. 많은 도움이 되었기를 바랍니다.
다음번 포스팅에서는 dplyr 패키지의 Window function 의 두번째 시간으로 'Lead and Lag'에 대해서 알아보겠습니다.
이번 포스팅에 도움이 되었다면 아래의 '공감 ~ ♡'를 꾹 눌러주세요. ^^