Rを使ったたくさんの時系列データに対する相関分析&時系列分析

前回の記事ではたくさんの系列に対してまとめて相関係数を計算する方法を示しました。
Rの関数corをデータフレームに対して使う方法ですが、これは簡単ではありますが望ましくはないと思います。
なぜならばcorに与えるデータフレームはすべての要素が数値でなければならないという厳しい制約条件があり、前回の記事の例で言えば、日付のフィールドを落とさなければならなかったのです。
これではたとえば相関係数の時間的変動を追いかけるなどといった複雑な相関分析ができません。
よってこのようなニーズに答えることのできる相関分析方法を示します。

データは前回の記事と同様みずほ銀行が作成した為替データを使います。

quote

#R
> library(dplyr)
> library(lubridate)
> library(magrittr)
> q <- read.csv("quote.csv")
> q %<>% select(-IDR.100., -MYR, -X) 
> q[q == "*****"] <- NA
> q %<>% mutate(date = ymd(date))
> q[(sapply(q, class) == "factor")] %<>% lapply(as.numeric) 
> q %<>% mutate(KRW.100. = coalesce(KRW.100., 0) + coalesce(KRW.100..1)) %>% select(-KRW.100..1)
    %>% mutate(CNY = coalesce(CNY, 0) + coalesce(CNY.1, 0)) %>% select(-CNY.1)
     date                 USD              GBP             EUR              CAD              CHF              SEK             DKK
 Min.   :2002-04-01   Min.   : 75.76   Min.   :117.5   Min.   : 94.23   Min.   : 70.45   Min.   : 76.16   Min.   :10.43   Min.   :12.67
 1st Qu.:2006-02-26   1st Qu.: 94.88   1st Qu.:141.4   1st Qu.:119.85   1st Qu.: 82.02   1st Qu.: 86.29   1st Qu.:12.62   1st Qu.:16.11
 Median :2010-01-27   Median :107.78   Median :174.3   Median :131.92   Median : 86.64   Median : 91.66   Median :14.21   Median :17.72
 Mean   :2010-01-25   Mean   :104.80   Mean   :172.4   Mean   :130.97   Mean   : 89.45   Mean   : 96.81   Mean   :14.10   Mean   :17.59
 3rd Qu.:2013-12-21   3rd Qu.:116.80   3rd Qu.:197.8   3rd Qu.:138.72   3rd Qu.: 96.20   3rd Qu.:106.57   3rd Qu.:15.18   3rd Qu.:18.62
 Max.   :2017-11-27   Max.   :133.20   Max.   :250.6   Max.   :169.62   Max.   :124.98   Max.   :136.89   Max.   :18.49   Max.   :22.75

      NOK             AUD              NZD             ZAR             BHD             CNY             HKD             INR
 Min.   :11.81   Min.   : 56.56   Min.   :45.28   Min.   : 6.62   Min.   :201.2   Min.   :  2.0   Min.   : 9.76   Min.   :1.400
 1st Qu.:14.17   1st Qu.: 79.09   1st Qu.:64.76   1st Qu.: 9.66   1st Qu.:252.1   1st Qu.:116.0   1st Qu.:12.24   1st Qu.:1.720
 Median :15.87   Median : 84.19   Median :74.39   Median :11.76   Median :286.4   Median :215.5   Median :13.84   Median :1.940
 Mean   :15.96   Mean   : 84.59   Mean   :73.34   Mean   :12.41   Mean   :278.5   Mean   :208.9   Mean   :13.48   Mean   :2.095
 3rd Qu.:17.06   3rd Qu.: 91.52   3rd Qu.:81.15   3rd Qu.:15.99   3rd Qu.:310.7   3rd Qu.:288.0   3rd Qu.:15.02   3rd Qu.:2.500
 Max.   :21.80   Max.   :107.66   Max.   :97.61   Max.   :19.69   Max.   :353.8   Max.   :479.0   Max.   :17.08   Max.   :3.070

      PHP             SGD           KRW.100.          THB             KWD             SAR             AED             MXN
 Min.   :1.740   Min.   :58.14   Min.   :  2.0   Min.   :2.420   Min.   :274.6   Min.   :20.21   Min.   :20.63   Min.   : 5.050
 1st Qu.:1.950   1st Qu.:64.71   1st Qu.:180.0   1st Qu.:2.720   1st Qu.:332.8   1st Qu.:25.31   1st Qu.:25.85   1st Qu.: 6.740
 Median :2.210   Median :70.41   Median :402.0   Median :2.910   Median :369.5   Median :28.75   Median :29.35   Median : 7.850
 Mean   :2.197   Mean   :72.26   Mean   :366.1   Mean   :3.003   Mean   :362.2   Mean   :27.96   Mean   :28.54   Mean   : 8.423
 3rd Qu.:2.362   3rd Qu.:79.25   3rd Qu.:578.0   3rd Qu.:3.230   3rd Qu.:399.0   3rd Qu.:31.17   3rd Qu.:31.81   3rd Qu.:10.310
 Max.   :2.810   Max.   :92.58   Max.   :666.0   Max.   :4.130   Max.   :436.6   Max.   :35.53   Max.   :36.27   Max.   :14.810
                                 NA's   :471
      PGK              HUF             CZK              PLN            RUB             TRY          IDR.100..1        MYR.1
 Min.   :   2.0   Min.   : 2.00   Min.   :   2.0   Min.   :   2   Min.   :1.410   Min.   :28.26   Min.   :0.760   Min.   :   2.0
 1st Qu.: 344.0   1st Qu.:11.00   1st Qu.: 204.0   1st Qu.: 384   1st Qu.:1.920   1st Qu.:39.17   1st Qu.:0.870   1st Qu.: 233.0
 Median : 610.0   Median :15.00   Median : 437.0   Median : 650   Median :2.500   Median :45.27   Median :0.960   Median : 412.0
 Mean   : 638.1   Mean   :17.31   Mean   : 467.3   Mean   : 669   Mean   :2.404   Mean   :44.00   Mean   :1.047   Mean   : 464.8
 3rd Qu.: 928.8   3rd Qu.:20.00   3rd Qu.: 627.0   3rd Qu.: 907   3rd Qu.:2.870   3rd Qu.:49.49   3rd Qu.:1.230   3rd Qu.: 709.0
 Max.   :1324.0   Max.   :45.00   Max.   :1266.0   Max.   :1453   Max.   :3.300   Max.   :58.52   Max.   :1.470   Max.   :1034.0
 NA's   :450      NA's   :1006    NA's   :1006     NA's   :1006   NA's   :2089    NA's   :2089                    NA's   :471
      TWD
 Min.   :2.510
 1st Qu.:2.947
 Median :3.390
 Mean   :3.296
 3rd Qu.:3.570
 Max.   :4.050

前回の記事と同様の前処理をしました。
このままの形は神Hadleyが言うところのtidy dataではないので縦持ちにしてやりましょう。

#R
> library(tidyr)
> qt <- q %>% gather(currency, price, -date)
> head(qt)
        date currency  price
1 2002-04-01      USD 133.15
2 2002-04-02      USD 133.20
3 2002-04-03      USD 133.20
4 2002-04-04      USD 133.10
5 2002-04-05      USD 132.30
6 2002-04-08      USD 131.55

我々がやりたいのは相関分析なのですから、すべてのcurrencyの組み合わせを作らなければなりません。
SQLをやる人はすぐに思いつくのでしょうが、自分とのjoinによりこれを実現します。

#R
> qt.comb <- inner_join(qt, qt, by = "date")
> head(qt.comb)
        date currency.x price.x currency.y price.y
1 2002-04-01        USD  133.15        USD  133.15
2 2002-04-01        USD  133.15        GBP  189.79
3 2002-04-01        USD  133.15        EUR  116.12
4 2002-04-01        USD  133.15        CAD   83.48
5 2002-04-01        USD  133.15        CHF   79.28
6 2002-04-01        USD  133.15        SEK   12.87
> nrow(qt)
[1] 130696
> nrow(qt.comb)
[1] 4443664

すべての通貨の組み合わせですので、各日付ごとにデータの行数が通貨の種類数(34)マイナス1の33倍になっています。
もしある組み合わせのデータが必要であれば、currency.xとcurrency.yとを指定すれば通貨のペアが得られます。

ここからすべての組み合わせについて相関係数を計算するためには下記のようにやります。

#R
> qt.comb %>%
    group_by(currency.x, currency.y) %>%
    summarise(cor = cor(price.x, price.y, use = "pairwise.complete.obs")) %>%
    filter(currency.x > currency.y) %>%
    arrange(desc(cor))
# A tibble: 561 x 3
# Groups:   currency.x [33]
   currency.x currency.y       cor
        <chr>      <chr>     <dbl>
 1        USD        AED 0.9999964
 2        SAR        AED 0.9999874
 3        USD        SAR 0.9999840
 4        SAR        BHD 0.9999622
 5        BHD        AED 0.9999592
 6        USD        BHD 0.9999528
 7        EUR        DKK 0.9999236
 8        USD        HKD 0.9997775
 9        HKD        AED 0.9997723
10        SAR        HKD 0.9997542
# ... with 551 more rows

これで昨日と同じ結果が得られました。

さて、この手法では日付列を残しているので相関係数の時間変化を追うことができます。
それは次のように実現します。

> qt.cor <- qt.comb %>%
   mutate(year = year(date)) %>%
   group_by(year, currency.x, currency.y) %>%
   summarise(cor = cor(price.x, price.y, use = "pairwise.complete.obs")) %>%
   filter(currency.x > currency.y) %>%
   arrange(desc(cor))
> qt.cor
# A tibble: 8,976 x 4
# Groups:   year, currency.x [528]
    year currency.x currency.y       cor
   <dbl>      <chr>      <chr>     <dbl>
 1  2014        USD        AED 0.9999979
 2  2016        USD        AED 0.9999972
 3  2014        USD        SAR 0.9999962
 4  2014        USD        BHD 0.9999959
 5  2014        SAR        AED 0.9999949
 6  2005        USD        AED 0.9999943
 7  2013        USD        SAR 0.9999942
 8  2002        USD        SAR 0.9999941
 9  2014        BHD        AED 0.9999940
10  2003        USD        AED 0.9999932
# ... with 8,966 more rows

2014年の米ドルとUAEディルハム(AED)との相関係数99.99979%、どこかのシステムの稼働率みたいな数字で、完璧なドルペッグのオペレーションがお見事です。
トップ10に2015年USD.AEDが出てこないところを見ると、この年はうまくいかなかったのでしょうか。
チャートで確認してみましょう。

#R
> library(ggplot2)
> qt.cor %>% filter(currency.x == "USD", currency.y == "AED") %>% ggplot(aes(x = year, y = cor)) + geom_line()
USDとAEDとの相関の年次変化。2007年(リーマンショック)で大きなトラッキングエラーが起こっているものの、絶対値で見ると十分高い相関係数値である。

さて、相関係数が高いかどうかも大事ですが、最近相関が高くなった通貨や、最近相関が無くなった通貨などを知ることによって我々は利益のチャンスを狙えるかもしれません。
相関係数の変動が激しい通貨はどれでしょうか?
変動の激しさは分散や標準偏差で測るのが基本です。

#R
> qt.cor %>%
   group_by(currency.x, currency.y) %>%
   summarise(var = var(cor, na.rm=TRUE), n = sum(!is.na(cor))) %>%
   arrange(desc(var)) %>%
   filter(n > 5)
# A tibble: 496 x 4
# Groups:   currency.x [31]
   currency.x currency.y       var     n
        <chr>      <chr>     <dbl> <int>
 1        THB        RUB 0.3764387     8
 2        RUB      MYR.1 0.3729797     8
 3        TWD   KRW.100. 0.3689234    14
 4        RUB        CAD 0.3681076     8
 5        SGD   KRW.100. 0.3672527    14
 6        ZAR        TRY 0.3572152     8
 7      MYR.1   KRW.100. 0.3568511    14
 8        TRY        RUB 0.3535817     8
 9        SGD        RUB 0.3519936     8
10   KRW.100.        GBP 0.3496538    14
# ... with 486 more rows

変動一位はタイバーツ vs ロシアルーブルです。
そう言えばこの組み合わせは前回の記事で逆相関が最大だった組み合わせですね。
内容を調べてみましょう。

#R
> qt.cor %>%
    unite(cxy, currency.x, currency.y, remove = FALSE) %>%
    filter(cxy %in% c("THB_RUB", "RUB_MYR.1", "TWD_KRW.100.", "RUB_CAD", "SGD_KRW.100.")) %>%
    ggplot(aes(year, cor)) + geom_line(aes(color = cxy))
相関係数の変動が大きい通貨ペアトップ5

なんということでしょう。負の相関だと思われていたRUB-THBは2010-2012は正相関でした。
全体で計算する危険性を感じずにいられません。
しかし気になるのはRUB-THBに限らず5つの通貨の組み合わせで似たようなトレンドを描いていること、すなわち相関が相関しているということです。
これは対象の通貨価格が変動しているのではなく、基準である日本円の価値が変動しているからではないかと推測できます。
そうでなければここまでぴったりの相関の相関(ややこしい)が起きるはずはありません。

深堀するのであればこれらの年に実際に価格が上げたのか下げたのかを見ていくのが正しいのではないかという気がします。
しかし相関分析の手法を紹介する目的から逸れそうなのでとりあえずここらあたりで一旦分析を閉めたいと思います。
こういうような見方をしてほしい、できないのか、などといったコメントやご要望があれば承りますのでどうぞ遠慮なくコメントください。

まとめます。
データフレームをそのデータフレーム自身とjoinして、group_byすれば任意のレベルで相関分析することができることがわかりました。
相関分析は多変量の探索的データ解析の基本になるので、これからも積極的に使っていきたいと思います。

コメントを残す