본문 바로가기
  • plotly로 바로쓰는 동적시각화 in R & 파이썬
데이터 전처리

누적합과 누적평균 in R

by 아참형인간 2022. 6. 26.
timeperiod_week.knit

사용데이터 : https://2stndard.tistory.com/68

cumsum()과 seq_along()을 사용한 누적합과 누적평균 구하기

누적 합계는 데이터의 시간의 흐름에 따라 계속적으로 합산되는 데이터를 말한다. 따라서 누적 합계는 대부분 시간의 일정한 단위나 시간의 흐름에 따라 발생된 이벤트에 따라 발생되는 데이터의 합계이다. 이 누적 합계는 1954년에 캠브리지 대학에서 제안되었는데 통계적 프로세스 모니터링의 한 방법으로 광범위하게 사용되고 있다.

누적 평균은 지속적으로 발생되는 데이터들의 누적값에 대한 평균을 말한다. 평균을 구하는데에는 데이터의 합계에 대한 데이터의 빈도의 비율을 말하는데 누적 평균에서는 계속적으로 발생되는 누적합계를 지속적으로 증가되는 데이터 빈도로 나누어 산출된다. 이 누적 평균은 누적 이동 평균(Cumulative Moving Average)이라고도 하는데 시계열 예측(Forecasting)이 모델중 하나로 많이 사용되는 모델이다.

R에서 누적 합계와 누적 평균은 cumsum()seq_along()을 사용하면 쉽게 구할 수 있다.

누적 합계와 누적 평균 산출에는 21년 이후의 코로나19 확진자의 주별 데이터를 사용하는데 다음과 같이 전처리 한다.

library(lubridate)

df_covid19_by_week <- df_covid19 |> 
  ## 한국 데이터와 각 대륙별 데이터만을 필터링
  filter(iso_code %in% c('KOR', 'OWID_ASI', 'OWID_EUR', 'OWID_OCE', 'OWID_NAM', 'OWID_SAM', 'OWID_AFR')) |>
  ## 읽은 데이터의 마지막 데이터에서 100일전 데이터까지 필터링
  filter(date >= as.Date('2021-01-01')) |>
  ## 국가명을 한글로 변환
  mutate(location = case_when(
    location == 'South Korea' ~ '한국', 
    location == 'Asia' ~ '아시아', 
    location == 'Europe' ~ '유럽', 
    location == 'Oceania' ~ '오세아니아', 
    location == 'North America' ~ '북미', 
    location == 'South America' ~ '남미', 
    location == 'Africa' ~ '아프리카')) |>
  ## 국가 이름의 순서를 설정 
  mutate(location = fct_relevel(location, '한국', '아시아', '유럽', '북미', '남미', '아프리카', '오세아니아')) |>
  ## 날짜로 정렬
  arrange(date) |>
  group_by(년 = year(date), 주 = week(date), location) |>
  summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
  filter(location == '한국') |>
  ungroup()

head(df_covid19_by_week, 10) 
## # A tibble: 10 x 4
##       년    주 location 신규확진자수
##    <dbl> <dbl> <fct>           <dbl>
##  1  2021     1 한국             5589
##  2  2021     2 한국             3883
##  3  2021     3 한국             3021
##  4  2021     4 한국             3133
##  5  2021     5 한국             2736
##  6  2021     6 한국             2706
##  7  2021     7 한국             3291
##  8  2021     8 한국             2794
##  9  2021     9 한국             2716
## 10  2021    10 한국             3048

cumsum()을 사용한 누적 합계 산출

cumsum()은 누적 합계를 구하는데 사용되는 함수이다. cumsum()을 사용할 때 하나 주의해야 할 것은 누적 합계의 대상이 되는 벡터나 데이터 프레임 열의 순서에 따라 누적 합계가 구해진다는 것이다.

df_covid19_by_week$누적확진자수 <- cumsum(df_covid19_by_week$신규확진자수)

head(df_covid19_by_week, 10) 
## # A tibble: 10 x 5
##       년    주 location 신규확진자수 누적확진자수
##    <dbl> <dbl> <fct>           <dbl>        <dbl>
##  1  2021     1 한국             5589         5589
##  2  2021     2 한국             3883         9472
##  3  2021     3 한국             3021        12493
##  4  2021     4 한국             3133        15626
##  5  2021     5 한국             2736        18362
##  6  2021     6 한국             2706        21068
##  7  2021     7 한국             3291        24359
##  8  2021     8 한국             2794        27153
##  9  2021     9 한국             2716        29869
## 10  2021    10 한국             3048        32917

cumsum()은 R base에서 제공하는 함수이기 떄문에 dplyr에서 사용하기 위해서는 mutate()와 함께 사용해야 한다.

df_covid19_by_week |>
  mutate(누적확진자수_dplyr = cumsum(신규확진자수))
## # A tibble: 79 x 6
##       년    주 location 신규확진자수 누적확진자수 누적확진자수_dplyr
##    <dbl> <dbl> <fct>           <dbl>        <dbl>              <dbl>
##  1  2021     1 한국             5589         5589               5589
##  2  2021     2 한국             3883         9472               9472
##  3  2021     3 한국             3021        12493              12493
##  4  2021     4 한국             3133        15626              15626
##  5  2021     5 한국             2736        18362              18362
##  6  2021     6 한국             2706        21068              21068
##  7  2021     7 한국             3291        24359              24359
##  8  2021     8 한국             2794        27153              27153
##  9  2021     9 한국             2716        29869              29869
## 10  2021    10 한국             3048        32917              32917
## # ... with 69 more rows

cumsum()을 사용할때 주의해아 하는 것은 반드시 먼저 대상 데이터의 정렬 상태를 반드시 확인해야 한다는 것이다.

앞서 전처리한 코로나 19의 주별 데이터의 경우는 주차수만으로 정렬을 한다면 연도가 무시되어서 21년 1주차 다음에 22년 1주차가 나오게 되어 잘못된 누적 합계가 산출되게 된다.

df_covid19_by_week |>
  arrange(주) |>
  mutate(누적확진자수_dplyr = cumsum(신규확진자수))
## # A tibble: 79 x 6
##       년    주 location 신규확진자수 누적확진자수 누적확진자수_dplyr
##    <dbl> <dbl> <fct>           <dbl>        <dbl>              <dbl>
##  1  2021     1 한국             5589         5589               5589
##  2  2022     1 한국            25762       599246              31351
##  3  2021     2 한국             3883         9472              35234
##  4  2022     2 한국            26969       626215              62203
##  5  2021     3 한국             3021        12493              65224
##  6  2022     3 한국            38290       664505             103514
##  7  2021     4 한국             3133        15626             106647
##  8  2022     4 한국            84848       749353             191495
##  9  2021     5 한국             2736        18362             194231
## 10  2022     5 한국           159896       909249             354127
## # ... with 69 more rows

이를 정상적으로 산출하기 위해서는 주차수와 더불어 연도별로도 정렬이 되어야 하며 연도별로 먼저 정렬되어야 한다.

df_covid19_by_week |>
  arrange(년, 주) |>
  mutate(누적확진자수_dplyr = cumsum(신규확진자수))
## # A tibble: 79 x 6
##       년    주 location 신규확진자수 누적확진자수 누적확진자수_dplyr
##    <dbl> <dbl> <fct>           <dbl>        <dbl>              <dbl>
##  1  2021     1 한국             5589         5589               5589
##  2  2021     2 한국             3883         9472               9472
##  3  2021     3 한국             3021        12493              12493
##  4  2021     4 한국             3133        15626              15626
##  5  2021     5 한국             2736        18362              18362
##  6  2021     6 한국             2706        21068              21068
##  7  2021     7 한국             3291        24359              24359
##  8  2021     8 한국             2794        27153              27153
##  9  2021     9 한국             2716        29869              29869
## 10  2021    10 한국             3048        32917              32917
## # ... with 69 more rows
df_covid19_by_week |>
  arrange(년, 주) |>
  mutate(누적확진자수_dplyr = cumsum(신규확진자수)) |>
  mutate(주 = sprintf('%02d', 주)) |>
  ggplot(aes(x = as.factor(paste0(년, '-', 주)), y = 누적확진자수_dplyr)) +
  geom_line(aes(group = location)) + 
  scale_x_discrete(guide = guide_axis(check.overlap = TRUE)) +
  theme(axis.text.x = element_text(angle = 90)) +
  labs(x = '연월')

cumsum()과 seq_anlong()을 사용한 누적 평균 산출

누적 평균 산출을 위해서는 앞서 산출했던 cumsum()의 결과를 해당 cumsum()을 산출하는데 사용한 데이터의 빈도수를 나누어야 한다. 하지만 몇개의 데이터가 합쳐져서 누적 합계가 산출되었는지를 가르키는 변수는 설정되지 않았다. 데이터를 얼핏 보면 ‘주’ 열을 사용하면 될 것같지만 ’연’이 넘어가면 ’주’도 1부터 다시 시작하기 때문에 이를 사용하기에는 어렵다. 그렇다면 열에 해당하는 시퀀스 넘버를 붙여주면 될 것이다. 이를 열로 만들어 놓으면 될수 있지만 이런 경우 사용되는 함수가 seq_along()이다.

seq_along()은 일련의 연속된 수치를 생성하는 함수이다. 이렇게 사용하는 함수는 seq()가 가장 많이 사용되지만 seq()는 시작값인 from과 마지막 값인 to를 설정해 주어야 하는 반면 seq_along()은 매개변수로 설정된 벡터의 길이만큼 연속된 수치가 설정된다. 결국 seq_along(x)seq(from = 1, to = length(x))와 동일한 결과를 낸다.

df_covid19_by_week$누적확진자수_평균 <- cumsum(df_covid19_by_week$신규확진자수)/seq_along(df_covid19_by_week$신규확진자수)

head(df_covid19_by_week, 10) 
## # A tibble: 10 x 6
##       년    주 location 신규확진자수 누적확진자수 누적확진자수_평균
##    <dbl> <dbl> <fct>           <dbl>        <dbl>             <dbl>
##  1  2021     1 한국             5589         5589             5589 
##  2  2021     2 한국             3883         9472             4736 
##  3  2021     3 한국             3021        12493             4164.
##  4  2021     4 한국             3133        15626             3906.
##  5  2021     5 한국             2736        18362             3672.
##  6  2021     6 한국             2706        21068             3511.
##  7  2021     7 한국             3291        24359             3480.
##  8  2021     8 한국             2794        27153             3394.
##  9  2021     9 한국             2716        29869             3319.
## 10  2021    10 한국             3048        32917             3292.
df_covid19_by_week |>
  arrange(년, 주) |>
  mutate(누적평균 = cumsum(신규확진자수)/seq_along(신규확진자수))
## # A tibble: 79 x 7
##       년    주 location 신규확진자수 누적확진자수 누적확진자수_평균 누적평균
##    <dbl> <dbl> <fct>           <dbl>        <dbl>             <dbl>    <dbl>
##  1  2021     1 한국             5589         5589             5589     5589 
##  2  2021     2 한국             3883         9472             4736     4736 
##  3  2021     3 한국             3021        12493             4164.    4164.
##  4  2021     4 한국             3133        15626             3906.    3906.
##  5  2021     5 한국             2736        18362             3672.    3672.
##  6  2021     6 한국             2706        21068             3511.    3511.
##  7  2021     7 한국             3291        24359             3480.    3480.
##  8  2021     8 한국             2794        27153             3394.    3394.
##  9  2021     9 한국             2716        29869             3319.    3319.
## 10  2021    10 한국             3048        32917             3292.    3292.
## # ... with 69 more rows
df_covid19_by_week |>
  arrange(년, 주) |>
  mutate(누적평균 = cumsum(신규확진자수)/seq_along(신규확진자수)) |>
  mutate(주 = sprintf('%02d', 주)) |>
  ggplot(aes(x = as.factor(paste0(년, '-', 주)), y = 누적평균)) +
  geom_line(aes(group = location)) + 
  scale_x_discrete(guide = guide_axis(check.overlap = TRUE)) +
  theme(axis.text.x = element_text(angle = 90)) +
  labs(x = '연월')

댓글