사용데이터 : https://2stndard.tistory.com/68
lubridate와 zoo를 사용하는 기간별 합계값 구하기 - 월별 데이터
회사에서 전문 연구직이 아닌 직원들은 데이터 분석을 할 때 많이 사용하는 분석이 연별, 월별, 주별, 일별 데이터 변화량이다. 상사에게 보고하는 보고서에는 연별 매출액, 월별 판매량, 주별 평균 재고량 등과 같이 특정한 주기별로 통계치를 만들어 내거나 매월 말일 판매량, 일요일 가입자수와 같이 특정 일자의 데이터를 넣어야 할 때를 많이 겪었을 것이다. 하지만 대부분 우리들에게 주어진 데이터는 이러한 데이터 형태로 보기좋게 정제되어있지 않을 것이기 때문에 주어진 데이터를 가지고 기간별 통계치나 값을 구하는 방법을 잘 알아둘 필요가 있다.
lubridate
패키지를 사용하여 월별, 주별 데이터나 월말, 주말 데이터를 구하는 방법을 알아본다.
먼저 lubridate
패키지를 설치한다.
if (!require(lubridate)) {
install.packages('lubridate')
library(lubridate)
}
이제 사용할 데이터는 코로나19의 일별 데이터인 ’df_covid19’를 다음과 같이 정제하여 사용하도록 하겠다.
df_covid19_by_period <- 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)
head(df_covid19_by_period, 10)
## # A tibble: 10 x 67
## iso_code continent location date total_cases new_cases new_cases_smoot~
## <chr> <chr> <fct> <date> <dbl> <dbl> <dbl>
## 1 OWID_AFR <NA> 아프리카 2021-01-01 2788203 27749 23359.
## 2 OWID_ASI <NA> 아시아 2021-01-01 20797886 75399 77627
## 3 OWID_EUR <NA> 유럽 2021-01-01 24073347 236850 213026.
## 4 OWID_NAM <NA> 북미 2021-01-01 23381886 202728 229431.
## 5 OWID_OCE <NA> 오세아~ 2021-01-01 48443 35 82.3
## 6 OWID_SAM <NA> 남미 2021-01-01 13245149 45902 62587.
## 7 KOR Asia 한국 2021-01-01 62593 824 956.
## 8 OWID_AFR <NA> 아프리카 2021-01-02 2811093 22890 23575.
## 9 OWID_ASI <NA> 아시아 2021-01-02 20868712 70826 76907.
## 10 OWID_EUR <NA> 유럽 2021-01-02 24234084 160737 215918.
## # ... with 60 more variables: total_deaths <dbl>, new_deaths <dbl>,
## # new_deaths_smoothed <dbl>, total_cases_per_million <dbl>,
## # new_cases_per_million <dbl>, new_cases_smoothed_per_million <dbl>,
## # total_deaths_per_million <dbl>, new_deaths_per_million <dbl>,
## # new_deaths_smoothed_per_million <dbl>, reproduction_rate <dbl>,
## # icu_patients <dbl>, icu_patients_per_million <dbl>, hosp_patients <dbl>,
## # hosp_patients_per_million <dbl>, weekly_icu_admissions <dbl>, ...
다음은 위 데이터중 한국데이터의 일간 그래프이다.
df_covid19_by_period |>
filter(location == '한국') |>
ggplot(aes(x = date, y = new_cases)) +
geom_line(aes(group = location)) +
scale_x_date(date_breaks = '2 month', date_labels = '%y/%m') +
scale_y_continuous(labels = scales::comma) +
labs(x = '연월', y = '신규확진자')
월간 데이터를 구하기 위해서는 먼저 설정되어야 할 것은 날짜 열은 ‘date’ 형태의 열이어야 한다는 것이다.
is.Date(df_covid19_100$date)
## [1] TRUE
월별 합계 데이터 구하기
월별 합계 데이터를 구하기 위해서는 먼저 월별로 그룹화하고 summarise()
를 사용하여 합계를 구해야 한다. 그런데 월별로 데이터를 그룹화하는 것을 어떻게 할 것인가? ‘date’ 형태의 날짜형 데이터가 아닌 문자열 형태로 날짜가 설정되어 있다면 문자열 함수(substr()
과 같은 )로 월을 가리키는 문자열을 뽑아내고 이를 가지고 그룹화하면 될 것이다. 그런데 여기서 하나 주의해아할 것은 문자열 형태로 월을 사용하면 1월, 11월, 12월, 2월의 순서로 나올 수 있다. 이를 방지하기 위해서는 월 문자열을 factor로 설정해서 미리 적절히 순서를 정렬해 놓거나 01월, 02월과 같이 ’0’을 넣어주는 방법이 있겠다. 하지만 1월, 2월이나 Jan, Feb과 같은 형태로 출력하려면 어떻게 할 것인가?
이러한 날짜 연산을 원활하기 하기 위해서 가급적 날짜는 문자열로 쓰는 것보다 ‘date’ 형태의 날짜형 데이터로 사용하는 것을 강력히 권한다. 이 경우 월별 데이터를 구하는 방법은 다음과 같은 세가지 방법이 있다.
lubridate의 year(), month()를 사용
’date’형의 열을 월별로 그룹화하기 위해서는 lubridate
의 month()
를 사용할 수 있다. month()
는 매개변수로 지정된 벡터의 월을 반환하는 함수이다. 하지만 month()
만을 사용하면 매년 같은 월 데이터들은 같은 월을 반환하기 때문에 월별 시계열적 변화를 보기 위해서는 반드시 년을 반환하는 year()
를 같이 사용해주어야 한다. ’df_covid19_by_period’에서 연별, 월별, 지역별 신규확진자의 합은 다음과 같이 구할 수 있다.
df_covid19_by_period |>
group_by(년 = year(df_covid19_by_period$date), 월 = month(df_covid19_by_period$date), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국')
## # A tibble: 18 x 4
## # Groups: 년, 월 [18]
## 년 월 location 신규확진자수
## <dbl> <dbl> <fct> <dbl>
## 1 2021 1 한국 16739
## 2 2021 2 한국 11523
## 3 2021 3 한국 13608
## 4 2021 4 한국 18995
## 5 2021 5 한국 18165
## 6 2021 6 한국 16924
## 7 2021 7 한국 42064
## 8 2021 8 한국 53658
## 9 2021 9 한국 60328
## 10 2021 10 한국 52613
## 11 2021 11 한국 85964
## 12 2021 12 한국 182903
## 13 2022 1 한국 228789
## 14 2022 2 한국 2409407
## 15 2022 3 한국 10102369
## 16 2022 4 한국 3899831
## 17 2022 5 한국 843766
## 18 2022 6 한국 137042
이 중에 한국의 월별 데이터를 선 그래프로 그린다면 다음과 같이 그릴 수 있겠다. 여기서 하나 주의할 것은 앞서 사용한 year()
와 month()
는 결과값이 문자열로 리턴된다는 것이다. 그래서 paste0()
를 사용하면 앞서 언급한 대로 1월, 10월, 11월, 12월, 2월과 같이 나타난다.
df_covid19_by_period |>
group_by(년 = year(date), 월 = month(date), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국') |>
ggplot(aes(x = paste0(년, '-', 월), y = 신규확진자수)) +
geom_line(aes(group = location)) +
theme(axis.text.x = element_text(angle = 90)) +
labs(x = '연월')
이를 방지하기 위해서 X축에 매핑될 변수를 as.Date()
를 사용하여 다시 ‘date’형으로 바꾸어 주어야 하는데 바꾸기 위해서는 년, 월, 일이 모두 필요해서 어쩔수없이’-01’을 붙여 매월 1일 데이터로 전환한다. 그리고 X축에 표현되는 date_labels
의 포맷을 ’%y/%m’으로 설정하여 일을 빼주면 된다. 여기서는 X축을 알아보기 쉽게 하기 위해 scale_x_date()
의 date_breaks
를 사용하여 2개월 단위로 X축 라벨을 찍어주었다.
df_covid19_by_period |>
group_by(년 = year(date), 월 = month(date), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국') |>
ggplot(aes(x = as.Date(paste0(년, '-', 월, '-01')), y = 신규확진자수)) +
geom_line(aes(group = location)) +
scale_x_date(date_breaks = '2 month', date_labels = '%y/%m') +
scale_y_continuous(labels = scales::comma) +
labs(x = '연월')
lubridate의 floor_date()를 사용
앞선 예에서 year()
와 month()
를 사용하면 문자열이 반환되고 이를 다시 ’date’형으로 바꾸어야 한다는 불편함이 있다. 이를 방지하기 위해서 매월 말일 단위로 그룹화하고 합계를 적용하면 ’date’형 열을 그대로 사용할 수 있기 때문에 앞의 예보다는 다소 코드가 쉬워질 수 있다.
floor_date
는 주어진 시간 주기에 맞는 마지막 날을 기준으로 그룹화하는 함수이다.
df_covid19_by_period |>
group_by(연월 = floor_date(date, 'month'), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국')
## # A tibble: 18 x 3
## # Groups: 연월 [18]
## 연월 location 신규확진자수
## <date> <fct> <dbl>
## 1 2021-01-01 한국 16739
## 2 2021-02-01 한국 11523
## 3 2021-03-01 한국 13608
## 4 2021-04-01 한국 18995
## 5 2021-05-01 한국 18165
## 6 2021-06-01 한국 16924
## 7 2021-07-01 한국 42064
## 8 2021-08-01 한국 53658
## 9 2021-09-01 한국 60328
## 10 2021-10-01 한국 52613
## 11 2021-11-01 한국 85964
## 12 2021-12-01 한국 182903
## 13 2022-01-01 한국 228789
## 14 2022-02-01 한국 2409407
## 15 2022-03-01 한국 10102369
## 16 2022-04-01 한국 3899831
## 17 2022-05-01 한국 843766
## 18 2022-06-01 한국 137042
이 데이터를 사용하여 선 그래프를 다음과 같이 그릴 수 있다. 다음에서는 date_labels
의 월 포맷을 ’%B’로 설정함으로써 월의 표기를 바꾸어주었다.
df_covid19_by_period |>
group_by(연월 = floor_date(date, 'month'), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국') |>
ggplot(aes(x = 연월, y = 신규확진자수)) +
geom_line(aes(group = location)) +
scale_x_date(date_breaks = '2 month', date_labels = '%y/%B') +
scale_y_continuous(labels = scales::comma)
zoo의 yearmonth()를 사용
앞의 두 예를 보면 월별 데이터를 만드는데 ’date’형을 사용하기 때문에 어쩔수 없이 일을 붙여주었다. 하지만 R에서 날짜를 다루는 패키지중에 하나인 zoo
패키지의 as.yearmon()
를 사용하면 연, 월만 가지는 ’yearmon’형을 만들 수 있다.
if (!require(zoo)) {
install.packages('zoo')
library(zoo)
}
df_covid19_by_period |>
group_by(연월 = zoo::as.yearmon(date), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국')
## # A tibble: 18 x 3
## # Groups: 연월 [18]
## 연월 location 신규확진자수
## <yearmon> <fct> <dbl>
## 1 1 2021 한국 16739
## 2 2 2021 한국 11523
## 3 3 2021 한국 13608
## 4 4 2021 한국 18995
## 5 5 2021 한국 18165
## 6 6 2021 한국 16924
## 7 7 2021 한국 42064
## 8 8 2021 한국 53658
## 9 9 2021 한국 60328
## 10 10 2021 한국 52613
## 11 11 2021 한국 85964
## 12 12 2021 한국 182903
## 13 1 2022 한국 228789
## 14 2 2022 한국 2409407
## 15 3 2022 한국 10102369
## 16 4 2022 한국 3899831
## 17 5 2022 한국 843766
## 18 6 2022 한국 137042
위의 데이터를 사용하여 선 그래프를 다음과 같이 그릴 수 있다. 다만 as.yearmon()
의 결과는 ‘date’ 형이 아니기 때문에 ggplot2
에서 제공하는 scale_x_date()
를 사용할 수 없고 zoo
패키지에서 제공하는 scales_x_yearmon()
을 사용해야 한다.
df_covid19_by_period |>
group_by(연월 = zoo::as.yearmon(date), location) |>
summarise(신규확진자수 = sum(new_cases, na.rm = T)) |>
filter(location == '한국') |>
ggplot(aes(x = 연월, y = 신규확진자수)) +
geom_line(aes(group = location)) +
scale_x_yearmon(format = "%B %Y", n = 9)+
scale_y_continuous(labels = scales::comma)
'데이터 전처리' 카테고리의 다른 글
열 이름 바꾸기 in R (0) | 2022.06.21 |
---|---|
일별(일간) 데이터를 주별(주간) 데이터로 만들기 in R (0) | 2022.06.18 |
데이터프레임 합치기(Merge) in R (0) | 2022.06.12 |
특정 조건 행의 데이터만 바꾸기 (0) | 2022.06.11 |
데이터 행의 제거 in R (0) | 2022.06.04 |
댓글