こんにちは,shun(@datasciencemore)です!
ついに..ついに今回の時間処理でこのR前処理講座が終了します!!!
長かった...
時間処理は最後を飾るにふさわしい,まさにラスボスといってもいいでしょう.
少しめんどくさいところもありますが,めげずに食らいついていきましょう!
目次
0.{lubridate}ってなに??
時間処理は簡単そうに見えるけど,実際に処理しようとすると色々考えることが多くて難しいんだ...
時間処理は,一見単純のようにみえるのですが,実は奥が深く,とても考えることが多いです.
ややこしい例を挙げると
- 通常は1年は365日だが,うるう年は1年は366日
- 通常は1日は24時間だが,サマータイム時は25時間
- 時差の影響により,国々の時刻は異なる.
などなど,めんどくさいことがたくさんでてきます.
時間処理を完全に理解することは難しいですが,この記事ではデータ分析でよく使用する時間処理についてに焦点を当てて説明したいと思います.
さて,baseRでは,時刻を表す型として,POSIXc型,POSIXlt型がありますが,これらの使い方は覚えなくて大丈夫です笑
これらの使い方は難しすぎるので僕は挫折しました...笑
後述のlubridateですべて代用できるので安心してください!
これらの代わりに時間処理を扱うパッケージ,{lubridate}について紹介します.
これを使いこなせれば,時間処理については問題ないです!
lubridateを使用すると,時間型を以下の2種類に分類できます.
- date型:日付を表す.
- datetime型:日付と時刻を表す.
実は,time型というのもあるのですがほとんど使用せず,上で挙げた2つの型で代用できるので割愛いたします.
ちなみにtime型,僕も実務で使ったことは1度もありません.
それでは,lubridateの代表的な使い方を紹介していきます.
1.文字列から時間型を作成
date型,datetime型を作成するには,y, m, d, h, m, sの6文字を文字列の要素に対応するように指定すればlubridateがいい感じに変えてくれます.
例えば,"2020/12/17"という文字列をdate型にする場合,ymd("2020/12/17")とするだけです.
同じ色の対応を見比べてみてください.
lubridate様,さすがっす.
y, m, d, h, m, sの6文字は,それぞれ次の意味です.
y:year(年)
m:month(月)
d:day(日)
h:hour(時)
m:minute(分)
s:second(秒)
それでは,具体的なコードをいろいろ見ていきましょう.
1 2 3 4 5 |
# 文字列をdate型に変換 これらはすべて同じ ymd("2020/12/17") ymd("2020-12-17") mdy("12-17-2020") dmy("17-Dec-2020") |
1 |
[1] "2020-12-17" |
1 2 3 4 5 |
# 文字列をdatetime型に変換 これらはすべて同じ ymd_hms("2020/12/17 10:04:59") ymd_hms("2020-12-17 10:04:59") mdy_hms("12-17-2020 10/04/59") dmy_hms("17-Dec-2020 10-04-59") |
1 |
[1] "2020-12-17 10:04:59 UTC" |
lubridateを使うと,「-」とか「/」を自動的に判断して,date型もdatetime型もいい感じに変換してくれます.
2.要素から時間型を作成
要素(年,月,日など単一の値)から時間型を作成することもできます. date型ならmake_date,datetime型ならmake_datetimeを使用します.
1 2 |
# 要素からdate型に変換 make_date(2020, 12, 17) |
1 |
[1] "2020-12-17" |
1 2 |
# 要素からdatetime型に変換 make_datetime(2020, 12, 17, 10, 4, 59) |
1 |
[1] "2020-12-17 10:04:59 UTC" |
これは,データフレームに列に年や月の情報が個別にあるとき,そこから時間型を作成するときに便利です.
例えば以下のようなデータフレームがあるとして,そこからdatetime型をmake_datetimeを使用して作成してみましょう.
1 2 3 4 5 6 7 8 |
# データフレームの定義 df = tibble( year = 2020, month = 12, day = 1:31, hour = 12 ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
> df # A tibble: 31 x 4 year month day hour <dbl> <dbl> <int> <dbl> 1 2020 12 1 12 2 2020 12 2 12 3 2020 12 3 12 4 2020 12 4 12 5 2020 12 5 12 6 2020 12 6 12 7 2020 12 7 12 8 2020 12 8 12 9 2020 12 9 12 10 2020 12 10 12 |
1 2 3 4 5 |
# make_datetimeを使用して,datetime型を作成 df %>% mutate( ymdhms = make_datetime(year, month, day, hour) ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# A tibble: 31 x 5 year month day hour ymdhms <dbl> <dbl> <int> <dbl> <dttm> 1 2020 12 1 12 2020-12-01 12:00:00 2 2020 12 2 12 2020-12-02 12:00:00 3 2020 12 3 12 2020-12-03 12:00:00 4 2020 12 4 12 2020-12-04 12:00:00 5 2020 12 5 12 2020-12-05 12:00:00 6 2020 12 6 12 2020-12-06 12:00:00 7 2020 12 7 12 2020-12-07 12:00:00 8 2020 12 8 12 2020-12-08 12:00:00 9 2020 12 9 12 2020-12-09 12:00:00 10 2020 12 10 12 2020-12-10 12:00:00 # … with 21 more rows |
3.時間型の変換
as_dateを使用するとdatetime型からdate型へ,as_datetimeを使用するとdate型からdatetime型へ変換できます.
1 2 |
# date型の変数定義 date = ymd("20200513") |
1 2 |
> date [1] "2020-05-13" |
1 2 |
# date型からdatetime型に変換 datetime = as_datetime(date) |
1 2 |
> datetime [1] "2020-05-13 UTC" |
1 2 |
# datetime型からdate型に変換 as_date(datetime) |
1 |
[1] "2020-05-13" |
4.要素の取得
日付時刻の各要素を取得する便利関数がいくつかあります.
その関数を以下のように時間型に適用すると,時間型の一部分を抽出できます.
全部だと多いので一例を紹介しますね.
1 2 |
# datetime型の変数定義 datetime = ymd_hms("2017/5/17 13:48:27") |
1 2 3 4 5 6 7 8 |
# date:年月日を取得 date(datetime) # day:日を取得 day(datetime) # minute:秒を取得 minute(datetime) |
1 2 3 4 5 6 7 8 9 10 11 |
> # date:年月日を取得 > date(datetime) [1] "2017-05-17" > > # day:日を取得 > day(datetime) [1] 17 > > # minute:秒を取得 > minute(datetime) [1] 48 |
また,ydayとwdayという関数は,それぞれ年初から何日目か,今週何日目かを教えてくれます.
1 2 3 4 5 |
# yday:年初から何日目か datetime %>% yday() # wday:今週何日目か datetime %>% wday() |
1 2 3 4 5 6 7 |
> # yday:年初から何日目か > datetime %>% yday() [1] 137 > > # wday:今週何日目か > datetime %>% wday() [1] 4 |
monthとwdayは,label = TRUEと設定すると,該当する月,曜日のfactorが取得できます.
1 2 3 |
# month(label = TRUE):何月かをfactorで取得 datetime %>% month(label = TRUE) |
1 2 |
[1] May Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < Oct < Nov < Dec |
1 2 3 |
# wday(label = TRUE):何曜日かをfactorで取得 datetime %>% wday(label = TRUE) |
1 2 |
[1] Wed Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat |
いやー,親切すぎるぜlubridate...
ここまで親切だともうbaseRに戻れませんね笑
5.時間型の丸め処理
以下の3種類の関数で,時間型を丸めることができます.
- floor_date:切り捨て
- round_date:四捨五入
- ceiling_date:切り上げ
1 2 |
# datetime型の変数定義 datetime = ymd_hms("2017/5/17 13:48:27") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# minuteが残るように切り捨て floor_date(datetime, "minute") # minuteが残るように四捨五入 round_date(datetime, "minute") # minuteが残るように切り上げ ceiling_date(datetime, "minute") # hourが残るように切り捨て floor_date(datetime, "hour") # hourが残るように四捨五入 round_date(datetime, "hour") # hourが残るように切り上げ ceiling_date(datetime, "hour") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
> # minuteが残るように切り捨て > floor_date(datetime, "minute") [1] "2017-05-17 13:48:00 UTC" > > # minuteが残るように四捨五入 > round_date(datetime, "minute") [1] "2017-05-17 13:48:00 UTC" > > # minuteが残るように切り上げ > ceiling_date(datetime, "minute") [1] "2017-05-17 13:49:00 UTC" > > # hourが残るように切り捨て > floor_date(datetime, "hour") [1] "2017-05-17 13:00:00 UTC" > > # hourが残るように四捨五入 > round_date(datetime, "hour") [1] "2017-05-17 14:00:00 UTC" > > # hourが残るように切り上げ > ceiling_date(datetime, "hour") [1] "2017-05-17 14:00:00 UTC" |
6.時間型の計算
ここでは,時間型の計算についてやっていきます.
例えば,自分が生まれてからどれくらい時間がたったのかを知りたいとしましょう.
そのようなときに,(現時点 - 生年月日)で引き算すればできそうですね.
このように時間型を使用して,いろいろな計算ができると便利ですね.
この時間型の計算をするうえで大事になってくるのが以下の3つのクラスです.
- duration
- period
- interval
それぞれについてじっくり見ていきましょう.
6.1 duration
以下の関数で指定した数値durationが取得できます.
durationは,秒で表しています.
- dseconds
- dminutes
- dhours
- ddays
- dweeks
- dyears
一例をあげますね.
1 2 3 4 5 |
# 128分のduration dminutes(128) # 128年のduration dyears(128) |
1 2 3 4 5 6 7 |
> # 128分のduration > dminutes(128) [1] "7680s (~2.13 hours)" > > # 128年のduration > dyears(128) [1] "4039372800s (~128 years)" |
6.2 period
以下の関数で指定したperiodが取得できます.
- seconds
- minutes
- hours
- days
- weeks
- years
こちらも一例を示します.
1 2 3 4 5 |
# 128分のperiod minutes(128) # 128年のperiod years(128) |
1 2 3 4 5 6 7 |
> # 128分のperiod > minutes(128) [1] "128M 0S" > > # 128年のperiod > years(128) [1] "128y 0m 0d 0H 0M 0S" |
durationは常に秒で表されるのに対して,periodは人間の時間間隔を表しているんだ.
例えば,2016年1月1日の1年後っていったら私たち人間の感覚だと2017年1月1日だよね?
ところが,2016年1月1日の31557600秒(1年を秒に換算した値)後は,2016年の12月31日になるんだ.
これは,2016年はうるう年で,年間366日あるから結果が異なるんだね.
いやー,わかりづらい笑
イメージにすると少しはわかりやすくなるでしょうか?
periodを使用するときは,起点となる時刻によって,同じ関数を使用しても値が異なってくるんですね.
この図の上の例の場合,2016年はうるう年なので1年=366日です.
2016年1月1日にyears(1)を足すと,2016年1月1日の1年後と人間の感覚で解釈されるため,years(1) = 366日となるんですね.
一方,durationであるdyears(1)は,起点が何であろうが関係ありません.常に31557600秒後です.
この違いによって,最終的な結果が1日異なるのです.
この図の下の例も参考にみてみてください.
2017年は普通の年なので,1年=365日です.
よって,years(1)もdyears(1)も同じ365日を表すのです.
またまたややこしくことで大変恐縮なのですが,実は正確にいうと365日は,3600秒×24時間×365日 = 31536000秒なので,dyears(1)と微妙に違います.
これは天文学で1年 = 365.25日と定義しているため,dyears(1)も365.25日の秒数 = 31557600秒としているみたいです.(Wikipedia情報)
なので,上の例は完璧に正確ではないですが,イメージを理解するうえで有用なので,載せてあります.
ややこしすぎやろ...笑
lubridateのバージョンによっても変わってきそうなので,そこまで神経質にならないで,なんとなくのイメージをつかめていただければOKです.
Rで確認してみましょう.
1 2 3 4 5 |
# うるう年である2016年の1年後 ymd("2016/1/1") + years(1) # うるう年である2016年の31557600秒後 ymd("2016/1/1") + dyears(1) |
1 2 3 4 5 6 7 |
> # うるう年である2016年の1年後 > ymd("2016/1/1") + years(1) [1] "2017-01-01" > > # うるう年である2016年の31557600秒後 > ymd("2016/1/1") + dyears(1) [1] "2016-12-31 06:00:00 UTC" |
1 2 3 4 5 |
# 普通の年である2017年の1年後 ymd("2017/1/1") + years(1) # 普通の年である2017年の31557600秒後 ymd("2017/1/1") + dyears(1) |
1 2 3 4 5 6 7 |
> # 普通の年である2017年の1年後 > ymd("2017/1/1") + years(1) [1] "2018-01-01" > > # 普通の年である2017年の31557600秒後 > ymd("2017/1/1") + dyears(1) [1] "2018-01-01 06:00:00 UTC" |
こんな感じです.
durationを足したら時刻が出てきちゃった理由は,上述の注意書きのとおりです.
あんまり深く考えないようにしましょう笑
6.3 interval
intervalは,開始時点と終了時点を指定し,その期間をdurationで表したものになります
durationなので,単位は秒ですね. 普通のdurationとの違いは,intervalは開始時点の情報があるということです.
intervalで使用する主な関数は以下のとおりです.
- %within% : intervalに含まれているか.
- int_start : intervalの開始時点
- int_end: intervalの終了時点
- int_overlap : 2つのintervalが重なっているか.
- int_length : intervalの秒数
- int_shift : intervalを指定分,ずらす.
intervalを以下のように定義します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# うるう年である2016年の1年後 ymd("2016/1/1") + years(1) # うるう年である2016年の31557600秒後 ymd("2016/1/1") + dyears(1) # 普通の年である2017年の1年後 ymd("2017/1/1") + years(1) # 普通の年である2017年の31557600秒後 ymd("2017/1/1") + dyears(1) # interval:2017年1月1日~2017年7月1日 inter1 = interval(ymd("2017/1/1"), ymd("2017/7/1")) # interval:2017年4月1日~2018年7月1日 inter2 = interval(ymd("2017/4/1"), ymd("2018/7/1")) # interval:2018年10月1日~2019年12月1日 inter3 = interval(ymd("2018/10/1"), ymd("2019/12/1")) # interval:2019年1月1日~2019年4月1日 inter4 = interval(ymd("2019/1/1"), ymd("2019/4/1")) # date:2018年9月1日 date1 = ymd("2018/9/1") # date:2019年10月1日 date2 = ymd("2019/10/1") |
この例をもとにRで確認していきましょう.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# inter1の開始時点 int_start(inter1) # inter2の終了時点 int_end(inter2) # inter1はinter2に含まれているか inter1 %within% inter2 # inter4はinter3に含まれているか inter4 %within% inter3 # date1はinter2に含まれるか date1 %within% inter2 # date2はinter3に含まれるか date2 %within% inter3 # inter1とinter2に重なりはあるか int_overlaps(inter1, inter2) # inter2とinter3に重なりはあるか int_overlaps(inter2, inter3) # inter4の期間(開始時点から終了時点までの秒数) int_length(inter4) # inter4を1年ずらす int_shift(inter4, years(1)) # inter1は何日か inter1 / days(1) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
> # inter1の開始時点 > int_start(inter1) [1] "2017-01-01 UTC" > > # inter2の終了時点 > int_end(inter2) [1] "2018-07-01 UTC" > > # inter1はinter2に含まれているか > inter1 %within% inter2 [1] FALSE > > # inter4はinter3に含まれているか > inter4 %within% inter3 [1] TRUE > > # date1はinter2に含まれるか > date1 %within% inter2 [1] FALSE > > # date2はinter3に含まれるか > date2 %within% inter3 [1] TRUE > > # inter1とinter2に重なりはあるか > int_overlaps(inter1, inter2) [1] TRUE > > # inter2とinter3に重なりはあるか > int_overlaps(inter2, inter3) [1] FALSE > > # inter4の期間(開始時点から終了時点までの秒数) > int_length(inter4) [1] 7776000 > > # inter4を1年ずらす > int_shift(inter4, years(1)) [1] 2020-01-01 UTC--2020-04-01 UTC > > # inter1は何日か > inter1 / days(1) [1] 181 |
上の図とコードを見比べて,何をしているのか確認してみてくださいね.
まとめ
今回は時間処理について以下のことをやりました.
- 文字列から時間型を作成
- 要素から時間型を作成
- 時間型の変換
- 要素の取得
- 時間型の丸め処理
- 時間型の計算
いやー,お疲れさまでした!
かなり難しかったと思います.
これを使いこなせれば,時間処理についてはほぼ完ぺきです.
最初は難しいと思いますが,1つずつ理解していきましょう.
これでR前処理講座は今度こそ終了です!!
皆様お疲れさまでした!!!
これがすべて理解できたら前処理マスターを名乗っていいと思います.
R前処理講座は,前処理の8割程度をカバーしていると考えています.
残りの2割は適宜ググって解決していきましょう.
別に前処理に限りませんが,完璧を目指すことはよくないですよ!!
それじゃ,お疲れさまでした!!!
参考
- https://www.amazon.co.jp/R%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9-Hadley-Wickham/dp/487311814X
- https://www.medi-08-data-06.work/entry/how_to_use_lubridate1902
- https://ja.wikipedia.org/wiki/%E5%B9%B4#:~:text=%E5%A4%A9%E6%96%87%E5%AD%A6%E3%81%A7%E3%81%AF%E8%A8%88%E9%87%8F%E3%81%AE%E5%8D%98%E4%BD%8D,%E3%81%A8%E5%AE%9A%E7%BE%A9%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82