ねこすたっと

ねこの気持ちと統計について悩む筆者の備忘録的ページ。

日付・時間の操作(2):期間を計算する(lubridateパッケージ)[R]

lubridateパッケージ*1を使うと日付・時刻型データ(単純に時間型データと呼ぶことにします)の扱いがとても楽になる。前編は下のリンクを参照。

necostat.hatenablog.jp

lubridateパッケージはtidyverseのコアパッケージには含まれていないので、別に読み込みする必要あり。

library(tidyverse)
library(lubridate)

時間型データを丸める(切り上げ・切り捨て・四捨五入)

分単位まで取られているデータでも、解析のときは時間単位でしか使用しなかったり、など端数を処理したいときは以下の関数を使う。 どの単位まで残したいかを引数unitで指定する。

  • floor_date( ):切り捨て
  • ceiling_date( ):切り上げ
  • round_date( ):四捨五入

使い方は下のとおり。

> round_date(ymd_hms("2022-08-08 5:15:29"), unit = "minute")
[1] "2022-08-08 05:15:00 UTC"

四捨五入を選ぶと、29秒は切り捨てられた。30秒は、

> round_date(ymd_hms("2022-08-08 5:15:30"), unit = "minute")
[1] "2022-08-08 05:16:00 UTC"

となり、切り上げになる。

期間を計算する

lubridateでは3種類の「期間」を扱うことができる。日本語に訳してしまうと何がなんだかわからなくなりそうなのでそのまま表記。

  • duration: 時間の長さを秒単位に直して計算する
  • period:人間の感覚の年月日で計算する
  • interval:2つの時点の間隔を秒単位で計算する

"interval"は他の2つと別であることがすぐに分かるが、"duration"と"period"の違いはなんだろうか。 後述のように、うるう年を考えれば分かりやすい。

duration:時間の長さを秒単位に直して計算する

dyears( ), dmonths( )などに数値を渡すと、その期間が何秒かを計算してくれる。

> dyears(1)
[1] "31557600s (~1 years)"
> dmonths(1)
[1] "2629800s (~4.35 weeks)"
> dweeks(1)
[1] "604800s (~1 weeks)"
> ddays(1)
[1] "86400s (~1 days)"
> dhours(1)
[1] "3600s (~1 hours)"
> dminutes(1)
[1] "60s (~1 minutes)"
> dseconds(1)
[1] "1s"

「1年 = 365日 × 24時間 × 60分 × 60秒 = 31536000秒」でないことに注意。うるう年があるので「1年 = 365.25日」になっている。

> dyears(1)/ddays(1) #1年=365.25日
[1] 365.25

同様に1か月も30.4375日になっている。

> dmonths(1)/ddays(1) #1か月=30.4375
[1] 30.4375

ある日付・時刻から任意の期間が経った後の日付を求めたいときは、日付・時刻にdyears( )などを足せばよい。 例えば、2020年1月1日から1年後の日付は下のコードで計算できる。

> ymd("2020-01-01") + dyears(1)
[1] "2020-12-31 06:00:00 UTC"

2020年はうるう年なので、1年後が2020年12月31日になっている。

また下のように、1か月後を計算しても2月1日にはなっていない。

> ymd("2020-01-01") + dmonths(1)
[1] "2020-01-31 10:30:00 UTC"

period:人間の感覚の年月日で計算する

今回は"duration"と違って、「年・月・日・時間・分・秒」といった単位にもとづいて計算してくれる。

> years(1)
[1] "1y 0m 0d 0H 0M 0S"
> months(1)
[1] "1m 0d 0H 0M 0S"
> weeks(1)
[1] "7d 0H 0M 0S"
> days(1)
[1] "1d 0H 0M 0S"
> hours(1)
[1] "1H 0M 0S"
> minutes(1)
[1] "1M 0S"
> seconds(1)
[1] "1S"

例えば、2020年1月1日の1年後は、

> ymd("2020-01-01") + years(1)
[1] "2021-01-01"

のように2021年1月1日と計算される。

また、2020年1月1日の1か月後は、

> ymd("2020-01-01") + months(1)
[1] "2020-02-01"

のように、2月1日と計算される。

interval:2つの時点の間隔を秒単位で計算する

次のようにinterval( )において、開始時点をstart、終了時点をendで指定すれば、期間として認識される。

interval1 <- interval(start = "1955-11-12", end = "1985-10-26")
> interval1
[1] 1955-11-12 UTC--1985-10-26 UTC

下のように、intervalの代わりに、%--%を使ってもよい。

interval2 <- ymd("1885-9-2") %--% ymd("2015-10-21")
> interval2
[1] 1885-09-02 UTC--2015-10-21 UTC

"interval"に対しての計算は以下のようなものがある。

  • int_length( ):ある期間の長さ(秒単位)を返す。
  • int_shift( ):ある期間全体をそのままの長さでずらしたいときに使う
  • int_start( ):期間の開始点を抽出する。
  • int_end( ):期間の終了点を抽出する。
  • %within%:ある期間が他の期間に含まれているかどうかの真偽を返す。
  • int_overps( ):2つの期間に重複期間があるかどうかの真偽を返す。

int_length( )を使ってintervalの長さを求める

intervalオブジェクトをそのまま引数にすれば良い。秒単位で計算される。

> int_length(interval1)
[1] 945302400

日単位で表示したければ、ddays(1)またはday(1)で割る(durationでもperiodでもOK)。int_length( )/dday(1)のように、秒単位の数値を割ろうとするとエラーになる。

> interval1/ddays(1)
[1] 10941

> interval1/days(1)
[1] 10941

int_shift( )を使ってintervalを前後にずらす

intervalオブジェクトとずらしたい長さを引数にしていすればよい。"duration"(dyears( )など)と"period"(years( ))によって違いが生じることは、これまでの説明どおり。

> int_shift(interval1, dyears(1))
[1] 1956-11-11 06:00:00 UTC--1986-10-26 06:00:00 UTC

> int_shift(interval1, years(1))
[1] 1956-11-12 UTC--1986-10-26 UTC

%within%を使って2つの期間の包含関係・重複関係を確認する

interval1がinterval2に含まれているかを確認するためには、次のコードを使う。interval1とinterval2の順序に注意。

> interval1 %within% interval2
[1] TRUE

interval1とinterval2に重複期間があるかを確認するためには次のコードを使う。この場合は順序不問。

> int_overlaps(interval1,interval2)
[1] TRUE

おわりに

  • 基本的にはduration( )とかinterval( )を使って、秒単位をもとに計算すればいいと思う。
  • NHK Eテレ「ねこのうた」に登場した猫の「いつものご飯」が、我が家の猫の「スペシャルご飯」以上だった。

参考資料

  • UTC, GMT, JSTについて参考にさせていただきました。

www.quicktranslate.com

  • Rを使った解析前のデータ処理について系統立てて解説してくださっています。

datasciencemore.com

  • こちらもわかりやすく解説してくださっています。

www.medi-08-data-06.work

*1:lubricate(円滑にする)より?