2.1 tsibbleオブジェクト

時系列データは、一連の数値 (観測値) と各数値が観測された時点の情報 (インデックス) から構成されている、と考えることができます。この情報はRではtsibbleオブジェクトとして保存できます。

インデックス変数

過去数年分の年次観測値があるとします。

Year Observation
2015 123
2016 39
2017 78
2018 52
2019 110

このデータをtsibble()関数を使ってtsibbleオブジェクトに作り替えます。

y <- tsibble(
  Year = 2015:2019,
  Observation = c(123, 39, 78, 52, 110),
  index = Year
)

tsibbleオブジェクトは、 tidyなデータフレーム (tibbleオブジェクト)に時間軸を導入して拡張したものです。時間軸を指定するindexYear列を指定することでYearがインデックス変数となり、観測値 (Observation) と観測時点 (Year) が結び付けられます。

一年に一度以上の頻度で観測される値では、時間クラス関数で変換した後で、インデックス変数に指定する必要があります。例えば、月次データセットzがあるとします。

z
#> # A tibble: 5 × 2
#>   Month    Observation
#>   <chr>          <dbl>
#> 1 2019 Jan          50
#> 2 2019 Feb          23
#> 3 2019 Mar          34
#> 4 2019 Apr          30
#> 5 2019 May          25

このデータは以下のコードでtsibbleオブジェクトに変換できます。

z %>%
  mutate(Month = yearmonth(Month)) %>%
  as_tsibble(index = Month)
#> # A tsibble: 5 x 2 [1M]
#>      Month Observation
#>      <mth>       <dbl>
#> 1 2019 Jan          50
#> 2 2019 Feb          23
#> 3 2019 Mar          34
#> 4 2019 Apr          30
#> 5 2019 May          25

まず時間クラス関数yearmonth()を使ってMonth列を文字から月次時間オブジェクトに変換します。次に、as_tsibble()の引数indexMonth列を指定して、データフレームをtsibbleに変換します。1行目右端の”[1M]“は月次データであることを表しています。

観測頻度ごとに、異なる時間クラス関数を使います。

観測頻度 時間クラス関数
年次 start:end
四半期 yearquarter()
月次 yearmonth()
週次 yearweek()
日次 as_date(), ymd()
さらに高頻度 as_datetime(), ymd_hms()

キー変数

tsibbleでは、1つのオブジェクトの中に複数の時系列データを保存できます。オリンピック陸上競技の百メートルから一万メートルまでの女性と男性ごとの最速記録を含むデータセットに関心があるとします。

olympic_running
#> # A tsibble: 312 x 4 [4Y]
#> # Key:       Length, Sex [14]
#>     Year Length Sex    Time
#>    <int>  <int> <chr> <dbl>
#>  1  1896    100 men    12  
#>  2  1900    100 men    11  
#>  3  1904    100 men    11  
#>  4  1908    100 men    10.8
#>  5  1912    100 men    10.8
#>  6  1916    100 men    NA  
#>  7  1920    100 men    10.8
#>  8  1924    100 men    10.6
#>  9  1928    100 men    10.8
#> 10  1932    100 men    10.3
#> # ℹ 302 more rows

1行目から、これはtsibbleオブジェクトであり、312行・4列と分かります。その横の”[4Y]“は観測頻度が4年ごとであることを表しています。2行目がキー構造で、14の時系列データがtsibbleオブジェクトの中に保存されていることが分かります。3行目以下が観測値のプレビューで、最初の10個が示されていますが、その中には1916年に欠損値が見て取れます。第一次世界大戦中はオリンピックが開催されなかったためです。

このオブジェクト内の14個の時系列データのそれぞれは、LengthSexのキー変数の組み合わせによって一意に特定されます。distinct()関数を使えば、キー変数やキー変数の組み合わせごとのカテゴリーを示すことができます。

olympic_running %>% distinct(Sex)
#> # A tibble: 2 × 1
#>   Sex  
#>   <chr>
#> 1 men  
#> 2 women

tsibbleオブジェクトを加工するのに使える関数

tsibbleオブジェクトを加工するには、mutate()filter()select()summarise()といったdplyrパッケージの関数が使えます。例として、オーストラリアでの医薬品販売データPBS tsibble を加工してみましょう。

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 Jul Concessional Co-pay… A     Alimenta… A01   STOMATOL…   18228 67877
#>  2 1991 Aug Concessional Co-pay… A     Alimenta… A01   STOMATOL…   15327 57011
#>  3 1991 Sep Concessional Co-pay… A     Alimenta… A01   STOMATOL…   14775 55020
#>  4 1991 Oct Concessional Co-pay… A     Alimenta… A01   STOMATOL…   15380 57222
#>  5 1991 Nov Concessional Co-pay… A     Alimenta… A01   STOMATOL…   14371 52120
#>  6 1991 Dec Concessional Co-pay… A     Alimenta… A01   STOMATOL…   15028 54299
#>  7 1992 Jan Concessional Co-pay… A     Alimenta… A01   STOMATOL…   11040 39753
#>  8 1992 Feb Concessional Co-pay… A     Alimenta… A01   STOMATOL…   15165 54405
#>  9 1992 Mar Concessional Co-pay… A     Alimenta… A01   STOMATOL…   16898 61108
#> 10 1992 Apr Concessional Co-pay… A     Alimenta… A01   STOMATOL…   18141 65356
#> # ℹ 67,586 more rows

これは、1991年7月から2008年6月までの、オーストラリア公的医療保険における処方薬月次データです。さまざまな補助タイプごと、また、解剖学的治療のための医薬品(ATC)ごとに分類されています。ここでは、Costの時系列データ(オーストラリア・ドル建ての処方薬総コスト)にだけ関心があるとします。

filter()関数を使ってA10処方薬だけを取り出すことができます。

PBS %>%
  filter(ATC2 == "A10")
#> # A tsibble: 816 x 9 [1M]
#> # Key:       Concession, Type, ATC1, ATC2 [4]
#>       Month Concession   Type   ATC1  ATC1_desc ATC2  ATC2_desc Scripts   Cost
#>       <mth> <chr>        <chr>  <chr> <chr>     <chr> <chr>       <dbl>  <dbl>
#>  1 1991 Jul Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   89733 2.09e6
#>  2 1991 Aug Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   77101 1.80e6
#>  3 1991 Sep Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   76255 1.78e6
#>  4 1991 Oct Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   78681 1.85e6
#>  5 1991 Nov Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   70554 1.69e6
#>  6 1991 Dec Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   75814 1.84e6
#>  7 1992 Jan Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   64186 1.56e6
#>  8 1992 Feb Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   75899 1.73e6
#>  9 1992 Mar Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   89445 2.05e6
#> 10 1992 Apr Concessional Co-pa… A     Alimenta… A10   ANTIDIAB…   97315 2.23e6
#> # ℹ 806 more rows

これで該当するtsibbleの行を抽出できました。次に、この先の分析で必要な列だけを選択してオブジェクトを簡素化しましょう。

PBS %>%
  filter(ATC2 == "A10") %>%
  select(Month, Concession, Type, Cost)
#> # A tsibble: 816 x 4 [1M]
#> # Key:       Concession, Type [4]
#>       Month Concession   Type           Cost
#>       <mth> <chr>        <chr>         <dbl>
#>  1 1991 Jul Concessional Co-payments 2092878
#>  2 1991 Aug Concessional Co-payments 1795733
#>  3 1991 Sep Concessional Co-payments 1777231
#>  4 1991 Oct Concessional Co-payments 1848507
#>  5 1991 Nov Concessional Co-payments 1686458
#>  6 1991 Dec Concessional Co-payments 1843079
#>  7 1992 Jan Concessional Co-payments 1564702
#>  8 1992 Feb Concessional Co-payments 1732508
#>  9 1992 Mar Concessional Co-payments 2046102
#> 10 1992 Apr Concessional Co-payments 2225977
#> # ℹ 806 more rows

filter()は特定の行を抽出するのに対し、select()関数は特定の列を選択します。

インデックス変数のMonthと、キー変数であるConcessionTypeをここでは明示的に選択していますが、実は選択せずにselect(Cost)としても残ります。(各行がキーとインデックスの一意な組み合わせであるために)tsibbleにとって必要だからです。

summarise()を使って、キー変数を越えてデータを集約できます。例えば、ConcessionTypeのキー変数がどうであれ、各月の総コストを求めたいとします。

PBS %>%
  filter(ATC2 == "A10") %>%
  select(Month, Concession, Type, Cost) %>%
  summarise(TotalC = sum(Cost))
#> # A tsibble: 204 x 2 [1M]
#>       Month  TotalC
#>       <mth>   <dbl>
#>  1 1991 Jul 3526591
#>  2 1991 Aug 3180891
#>  3 1991 Sep 3252221
#>  4 1991 Oct 3611003
#>  5 1991 Nov 3565869
#>  6 1991 Dec 4306371
#>  7 1992 Jan 5088335
#>  8 1992 Feb 2814520
#>  9 1992 Mar 2985811
#> 10 1992 Apr 3204780
#> # ℹ 194 more rows

新しい変数TotalCは、各月の全てのCostの合計です。

mutate()関数を使えば、新しい変数を作り出せます。以下では、金額の単位をドルから百万ドルに変更しています。

PBS %>%
  filter(ATC2 == "A10") %>%
  select(Month, Concession, Type, Cost) %>%
  summarise(TotalC = sum(Cost)) %>%
  mutate(Cost = TotalC/1e6)
#> # A tsibble: 204 x 3 [1M]
#>       Month  TotalC  Cost
#>       <mth>   <dbl> <dbl>
#>  1 1991 Jul 3526591  3.53
#>  2 1991 Aug 3180891  3.18
#>  3 1991 Sep 3252221  3.25
#>  4 1991 Oct 3611003  3.61
#>  5 1991 Nov 3565869  3.57
#>  6 1991 Dec 4306371  4.31
#>  7 1992 Jan 5088335  5.09
#>  8 1992 Feb 2814520  2.81
#>  9 1992 Mar 2985811  2.99
#> 10 1992 Apr 3204780  3.20
#> # ℹ 194 more rows

最後に、本章のこの先で使えるよう、出来上がったtsibbleを保存しましょう。

PBS %>%
  filter(ATC2 == "A10") %>%
  select(Month, Concession, Type, Cost) %>%
  summarise(TotalC = sum(Cost)) %>%
  mutate(Cost = TotalC / 1e6) -> a10

関数をパイプ演算子(%>%)でつなげた流れの最後に右アサインメント (->)を使っています。Rコードとしては珍しいのですが、長い一連のコマンドの最後に使うには便利です。コードの流れが上から下へ続くからです。

csvファイルの読み込みとtsibbleへの変換

本書で使うのデータはほぼ全てtsibbleオブジェクトとして保存済みです。しかし、Rにインポートする前のデータは、データベースやエクセル・ファイル、csvファイルに保存されていることがほとんどです。ですから、しばしば、tsibbleを作成する第一歩は、データを読み込み、インデックス変数とキー変数を指定することになります。

例えば、以下のような四半期データがcsvファイルにあるとします(最初の10行だけ示しています)。このデータセットはオーストラリアの囚人数(Count)に関する情報を、州別(State)、性別(Gender)、法的身分別(Legal)、先住身分別(Indigenous)(ここでATSIはアボリジニかトレス海峡諸島民を表します)に分解して提供しています。

Date State Gender Legal Indigenous Count
2005-03-01 ACT 女性 未決 ATSI 0
2005-03-01 ACT 女性 未決 Non-ATSI 2
2005-03-01 ACT 女性 既決 ATSI 0
2005-03-01 ACT 女性 既決 Non-ATSI 5
2005-03-01 ACT 男性 未決 ATSI 7
2005-03-01 ACT 男性 未決 Non-ATSI 58
2005-03-01 ACT 男性 既決 ATSI 5
2005-03-01 ACT 男性 既決 Non-ATSI 101
2005-03-01 NSW 女性 未決 ATSI 51
2005-03-01 NSW 女性 未決 Non-ATSI 131

Rに読み込み、それからどの列がインデックス変数で、どの列がキー変数かを指定するだけでtsibbleオブジェクトを作成できます。その他の列は値です。値の列数は多数になり得ますがこのケースでは1つだけ(Count)です。元のcsvファイルは日次として保存していましたが、実際には四半期なので、Date変数を四半期に変換したQuarter変数に入れ替える必要があります。

prison <- readr::read_csv("https://OTexts.com/fppjp/extrafiles/prison_population.csv")
prison <- prison %>%
  mutate(Quarter = yearquarter(Date)) %>% 
  select(-Date) %>%
  as_tsibble(key = c(State, Gender, Legal, Indigenous),
             index = Quarter)

prison
#> # 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   女性   既決  ATSI           0 2005 Q1
#>  2 ACT   女性   既決  ATSI           0 2005 Q2
#>  3 ACT   女性   既決  ATSI           0 2005 Q3
#>  4 ACT   女性   既決  ATSI           0 2005 Q4
#>  5 ACT   女性   既決  ATSI           0 2006 Q1
#>  6 ACT   女性   既決  ATSI           0 2006 Q2
#>  7 ACT   女性   既決  ATSI           0 2006 Q3
#>  8 ACT   女性   既決  ATSI           1 2006 Q4
#>  9 ACT   女性   既決  ATSI           0 2007 Q1
#> 10 ACT   女性   既決  ATSI           0 2007 Q2
#> # ℹ 3,062 more rows

このtsibbleは、8つの州、2つの性、2つの法的身分、2つの先住身分の組み合わせ、計64の時系列データを含んでいます。各時系列データは2005 Q1から2016 Q4までの、長さ48の観測値から構成されています。

tsibbleが有効であるためには、キーの組み合わせで一意に特定された各時系列データ内ではインデックスに重複があってはいけません。もしも重複があれば、tsibble()as_tsibble()関数はエラーを返します。

季節周期

グラフやモデルの作成にデータの季節周期を使うことがあります。季節周期は、繰り返す季節パターン1回分に含まれる観測値の数のことです。ほとんどの場合、季節周期はインデックス変数から自動的に探知されます。

縦にデータの観測頻度、横に期間を置いて、それぞれの季節周期を下表に示します。

データの観測頻度 1分間 1時間 1日 1週間 1年間
四半期 4
月次 12
週次 52
日次 7 365.25
時間 24 168 8766
毎分 60 1440 10080 525960
毎秒 60 3600 86400 604800 31557600

四半期、月次、週次データであれば、季節周期は1年間に対する1つだけです。1年は厳密には\(52\) 週ではなく、4年に一度のうるう年を考慮すると平均で \(365.25/7 = 52.18\) 週ですが、多くのモデルが扱える季節周期は整数だけなので、季節周期を整数で近似することは役に立ちます。

1週間に一度以上観測されるデータには、1つ以上の季節パターンがしばしばあります。例えば、日次に観測されるデータは1週間(周期\(=7\))や1年間(周期\(=365.25\))の季節パターンを有し得ます。同じように、毎分観測されるデータは1時間(周期\(=60\))、1日(周期\(=24\times60=1440\))、1週間(周期\(=24\times60\times7=10080\))さらに1年間(周期\(=24\times60\times365.25=525960\))の季節性を有し得ます。

さらに複雑で(普段は見ることのない)季節パターンは、lubridateパッケージのperiod()関数を使えば指定できます。