[R] dplyr 패키지의 윈도우 함수 (2) Lead and Lag : lead() 함수, lag() 함수
R 분석과 프로그래밍/R 데이터 전처리 2016. 10. 22. 18:51window function은 n개의 행을 input으로 받아서 n개의 행을 가진 output을 반환하는 함수를 말합니다.
지난번 포스팅에서는 dplyr package의 window function 중에서 Ranking and Ordering을 하는 함수들로서 row_number(), min_rank(), dense_rank(), cume_dist(), percent_rank(), ntile() 에 대해서 알아보았습니다. (바로가기 http://rfriend.tistory.com/241)
이번 포스팅에서는 dplyr 패키지의 window function 두번째 시간으로서 특정 칼럼의 행을 위로 올리거나(Lead) 아니면 내리는(Lag) 함수에 대해서 알아보겠습니다.
lead() 나 lag() 함수는 시계열 데이터를 분석할 때 많이 사용하는 편입니다. 특정 그룹id와 날짜/시간 기준으로 정렬(sorting)을 해놓은 다음에, lead() 나 lag() 함수를 가지고 행을 하나씩 내리구요, 직전 날짜/시간 대비 이후의 값의 변화, 차이(difference)를 구하는 식으로 말이지요.
(시계열분석에 특화된 package를 사용하면 더 편하기 하지만.... 데이터 프레임을 가지고 dplyr 패키지의 lead()나 lag() 함수 알아놓는것도 유용해요)
먼저 간단한 벡터를 가지고 lead()와 lag() 사용법을 소개하겠습니다.
(1) lead(x, n = 1L, default = NA, ...) in {dplyr} package
lead() 함수는 벡터 값을 n = 1L (양의 정수값) 의 값 만큼 앞에서 제외하고, 제일 뒤의 n = 1L 값만큼의 값은 NA 로 채워놓은 값을 반환합니다. 이때, n 표기는 생략할 수 있습니다.
> ##------------------------------------------------- > ## R dplyr package : window function - lead and lag > ##------------------------------------------------- > > library(dplyr) > > # lead() > x <- c(1:10) > > lead(x, n = 1) [1] 2 3 4 5 6 7 8 9 10 NA > > lead(x, 2) [1] 3 4 5 6 7 8 9 10 NA NA
|
(2) lag(x, n = 1L, default = NA, ...) in {dplyr} package
lag() 함수는 lead() 함수와 정반대로 생각하시면 됩니다. lag() 함수의 n = 1L(양의 정수값) 만큼 제일 앞자리부터 뒤로 옮기고, n = 1L 개수 만큼의 자리에 NA 값을 채워넣은 값을 반환합니다.
default = "." 처럼 특정 값을 설정해주면 NA 대신 새로 설정해준 값 혹은 기호가 채워진 값을 반환합니다. (아래 세번째 예의 경우 "."으로 빈 자리가 채워지면서 모든 값에 큰 따옴표("")가 붙으면서 character 형태로 바뀌었습니다)
> # lag() > x <- c(1:10) > > lag(x, n = 1) [1] NA 1 2 3 4 5 6 7 8 9 > > lag(x, 2) [1] NA NA 1 2 3 4 5 6 7 8 > > lag(x, 2, default = ".") [1] "." "." "1" "2" "3" "4" "5" "6" "7" "8"
|
[문제] 위의 x_df 데이터 프레임의 group 별로 직전 대비 직후 값의 차이가 가장 큰 값(max)과 가장 작은 값을 각각 구하시오. (What are the max and min difference values between x and lag(x) of x_df dataframe by group?)
(0) 예제 데이터 프레임 만들기
분석할 때 보통 벡터 보다는 데이터 프레임을 가지고 많이 하므로 예제 데이터 프레임을 하나 만들어보겠습니다. 'group'이라는 요인(factor)형 변수와 seq_no 이라는 시간 순서를 나타내는 변수, 그리고 각 group별로 5개씩의 관찰값을 가진 숫자형(numeric) 변수 x로 구성된 데이터 프레임입니다.
> ##-- make data frame as an example > group <- rep(c("A", "B"), each = 5) > seq_no <- rep(1:5, 2) > set.seed(1234) > x <- round(100*runif(10), 1) > > x_df <- data.frame(group, seq_no, x) > x_df group seq_no x 1 A 1 11.4 2 A 2 62.2 3 A 3 60.9 4 A 4 62.3 5 A 5 86.1 6 B 1 64.0 7 B 2 0.9 8 B 3 23.3 9 B 4 66.6 10 B 5 51.4
|
(1) lag() 하려고 하는 기준대로 정렬이 안되어 있으면 -> 먼저 정렬(sorting) 부터!
예제로 사용하려고 sample(nrow()) 함수로 무작위로 순서를 섞어서 x_df_random 이라는 데이터 프레임을 만들어보았습니다. dplyr 패키지의 arrange() 함수를 가지고 group, seq_no 기준으로 정렬을 해보겠습니다.
> # if data frame is not ordered properly, > # then arrnage it first by lag criteria > x_df_random <- x_df[sample(nrow(x_df)),] > x_df_random group seq_no x 7 B 2 0.9 5 A 5 86.1 3 A 3 60.9 10 B 5 51.4 2 A 2 62.2 9 B 4 66.6 6 B 1 64.0 1 A 1 11.4 8 B 3 23.3 4 A 4 62.3 > > x_df_seq <- arrange(x_df_random, group, seq_no) > x_df_seq group seq_no x 1 A 1 11.4 2 A 2 62.2 3 A 3 60.9 4 A 4 62.3 5 A 5 86.1 6 B 1 64.0 7 B 2 0.9 8 B 3 23.3 9 B 4 66.6 10 B 5 51.4
|
(2) mutate() 함수와 lag() 함수로 group_x, x_lag 변수 만들기
|
(3) group 과 group_lag 의 값이 서로 다르면 그 행(row)은 filter() 함수로 제외하기
> # if group and group_lag are different, then delete the row > x_df_seq_lag_2 <- x_df_seq_lag %>% + filter(group == group_lag) > > x_df_seq_lag_2 group seq_no x group_lag x_lag 1 A 2 62.2 A 11.4 2 A 3 60.9 A 62.2 3 A 4 62.3 A 60.9 4 A 5 86.1 A 62.3 5 B 2 0.9 B 64.0 6 B 3 23.3 B 0.9 7 B 4 66.6 B 23.3 8 B 5 51.4 B 66.6
|
(4) group별로 x와 x_lag 값의 차이가 가장 큰 값(max)과 가장 작은 값(min) 구하기
지지난번 포스팅에서 소개했던 group_by()와 summarise(max()), summarise(min()) 함수를 이용하면 되겠습니다.
> # select max and min of difference between x and x_lag > x_df_seq_lag_2 %>% + group_by(group) %>% + summarise(max_lag = max(x - x_lag, na.rm = TRUE), + min_lag = min(x - x_lag, na.rm = TRUE)) # A tibble: 2 x 3 group max_lag min_lag <fctr> <dbl> <dbl> 1 A 50.8 -1.3 2 B 43.3 -63.1
|
(5) 위의 1~4를 chain operator와 group_by() 를 사용해서 한번에 모두 처리하기.
library(dplyr) ## sample data group <- rep(c("A", "B"), each = 5) seq_no <- rep(1:5, 2) set.seed(1234) x <- round(100*runif(10), 1) x_df <- data.frame(group, seq_no, x) ## max and min of (x - x_lag) x_df %>% arrange(group, seq_no) %>% group_by(group) %>% summarise(max_lag = max(x - lag(x, 1), na.rm = TRUE), min_lag = min(x - lag(x, 1), na.rm = TRUE)) # # A tibble: 2 x 3 # group max_lag min_lag # <chr> <dbl> <dbl> # 1 A 50.8 -1.3 # 2 B 43.3 -63.1
|
그리 어렵지 않지요?
많은 도움이 되었기를 바랍니다.
다음번 포스팅에서는 dplry package window function 세번째로 시간으로 Cumulative aggregates 를 하는데 사용하는 cumall(), cumany(), cummean() 함수, 그리고 Recycled aggregates 에 대해서 알아보겠습니다.
이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 눌러주세요.
(Tistory 가 포스팅별로 조회수를 알려주는 기능이 없다보니 '공감♡' 개수로 참고하려고해요)
댓글을 달아 주세요
안녕하세요! r friend님. 이렇게 계속 답글로 질문드리게 되네요. 혹시 이글을 보신다면 한가지 여쭤보고 싶은게 있습니다.
예시에서 group과 group_lag의 값이 다르면 해당 로우를 제외하는 방법을 알려주셨는데요. 제가 가진 자료 같은 경우에는 계속해서 데이터를 축적해나가는 경우라 해당로우를 제외할수 없는 경우가 발생합니다..T_T 여러가지 방법을 생각해 보았지만 제가 실력이 부족해서 정답이 떠오르지 않아 이렇게 문의 드립니다.. T_T
모르고 엔터를 쳐서 이어서 다시 적습니다;
혹시 이럴경우에 해당로우를 제외하지 않고 다른값으로 치환한다거나 하는 방법이 있을까요?
감사합니다
안녕하세요 김송아 님,
제가 이번주 월요일에 핸드폰을 바꾸었는데요, Tistory 어플만 다운로드 해놓고 로그인 하는걸 깜빡해서 댓글이 달린줄을 오늘 저녁에서야 알았습니다. 답변 늦어서 죄송합니다.
아래 코드처럼 chain operator(%>%)와 group_by(), summarise(), lag() 함수를 한꺼번에 이어서 사용하면 원본 데이터(x_df)는 안건드린채로 원하는 결과를 얻을 수 있습니다. (포스팅 본문 마지막에 업데이트 해두었습니다.)
# max and min of (x - x_lag)
x_df %>%
arrange(group, seq_no) %>%
group_by(group) %>%
summarise(max_lag = max(x - lag(x, 1), na.rm = TRUE),
min_lag = min(x - lag(x, 1), na.rm = TRUE))