データ分析関連メモ(メモです)

仲秋の候、涼やかな秋風の下、ご一同様にはその後お健やかにお過ごしのことと存じます。

日付の途中を補完するtidyr::complete()関数

時系列データでよく使うので備忘録。

まずtidyr::complete()関数の使い方から。 組み合わせの欠損を埋めてくれる関数。
Complete a data frame with missing combinations of data — complete • tidyr
メモ:時系列とか連番のデータを補完するときはtidyrのcomplete()とfull_seq()が便利そう - Technically, technophobic.


サンプルデータを作成する。idが”111”と”222”のデータがあるが、それぞれyear_monthの途中が欠けている。
id“111”は3,5月、”222”は2,3月のレコードが無い。

df_1 <- tibble::tribble(
  ~id, ~year_month, ~cnt,
  "111", "2022-01-01", 1,
  "111", "2022-02-01", 2,
  "111", "2022-04-01", 4,
  "222", "2022-01-01", 1,
  "222", "2022-04-01", 4,
  "222", "2022-05-01", 5,
) |>
  dplyr::mutate(year_month = as.Date(year_month))


seq.Date()関数とtidyr::complete()関数を組み合わせると、補完することができる。

Populating Missing Dates with Complete and Fill Functions in R and Exploratory | by Kan Nishida | learn data science

df_1 |>
  tidyr::complete(id,
                  year_month = seq.Date(as.Date(min(year_month, na.rm = TRUE)), 
                                        as.Date(max(year_month, na.rm = TRUE)), 
                                        by = "months"))

# A tibble: 10 × 3
# id    year_month   cnt
# <chr> <date>     <dbl>
# 1 111   2022-01-01     1
# 2 111   2022-02-01     2
# 3 111   2022-03-01    NA
# 4 111   2022-04-01     4
# 5 111   2022-05-01    NA
# 6 222   2022-01-01     1
# 7 222   2022-02-01    NA
# 8 222   2022-03-01    NA
# 9 222   2022-04-01     4
# 10 222   2022-05-01     5



ここから気になって調べたところ。

seq.Date()関数の始まりと終わりの日付に、元のデータに存在しないものを指定しても、指定した範囲で補完してくれる。
例えば元のサンプルデータには2022年1月から2022年5月までしか存在しないが、seq.Date()関数に2021年12月から2022年6月まで指定すると次のように返ってくる。

df_1 |>
  tidyr::complete(id,
                  year_month = seq.Date(as.Date("2021-12-01"), 
                                        as.Date("2022-06-01"), 
                                        by = "months"))

# A tibble: 14 × 3
# id    year_month   cnt
# <chr> <date>     <dbl>
# 1 111   2021-12-01    NA
# 2 111   2022-01-01     1
# 3 111   2022-02-01     2
# 4 111   2022-03-01    NA
# 5 111   2022-04-01     4
# 6 111   2022-05-01    NA
# 7 111   2022-06-01    NA
# 8 222   2021-12-01    NA
# 9 222   2022-01-01     1
# 10 222   2022-02-01    NA
# 11 222   2022-03-01    NA
# 12 222   2022-04-01     4
# 13 222   2022-05-01     5
# 14 222   2022-06-01    NA


完全に範囲外の1900年の日付を指定すると、その範囲だけ組み合わせが発生する。
元々存在していた2022年のレコードは変わらず。

df_1 |>
  tidyr::complete(id,
                  year_month = seq.Date(as.Date("1900-01-01"), 
                                        as.Date("1900-02-01"), 
                                        by = "months"))

# A tibble: 10 × 3
# id    year_month   cnt
# <chr> <date>     <dbl>
# 1 111   1900-01-01    NA
# 2 111   1900-02-01    NA
# 3 222   1900-01-01    NA
# 4 222   1900-02-01    NA
# 5 111   2022-01-01     1
# 6 111   2022-02-01     2
# 7 111   2022-04-01     4
# 8 222   2022-01-01     1
# 9 222   2022-04-01     4
# 10 222   2022-05-01     5


次に、idとは別に、idに対して紐づいている変数を持っている場合。
サンプルデータとして、各idにnameがあるデータを作成する。

df_2 <- tibble::tribble(
  ~id, ~name, ~year_month, ~cnt,
  "111", "xxx", "2022-01-01", 1,
  "111", "xxx", "2022-02-01", 2,
  "111", "xxx", "2022-04-01", 4,
  "222", "yyy", "2022-01-01", 1,
  "222", "yyy" ,"2022-04-01", 4,
  "222", "yyy", "2022-05-01", 5,
) |>
  dplyr::mutate(year_month = as.Date(year_month))


これをそのままtidyr::complete()に放り込むと、idとnameを含めて、すべての組み合わせが作成される。

df_2 |>
  tidyr::complete(id, name,
                  year_month = seq.Date(as.Date(min(year_month, na.rm = TRUE)), 
                                        as.Date(max(year_month, na.rm = TRUE)), 
                                        by = "months"))

# A tibble: 20 × 4
# id    name  year_month   cnt
# <chr> <chr> <date>     <dbl>
# 1 111   xxx   2022-01-01     1
# 2 111   xxx   2022-02-01     2
# 3 111   xxx   2022-03-01    NA
# 4 111   xxx   2022-04-01     4
# 5 111   xxx   2022-05-01    NA
# 6 111   yyy   2022-01-01    NA
# 7 111   yyy   2022-02-01    NA
# 8 111   yyy   2022-03-01    NA
# 9 111   yyy   2022-04-01    NA
# 10 111   yyy   2022-05-01    NA
# 11 222   xxx   2022-01-01    NA
# 12 222   xxx   2022-02-01    NA
# 13 222   xxx   2022-03-01    NA
# 14 222   xxx   2022-04-01    NA
# 15 222   xxx   2022-05-01    NA
# 16 222   yyy   2022-01-01     1
# 17 222   yyy   2022-02-01    NA
# 18 222   yyy   2022-03-01    NA
# 19 222   yyy   2022-04-01     4
# 20 222   yyy   2022-05-01     5


idとnameの組み合わせは動かしたくない場合は、tidyr::nesting()関数で囲って渡す。

df_2 |>
  tidyr::complete(tidyr::nesting(id, name),
                  year_month = seq.Date(as.Date(min(year_month, na.rm = TRUE)), 
                                        as.Date(max(year_month, na.rm = TRUE)), 
                                        by = "months"))

# A tibble: 10 × 4
# id    name  year_month   cnt
# <chr> <chr> <date>     <dbl>
# 1 111   xxx   2022-01-01     1
# 2 111   xxx   2022-02-01     2
# 3 111   xxx   2022-03-01    NA
# 4 111   xxx   2022-04-01     4
# 5 111   xxx   2022-05-01    NA
# 6 222   yyy   2022-01-01     1
# 7 222   yyy   2022-02-01    NA
# 8 222   yyy   2022-03-01    NA
# 9 222   yyy   2022-04-01     4
# 10 222   yyy   2022-05-01     5