1 시계열 데이터 역학관계

R에서 시계열 데이터를 처리하기 위한 전용 자료구조로 zoo, Z’s Ordered Observationsxts, eXtensible Time Series가 많이 활용되었지만, tidyverse 등장이후 시계열 자료구조도 lubridate 팩키지의 등장 이후 시계열이 아닌 많이 사용되는 데이터프레임에서 표준으로 자리를 잡아가고 있으며 tsibble이 두가지 큰 흐름의 간극을 매워가면서 새로운 지평을 열어가고 있다.

시계열 역학관계

2 xts 기본

xts 팩키지는 시계열 데이터를 기본 행렬자료구조에 시계열 정보를 인덱스로 붙여 표현한다.

2.1 xts 데이터 생성 및 분해

xts 자료구조는 zoo를 기반으로 확장한 시계열 자료구조로 행렬을 데이터에 담고 있고, 시계열 정보를 인덱스를 표현한다. 따라서 두가지 데이터와 시계열 인덱스가 있는 경우 xts() 함수에 넣어 order.by= 인자를 넣어 xts 자료구조를 생성시킨다.

library(xts)

time_data  <- matrix(1:6, ncol = 2, nrow = 3)
time_index <- Sys.Date() + 1:3

time_xts <- xts(time_data, order.by = time_index)

class(time_xts)
[1] "xts" "zoo"
time_xts
           [,1] [,2]
2019-02-20    1    4
2019-02-21    2    5
2019-02-22    3    6

xts 자료구조에서 시계열 정보를 날려버리고 데이터만 취할 경우 coredata() 함수를 사용하고, 시계열 인덱스 색인 정보만 추출할 경우 index() 함수를 사용해서 추출한다.

coredata(time_xts)
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
index(time_xts)
[1] "2019-02-20" "2019-02-21" "2019-02-22"

2.2 xts 데이터 가져오기

AirPassengers 시계열 데이터는 ts 자료구조를 갖고 있어 이를 xts 자료구조로 변환시킬 경우 as.xts() 함수를 사용한다. 마찬가지로 read_._csv(), read.zoo()으로 데이터프레임으로 불러온 후에 xts() 함수에 벡터 혹은 행렬로 데이터를 넣고 시계열(POSIXlt) 벡터를 order.by=에 지정하여 xts 객체를 생성한다.

## ts 객체변환
library(tidyverse)
as.xts(AirPassengers) %>% head
         [,1]
Jan 1949  112
Feb 1949  118
Mar 1949  132
Apr 1949  129
May 1949  121
Jun 1949  135
## 데이터프레임 --> xts
sunspot_df <- read_csv("https://raw.githubusercontent.com/statsmodels/statsmodels/master/statsmodels/datasets/sunspots/sunspots.csv", col_types = cols(
  YEAR = col_integer(),
  SUNACTIVITY = col_double()
))

sunspot_df <- sunspot_df %>% 
  mutate(YEAR = lubridate::ymd(YEAR, truncated = 2L))

sunspot_xts <- xts(sunspot_df[,-1], order.by=sunspot_df$YEAR)

sunspot_xts %>% head
           SUNACTIVITY
1700-01-01           5
1701-01-01          11
1702-01-01          16
1703-01-01          23
1704-01-01          36
1705-01-01          58

2.3 xts 데이터 쿼리

xts 시계열 데이터가 준비되면, 원하는 정보를 xts 자료구조에서 쿼리를 작성해서 던지게 되면 뽑아낼 수 있다. PerformanceAnalytics 팩키지 edhec 데이터셋은 이미 xts 자료구조로 되어 있어 쿼리 연습을 하기 적합하다. EDHEC - Risk Hedge Fund Style Indices는 헷지펀드 인덱스 수익률에 대한 시계열 정보가 담겨있다.

library(PerformanceAnalytics)
data("edhec")

edhec["2009-01", 1:3]
           Convertible Arbitrage CTA Global Distressed Securities
2009-01-31                0.0491    -0.0016                0.0082
edhec["2009-01/2009-05", 1:3]
           Convertible Arbitrage CTA Global Distressed Securities
2009-01-31                0.0491    -0.0016                0.0082
2009-02-28                0.0164    -0.0031               -0.0122
2009-03-31                0.0235    -0.0180                0.0022
2009-04-30                0.0500    -0.0140                0.0387
2009-05-31                0.0578     0.0213                0.0504

index() 함수를 시계열 인덱스를 빼서 부울 연산자와 조합하여 해당기간 정보를 뽑아내거나 시계열 데이터를 별도 벡터로 정리한 후에 이를 색인으로 활용하여 데이터를 추출하는 것도 가능하다.

edhec[index(edhec) > "2009-01-01" & index(edhec) <= "2009-02-28"]
           Convertible Arbitrage CTA Global Distressed Securities
2009-01-31                0.0491    -0.0016                0.0082
2009-02-28                0.0164    -0.0031               -0.0122
           Emerging Markets Equity Market Neutral Event Driven
2009-01-31          -0.0112                0.0079       0.0132
2009-02-28          -0.0133               -0.0046      -0.0091
           Fixed Income Arbitrage Global Macro Long/Short Equity
2009-01-31                 0.0112       0.0029           -0.0017
2009-02-28                 0.0065      -0.0055           -0.0161
           Merger Arbitrage Relative Value Short Selling Funds of Funds
2009-01-31           0.0056         0.0100        0.0282         0.0060
2009-02-28           0.0006        -0.0016        0.0328        -0.0037
q_dates <- lubridate::ymd(c("2009-01-31", "2009-02-28"))
edhec[q_dates]
           Convertible Arbitrage CTA Global Distressed Securities
2009-01-31                0.0491    -0.0016                0.0082
2009-02-28                0.0164    -0.0031               -0.0122
           Emerging Markets Equity Market Neutral Event Driven
2009-01-31          -0.0112                0.0079       0.0132
2009-02-28          -0.0133               -0.0046      -0.0091
           Fixed Income Arbitrage Global Macro Long/Short Equity
2009-01-31                 0.0112       0.0029           -0.0017
2009-02-28                 0.0065      -0.0055           -0.0161
           Merger Arbitrage Relative Value Short Selling Funds of Funds
2009-01-31           0.0056         0.0100        0.0282         0.0060
2009-02-28           0.0006        -0.0016        0.0328        -0.0037

최근 3개월, 마지막 1년 등을 first(), last() 함수로 구현할 수 있다. xts::last()xts::first()를 조합시켜 중간, 예를 들어 최근 1년 첫 3개월을 추출할 수도 있다.

xts::first(edhec[, c("Funds of Funds", "Short Selling")], "3 months")
           Funds of Funds Short Selling
1997-01-31         0.0317       -0.0166
1997-02-28         0.0106        0.0426
1997-03-31        -0.0077        0.0778
xts::last(edhec[, c("Funds of Funds", "Short Selling")], "7 months")
           Funds of Funds Short Selling
2009-02-28        -0.0037        0.0328
2009-03-31         0.0008       -0.0462
2009-04-30         0.0092       -0.0820
2009-05-31         0.0312        0.0008
2009-06-30         0.0024       -0.0094
2009-07-31         0.0153       -0.0596
2009-08-31         0.0113       -0.0165
xts::last(edhec[, c("Funds of Funds", "Short Selling")], "1 year") %>% xts::first(., "3 months")
           Funds of Funds Short Selling
2009-01-31         0.0060        0.0282
2009-02-28        -0.0037        0.0328
2009-03-31         0.0008       -0.0462

2.4xts 데이터 결합

x_df, y_df 데이터프레임을 x_xts, y_xts 객체로 변환을 한 후에 merge() 함수에 index= 인자를 넘겨 두 xts 객체를 결합시킬 수 있다. 그리고 이를 +, -, *, / 등 사칙연산도 수행시킬 수 있다.

library(lubridate)
## 두 xts 객체 생성
x_df <- tribble(~"날짜", ~"값",
                "2019-02-09", 0,
                "2019-02-10", 0,
                "2019-02-11", 0)

y_df <- tribble(~"날짜", ~"값",
                "2019-02-09", 1,
                "2019-02-10", 2,
                "2019-02-12", 3)

x_xts <- xts(x_df$``, order.by = ymd(x_df$`날짜`))
y_xts <- xts(y_df$``, order.by = ymd(y_df$`날짜`))

## 날짜가 다른 두 xts 객체 결합
x_xts + y_xts
           e1
2019-02-09  1
2019-02-10  2
## 날짜가 다른 두 xts 객체 기준 잡고 결합
(x_join <- merge(x_xts, index(y_xts), fill=0))
           x_xts
2019-02-09     0
2019-02-10     0
2019-02-11     0
2019-02-12     0
(y_join <- merge(y_xts, index(x_xts), fill=0))
           y_xts
2019-02-09     1
2019-02-10     2
2019-02-11     0
2019-02-12     3
## 연산
x_join / y_join
           x_xts
2019-02-09     0
2019-02-10     0
2019-02-11   NaN
2019-02-12     0

2.5xts 데이터 병합(merge)

merge() 함수를 활용하여 서로 다른 변수를 갖는 xts 객체를 병합(merge) 할 수 있다. 이를 위해서 join= 인자를 사용하고 더 나아가 결측값도 fill = na.locf을 사용해서 채워넣는 것이 가능하다.

## 두 xts 객체 생성
x_df <- tribble(~"날짜", ~"삼성전자",
                "2019-02-09", 0,
                "2019-02-10", 0,
                "2019-02-11", 0)

y_df <- tribble(~"날짜", ~"KPMG",
                "2019-02-09", 1,
                "2019-02-10", 2,
                "2019-02-12", 3)

x_xts <- xts(x_df$`삼성전자`, order.by = ymd(x_df$`날짜`))
y_xts <- xts(y_df$KPMG, order.by = ymd(y_df$`날짜`))

merge(x_xts, y_xts)
           x_xts y_xts
2019-02-09     0     1
2019-02-10     0     2
2019-02-11     0    NA
2019-02-12    NA     3
merge(x_xts, y_xts, join="inner")
           x_xts y_xts
2019-02-09     0     1
2019-02-10     0     2
merge(x_xts, y_xts, join="left", fill = na.locf)
           x_xts y_xts
2019-02-09     0     1
2019-02-10     0     2
2019-02-11     0     2

횡으로 xts 객체를 결합하는 것과는 별개로 종으로 rbind 함수를 사용해서 결합시킬 수도 있다.

## 두 xts 객체 결합
rbind(x_xts, y_xts)
           [,1]
2019-02-09    0
2019-02-09    1
2019-02-10    0
2019-02-10    2
2019-02-11    0
2019-02-12    3

2.6 xts 결측값 처리

na.locf()함수를 사용해서 결측값을 채워넣을 수 있다. 물로 na.fill() 함수에 fill= 인자를 지정해서 결측값을 넣는 것도 가능하다. 그외에도 na.trim(), na.omit() 함수를 이용해서 결측값을 제거할 수도 있고ㅡ, na.approx() 함수로 결측값에 대한 보정도 가능하다.

## NA 결측값을 갖는 `xts` 객체 생성
na_df <- tribble(~"날짜", ~"KPMG",
                 "2019-02-07", NA,
                 "2019-02-09", 1,
                 "2019-02-10", 2,
                 "2019-02-11", NA,
                 "2019-02-12", NA,
                 "2019-02-13", 3,
                 "2019-02-17", NA)

na_xts <- xts(na_df$KPMG, order.by = ymd(na_df$`날짜`))

cbind(na_xts, na.locf(na_xts), na.locf(na_xts, fromLast = TRUE))
           na_xts na.locf.na_xts. na.locf.na_xts..fromLast...TRUE.
2019-02-07     NA              NA                                1
2019-02-09      1               1                                1
2019-02-10      2               2                                2
2019-02-11     NA               2                                3
2019-02-12     NA               2                                3
2019-02-13      3               3                                3
2019-02-17     NA               3                               NA
na.fill(na_xts, fill=999)
           [,1]
2019-02-07  999
2019-02-09    1
2019-02-10    2
2019-02-11  999
2019-02-12  999
2019-02-13    3
2019-02-17  999
na.trim(na_xts)
           [,1]
2019-02-09    1
2019-02-10    2
2019-02-11   NA
2019-02-12   NA
2019-02-13    3
na.omit(na_xts)
           [,1]
2019-02-09    1
2019-02-10    2
2019-02-13    3
na.approx(na_xts)
               [,1]
2019-02-09 1.000000
2019-02-10 2.000000
2019-02-11 2.333333
2019-02-12 2.666667
2019-02-13 3.000000
na.spline(na_xts)
                 [,1]
2019-02-07 -2.0000000
2019-02-09  1.0000000
2019-02-10  2.0000000
2019-02-11  2.6666667
2019-02-12  3.0000000
2019-02-13  3.0000000
2019-02-17 -0.3333333

2.7 xts 지연시차(lag), 선행시차(lead)

lag() 함수의 k= 인수값을 달리하여 지연시차(lag) 및 선행시차(lead)를 담아낼 수 있다.

lead_xts <- lag(na_xts, k = -1)
lag_xts  <- lag(na_xts, k = 1)

merge(lead_xts, na_xts, lag_xts)
           lead_xts na_xts lag_xts
2019-02-07       NA     NA      NA
2019-02-09       NA      1      NA
2019-02-10        1      2       1
2019-02-11        2     NA       2
2019-02-12       NA     NA      NA
2019-02-13       NA      3      NA
2019-02-17        3     NA       3

시계열 데이터의 정상성(stationarity)을 확보화기 위해서 차분을 사용하고 이를 계절성(seasonality)을 함께 반영하는 경우 diff() 함수에 differences=를 인자로 함께 넣어 구현한다.

air_xts <- as.xts(AirPassengers) %>% head(., n=24)

air_diff <- diff(air_xts, lag=12, differences = 1)
merge(air_xts, air_diff)
         air_xts air_diff
Jan 1949     112       NA
Feb 1949     118       NA
Mar 1949     132       NA
Apr 1949     129       NA
May 1949     121       NA
Jun 1949     135       NA
Jul 1949     148       NA
Aug 1949     148       NA
Sep 1949     136       NA
Oct 1949     119       NA
Nov 1949     104       NA
Dec 1949     118       NA
Jan 1950     115        3
Feb 1950     126        8
Mar 1950     141        9
Apr 1950     135        6
May 1950     125        4
Jun 1950     149       14
Jul 1950     170       22
Aug 1950     170       22
Sep 1950     158       22
Oct 1950     133       14
Nov 1950     114       10
Dec 1950     140       22

3 apply()rollapply() 함수

3.1 이산 apply() 함수

split-apply-combine 패턴을 동일하게 xts 시계열에도 적용시킬 수 있다. 이를 통해서 일별, 주별, 월별, 분기별, 년도별로 다양한 통계량을 산출할 수 있게 된다. endpoints() 함수로 시계열 끝점을 두어 시계열을 쪼개고(split) 함수를 적용(apply)시킨 후에 period.apply()함수로 결합(combine)하는 과정을 거친다.

자주 사용되는 apply.weekly(), apply.monthly(), apply.yearly(), apply.quarterly() 등 함수를 바로 사용하는 것도 가능하다.

ep <- endpoints(edhec, on="years")

period.apply(edhec[ ,"Funds of Funds"], INDEX=ep, FUN=mean) %>% head
           Funds of Funds
1997-12-31    0.013591667
1998-12-31    0.003725000
1999-12-31    0.021300000
2000-12-31    0.006616667
2001-12-31    0.002933333
2002-12-31    0.001066667
apply.quarterly(edhec[ ,"Funds of Funds"], mean) %>% head
           Funds of Funds
1997-03-31    0.011533333
1997-06-30    0.016966667
1997-09-30    0.027333333
1997-12-31   -0.001466667
1998-03-31    0.019766667
1998-06-30    0.002466667

3.2 연속 rollapply() 함수

apply() 함수는 split으로 쪼개진 각 부분 시계열에 대해 함수연산을 적용시켜 결과를 취합하는 이산연산 과정인 반면, rollapply() 함수는 이동평균이나 합계(sum()) 함수처럼 연속연산과 대비된다.

bimonthly <- rollapply(edhec["200701/12", "Funds of Funds"], 2, mean)
edhec_sum <- rollapply(edhec["200701/12", "Funds of Funds"], 3, sum)
merge(edhec["200701/12", "Funds of Funds"], bimonthly, edhec_sum)
           Funds.of.Funds Funds.of.Funds.1 Funds.of.Funds.2
2007-01-31         0.0121               NA               NA
2007-02-28         0.0096          0.01085               NA
2007-03-31         0.0096          0.00960           0.0313
2007-04-30         0.0163          0.01295           0.0355
2007-05-31         0.0204          0.01835           0.0463
2007-06-30         0.0082          0.01430           0.0449
2007-07-31         0.0041          0.00615           0.0327
2007-08-31        -0.0222         -0.00905          -0.0099
2007-09-30         0.0199         -0.00115           0.0018
2007-10-31         0.0303          0.02510           0.0280
2007-11-30        -0.0148          0.00775           0.0354
2007-12-31         0.0040         -0.00540           0.0195