-
시계열 분석 시리즈 I (Tsibble 객체 다루기)데이터 2024. 3. 3. 22:40
들어가기
가장 많은 종류의 패키지와 표준들이 나오는 분야가 시계열 분석과 예측 관련 분야가 아닐까 싶다. 그래서 시계열 분석에 대한 여러 강의를 듣다 보면, 분석의 개념과 방법은 동일하지만 사용하는 패키지나 코드는 강의마다 다르다는 점을 많이 느꼈었다. 이 시리즈는 비교적 최근에 등장해서 시계열 분석을 tidyverse한 방향으로 수행하는 fpp3 패키지를 기반으로 작성되었으며, 호주의 모나쉬 대학교 Rob J Hyndman 교수의 “Forecast Priciples and Practice” 책을 공부하면서 나름대로 재정리한 내용이다. 이 책은 시계열 분석 분야에서는 꽤(가장?) 유명한 책이다. 한글 번역본은 fpp2 패키지를 사용한 2판인데 반해 온라인 원문 버전은 3판으로 fpp3 패키지를 사용하였다. 2판 책과 3판 책의 시계열 예측 개념은 동일하지만, 내용과 R 소스는 상당한 차이가 있어서 정리는 최신판인 3판 원서를 기준으로 공부하고 정리하였다.
- https://otexts.com/fpp3/index.html (온라인 원문 버전 3판)
- https://otexts.com/fppkr/index.html (온라인 한글 버전 2판)
시계열 데이터
시계열 데이터의 예측과 관련된 여러 모델을 사용하기 위해서는 데이터를 시계열 데이터 객체로 변환하여 사용해야 한다. 시계열 데이터 객체에는 여러 종류가 있다. 객체에 따라 데이터의 표현 방식과 사용할 수 있는 메써드의 종류가 달라지기 때문에 어떤 시계열 객체를 사용하여 분석과 예측을 할 것인가는 중요한 문제이다. 시계열 데이터 처리를 위한 패키지의 종류는 다양하고 패키지별로 다루는 데이터 객체도 상이하다. R 기본판에는 ts라는 시계열 패키지가 포함되어 있고 이후 기능이 보완된 패키지, 예를 들어 xts나 zoo 같은 패키지들이 나와 사용되고 있다. 또 최근에는 R의 생태계가 tidyverse한 방향으로 진화되어 가고 있기 때문에, 시계열 데이터 모델링도 tidyverse 모델과 연동되는 tsibble 패키지가 개발되었다. 필자가 참조한 교재 3판에서는 tsibble 패키지를 사용하여 시계열 데이터를 모델링 하였고, 이 글의 모든 소스도 tsibble 패키지를 사용하였다.
이는 이 글에서 제시되는 모델링을 위한 소스들이 시계열 데이터 분석에 대한 많은 u-tube 등 기존의 강의 보다 새로운 절차로 시계열 분석이 진행된다는 의미이다.
참고 : copilot의 데이터 객체 종류에 대한 설명
R에서 시계열 데이터 분석과 예측을 위해 사용되는 대표적인 패키지와 그 패키지에서 사용하는 시계열 데이터 객체는 다음과 같습니다.
- stats 패키지: R의 기본 패키지 중 하나로, ts 객체를 사용합니다. ts 객체는 일정한 시간 간격으로 측정된 데이터를 다루며, 시간 인덱스는 ‘start’, ‘end’ 및 ‘frequency’ 값으로 계산됩니다.
- zoo 패키지: zoo 패키지는 불규칙한 시간 간격으로 측정된 데이터를 다룰 수 있습니다. zoo 객체는 시간 인덱스를 데이터의 필수 열로 유지하며, 다양한 데이터 구조를 가능하게 합니다.
- xts 패키지: xts 패키지는 zoo 패키지를 확장한 것으로, 시간 인덱스를 효율적으로 다룰 수 있습니다.
- tsibble 패키지: tsibble 패키지는 tidyverse를 시계열 데이터에 확장한 것입니다. tsibble 객체는 시간 인덱스를 데이터의 필수 열로 유지하며, 다양한 데이터 구조를 가능하게 합니다. 또한, tsibble은 ‘index’와 ‘key’ 변수를 선언하여 각 관측치를 시간과 키로 고유하게 식별합니다.
- fable 패키지: fable 패키지는 일반적으로 사용되는 단변량 및 다변량 시계열 예측 모델을 제공합니다. fable 패키지는 tsibble 객체를 사용합니다.
각 패키지에서 사용하는 시계열 데이터 객체로 데이터프레임을 변환하는 예는 다음과 같습니다.
# 일반 데이터 프레임 생성 df <- data.frame( time = seq(as.Date("2000/1/1"), by = "month", length.out = 100), value = rnorm(100) ) # stats 패키지의 ts 객체로 변환 ts_obj <- ts(df$value, start = c(2000, 1), frequency = 12) # zoo 패키지의 zoo 객체로 변환 library(zoo) zoo_obj <- zoo(df$value, order.by = df$time) # xts 패키지의 xts 객체로 변환 library(xts) xts_obj <- xts(df$value, order.by = df$time) # tsibble 패키지의 tsibble 객체로 변환 library(tsibble) tsibble_obj <- tsibble(df, index = time)
tsibble 객체
시계열 데이터 분석의 첫 단계는 시계열 데이터 객체를 생성하는 것이다. 분석과 관련된 함수(메써드)를 이용하여 시계열 분석을 쉽게 하기 위해서는 일반적인 데이터를 시계열 객체로 변환해야한다. 우리는 시계열 데이터 객체 중 tsibble 객체를 활용하여 시계열 분석을 수행할 것이다. 먼저 tsibble 패키지에 내장된 PBS 데이터를 이용해 tsibble 객체의 특성을 알아보고 몇 가지 데이터 다루는 작업을 해보자. PBS 데이터는 1991년 7월부터 2008년 6월까지의 204개월간의 호주의 Medicare Australia 처방전 관련 데이터이다. 먼저 PBS 데이터를 살펴보자.
PBS
# A tsibble: 67,596 x 9 [1M] # Key: Concession, Type, ATC1, ATC2 [336] Month Concession Type ATC1 ATC1_desc ATC2 ATC2_desc Scripts Cost <mth> <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> 1 1991 7 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 18228 67877 2 1991 8 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 15327 57011 3 1991 9 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 14775 55020 4 1991 10 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 15380 57222 5 1991 11 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 14371 52120 6 1991 12 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 15028 54299 7 1992 1 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 11040 39753 8 1992 2 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 15165 54405 9 1992 3 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 16898 61108 10 1992 4 Concessional Co-paymen… A Alimenta… A01 STOMATOL… 18141 65356 # ℹ 67,586 more rows
PBS 데이터는 tsibble 객체로 67,596건의 관측치와 9개의 컬럼으로 구성되어 있으며 시간 주기는 월(1M)이라고 나와 있다. 두 번째 줄에서는 Key가 되는 변수를 설명한다. PBS 데이터의 Key는 “Concession”, “Type”, “ATC1”, “ATC2” 이다. Key는 시계열 데이터를 특정하기 위해 존재하는 변수이고 실제로 데이터 분석이나 예측을 위한 값(Value)이 들어있는 변수는 Scripts(처방건수)와 Cost(비용) 이다. 즉 Value인 Scripts와 Cost를 filtering해서 특정 위해 존재하는 변수가 Key이다. Key변수 뒤에 나오는 [336]의 의미는 무엇일까? 이것은 Key 값의 조합을 통해 특정할 수 있는 고유한 시계열 데이터의 종류가 336건 이란 의미이다. PBS라는 tsibble 데이터 하나에 분석 가능한 336건의 다른 시계열 데이터가 들어있다고 이해하면 된다.
tsibble 데이터 주무르기
다양한 시계열 분석을 위해 필요한 데이터를 준비하는 작업을 해보자. 예들 들어 PBS 데이터에서 ACT2가 “A10”인 비용(Cost)에 관심이 있다고 가정해 보자. 다음과 같이 원하는 데이터를 발췌할 수 있다.
PBS %>% filter(ATC2 == "A10") %>% select(Cost)
# A tsibble: 816 x 6 [1M] # Key: Concession, Type, ATC1, ATC2 [4] Cost Month Concession Type ATC1 ATC2 <dbl> <mth> <chr> <chr> <chr> <chr> 1 2092878 1991 7 Concessional Co-payments A A10 2 1795733 1991 8 Concessional Co-payments A A10 3 1777231 1991 9 Concessional Co-payments A A10 4 1848507 1991 10 Concessional Co-payments A A10 5 1686458 1991 11 Concessional Co-payments A A10 6 1843079 1991 12 Concessional Co-payments A A10 7 1564702 1992 1 Concessional Co-payments A A10 8 1732508 1992 2 Concessional Co-payments A A10 9 2046102 1992 3 Concessional Co-payments A A10 10 2225977 1992 4 Concessional Co-payments A A10 # ℹ 806 more rows
발췌한 데이터의 건수가 816건으로 줄었으나, 변수는 6개이다. Key 변수는 Cost를 측정하기 위한 Concession, Type, ATC1, ATC2 변수가 아직 살아있고, 그 안에 4가지 유형의 시계열 데이터가 들어있음을 알 수 있다. 즉 인덱스가 같은 네가지 종류의 데이터 들어있다. 다음 그래프를 통해 살펴보자.
PBS %>% filter(ATC2 == "A10") %>% select(Cost) %>% autoplot(Cost/1e6)
처방전 데이터의 월별 Cost가 Key 변수의 4가지 조합으로 구분되어 표시되어있다. 그럼 이번에는 ACT2 타입이 “A10”인 4가지 종류의 데이터의 Cost가 합쳐진 종합된 시계열 데이터를 만들어 보자.
PBS %>% filter(ATC2 == "A10") %>% select(Cost) %>% summarise(A10_sum = sum(Cost)) %>% mutate(A10_sum = A10_sum/1e6) -> A10 # 100만불 단위로 조정하여 a10에 저장
이렇게 해서 ACT2 타입이 “A10”인 데이터를 종합한 데이터를 만들 수 있다. 이것을 가시화 해보면 아래 그래프가 된다.
A10 %>% autoplot(A10_sum) + labs(title = "호주 월별 처방전 비용변화", y = "호주달러(백만)")
index variable
시계열 데이터를 분석하는데 시간 주기는 매우 중요하다. 분석과 예측의 목적에 따라 분석해야 하는 주기가 달라진다. tsibble에서는 시간의 주기를 index로 정의한다. 주기를 정의하는데 사용하는 함수에는 다음과 같은 것들이 있다.
주기 함수 Annual start:end Quarterly yearquarter() Monthly yearmonth() Weekly yearweek() Daily as_date(), ymd() Sub-daily as_datetime(), ymd_hms() A10 데이터는 index 변수가 Month이고 주기가 [1M]으로 “월”로 설정된 데이터이다. 월별로 집계된 데이터를 분기별로 조정하여 재집계하는 문제를 생각해보자. 먼저 각 Month에 해당하는 분기(Quarter) 변수를 만드는 것이 필요하다. yearquarter() 함수를 사용하여 Month에 해당하는 Quarter로 만든다. 다음엔 월별 데이터를 분기별로 집계하는 작업이 필요하다. 분석하는 문제의 성격에 따라 월별데이터를 더해서 분기의 합으로 계산 할 수도 있고, 해당 분기의 평균을 계산해서 분기 평균으로 계산 할 수도 있다. 다음에는 인덱스 변수를 월(Month)에서 분기(Quarter)로 다시 지정한다. 아래 예를 보자.
A10 %>% mutate(Quarter = yearquarter(Month)) %>% # Month에 해당하는 Quarter 변수 생성 index_by(Quarter) %>% # Quarter 변수별 그룹 (group_by 대신!!) summarise(Quarter_cost = sum(A10_sum)) %>% # Quarter 변수별로 합계 autoplot(Quarter_cost) + labs(title = "호주 분기별 처방전 비용변화", y = "호주달러(백만)")
yearquarter() 함수를 이용하여 Month를 분기로 변환하여 Quarter라는 새로운 변수를 생성했다. 생성된 Quarter 변수를 index로 지정하고, index 별로 그룹핑하기 위해 index_by() 함수를 이용하였다. 참고로 index 별로 그룹핑 시키는 함수가 index_by() 함수이다. 이제 월별 Cost의 합을 구하여 새로운 변수인 Quarter_cost를 생성하였다. 생성된 데이터를 이용하여 autoplot()함수로 분기별 처방전 비용 그래프를 작성하였다.
호주 전기 소요량 데이터
vic_elec 데이터는 2012-01-01부터 2014년 12-31까지 3년간 호주 멜버른 지역의 전력소요량와 기온을 30분 간격으로 측정한 시계열 데이터이다. 먼저 데이터를 살펴보자.
vic_elec %>% autoplot(Demand)
tsibble 데이터로 관측치 52,608 데이터로 5개의 변수로 구성되어 있으며, 주기는 30분으로 되어 있다. 만일 일일 단위 전력 수요를 예측해야 할 경우가 생긴다면 어떻게 해야 할까? 30분 주기 데이터 그대로 분석과 예측을 하면 결과가 이상해 질것은 자명하다. 시계열 데이터의 주기를 일 단위로 변환해서 일단위로 분석하고 싶다면 어떻게 해야 할까?
생각의 순서는 다음과 같다. 1. date() 함수를 이용하여 index 조정을 위한 “Day” 변수 생성 1. index_by() 함수를 이용하여 인덱스를 새로운 “Day” 변수로 변경 1. 30분 단위 전력소요를 일 단위 총합으로 종합 1. 30분 단위 기온을 일 단위 평균 또는 최저, 최고 기온으로 변환해야 한다.
아래 코드를 보자.vic_elec_day <- vic_elec %>% mutate(Day = as_date(Time)) %>% index_by(Day) %>% summarise(Demand_day = sum(Demand)/1000, # 단위 조정 Max_tmp = max(Temperature), # 그날의 최고기온으로 설정 Holiday = any(Holiday)) # 하나라도 TRUE 면 그날을 TRUE로 vic_elec_day
# A tsibble: 1,096 x 4 [1D] Day Demand_day Max_tmp Holiday <date> <dbl> <dbl> <lgl> 1 2012-01-01 222. 32.7 TRUE 2 2012-01-02 258. 39.6 TRUE 3 2012-01-03 267. 31.8 FALSE 4 2012-01-04 223. 25.1 FALSE 5 2012-01-05 211. 21.2 FALSE 6 2012-01-06 210. 23.6 FALSE 7 2012-01-07 203. 29 FALSE 8 2012-01-08 193. 27.8 FALSE 9 2012-01-09 214. 24 FALSE 10 2012-01-10 215. 19.6 FALSE # ℹ 1,086 more rows
새로 생성된 vic_elec_day는 원래 30분 간격으로 되어있던 52,608건의 관측치가 일 단위 관측치 1096건으로 축약되고, 인덱스가 일[1D]로 변경되었다. 분석의 대상이 되는 value의 종합은 상황에 따라 합, 평균, 최대, 최소 등 상황에 맞는 통계 값을 이용하여 종합해야 한다. 아래는 일단위 전력 소요량 그래프이다.
vic_elec_day %>% autoplot(Demand_day)
일반객체를 tsibble로 변환하기
이번에는 csv 형태로 저장된 tibble 객체를 읽어 tsibble로 변환하여 처리하는 문제를 연습해 보자.
url <- "https://OTexts.com/fpp3/extrafiles/prison_population.csv" prison <- readr::read_csv(url) prison
# A tibble: 3,072 × 6 Date State Gender Legal Indigenous Count <date> <chr> <chr> <chr> <chr> <dbl> 1 2005-03-01 ACT Female Remanded ATSI 0 2 2005-03-01 ACT Female Remanded Non-ATSI 2 3 2005-03-01 ACT Female Sentenced ATSI 0 4 2005-03-01 ACT Female Sentenced Non-ATSI 5 5 2005-03-01 ACT Male Remanded ATSI 7 6 2005-03-01 ACT Male Remanded Non-ATSI 58 7 2005-03-01 ACT Male Sentenced ATSI 5 8 2005-03-01 ACT Male Sentenced Non-ATSI 101 9 2005-03-01 NSW Female Remanded ATSI 51 10 2005-03-01 NSW Female Remanded Non-ATSI 131 # ℹ 3,062 more rows
prison 데이터는 tibble이고, 30276건의 죄수 관련 통계 자료이다. 변수는 Date, State, Gender 등이고 Count가 통계수치임을 알 수 있다. 결국 tsibble로 변환하기 위해서는 index는 Date가 되고 Key는 State, Gender, Legal, Indigeneous가 됨을 알 수 있다. 참고로 Date의 주기가 얼마나 되는지 파악하기 위해 Date 변수를 확인해 보자
prison %>% distinct()
# A tibble: 3,072 × 6 Date State Gender Legal Indigenous Count <date> <chr> <chr> <chr> <chr> <dbl> 1 2005-03-01 ACT Female Remanded ATSI 0 2 2005-03-01 ACT Female Remanded Non-ATSI 2 3 2005-03-01 ACT Female Sentenced ATSI 0 4 2005-03-01 ACT Female Sentenced Non-ATSI 5 5 2005-03-01 ACT Male Remanded ATSI 7 6 2005-03-01 ACT Male Remanded Non-ATSI 58 7 2005-03-01 ACT Male Sentenced ATSI 5 8 2005-03-01 ACT Male Sentenced Non-ATSI 101 9 2005-03-01 NSW Female Remanded ATSI 51 10 2005-03-01 NSW Female Remanded Non-ATSI 131 # ℹ 3,062 more rows
Date 변수를 보니 2005년 3월 1일부터 2016년 12월 1일까지 기록된 12년분(12년 * 4 = 48개 분기) 데이터이다. date 형식으로 기록되었지만, 실제 주기는 3개월 단위로 기록된 데이터이다. 따라서 tsibble 데이터로 변환하기 위해 index를 Quarter로 변환하고 Date와 Count를 제외한 나머지 변수를 Key로 지정하면 된다.
prison_Q <- prison %>% mutate(Quarter = yearquarter(Date)) %>% # Date를 yearquarter 이용 분기로 변환 as_tsibble(index = Quarter, key = c(State, Gender, Legal, Indigenous)) %>% select(-Date) # 필요없는 Date는 삭제 prison_Q
# A tsibble: 3,072 x 6 [1Q] # Key: State, Gender, Legal, Indigenous [64] State Gender Legal Indigenous Count Quarter <chr> <chr> <chr> <chr> <dbl> <qtr> 1 ACT Female Remanded ATSI 0 2005 Q1 2 ACT Female Remanded ATSI 1 2005 Q2 3 ACT Female Remanded ATSI 0 2005 Q3 4 ACT Female Remanded ATSI 0 2005 Q4 5 ACT Female Remanded ATSI 1 2006 Q1 6 ACT Female Remanded ATSI 1 2006 Q2 7 ACT Female Remanded ATSI 1 2006 Q3 8 ACT Female Remanded ATSI 0 2006 Q4 9 ACT Female Remanded ATSI 0 2007 Q1 10 ACT Female Remanded ATSI 1 2007 Q2 # ℹ 3,062 more rows
prison_Q 데이터는 tsibble 객체로 변환되었음을 알 수 있다.
prison_Q %>% filter(State == "QLD" & Legal == "Sentenced" & Gender == "Male") %>% autoplot(Count) + labs(title = "호주 퀸즈랜드 남성 재소자 현황황")
위 그래프는 prison 데이터의 key 값을 한정하여 tsibble 시계열 데이터를 생성하고, 그래프를 그린 것이다.
참고로 tsibble 객체에 적용 가능한 함수(메써드)들은 다음과 같이 확인해 볼 수 있다.
methods(class = "tbl_ts")
[1] $ $<- %within% [ [5] [[ [[<- [<- aggregate_key [9] append_row arrange as.data.frame as.ts [13] as_dable as_fable as_tibble as_tsibble [17] autolayer autoplot cbind coerce [21] common_periods count distinct dplyr_col_modify [25] dplyr_reconstruct dplyr_row_slice drop_na estimate [29] features features_all features_at features_if [33] fill fill_gaps format frequency [37] gather group_by index_by initialize [41] key key_data key_drop_default key_vars [45] measured_vars measures model names<- [49] nest new_data Ops pivot_longer [53] pivot_wider rbind scan_gaps select [57] show slotsFromS3 spread summarise [61] tally tbl_sum transmute type_sum [65] ungroup unnest see '?methods' for accessing help and source code
'데이터' 카테고리의 다른 글
기상분석 I (바람 분석) (7) 2024.10.13 시계열 분석 시리즈 II (Plots, Decomposition, Autocorrelation) (0) 2024.03.03 데이터 주무르기 (1) 2024.02.18 Association Rule 분석(장바구니 분석) (2) 2024.02.04 k-mean Clustering (2) 2024.01.21