※この記事はDo more with dates and times in R with lubridate 1.3.0を翻訳したものですが、とても雑にやったので原文と大分違っていると思います。
日付と時刻のパース
Rで日付や時刻を含むデータを扱う方法は大分ややこしいが、{lubridate}
パッケージを使えばもっと単純にやれる。
例えば文字列をパースする場合、y
, m
, d
を文字列に含まれる年月日の順序に合わせて並び替えた関数を使えばよい。例を見たほうがわかりやすいだろう。
library(lubridate)
> ymd("20110604")
[1] "2011-06-04"
> mdy("06-04-2011")
[1] "2011-06-04"
> dmy("4/6/2011")
[1] "2011-06-04"
これらの関数は様々な形式、セパレータを扱えるので、パースの手順を単純にできる。
もし文字列に時刻の情報もあるのなら、関数名に続けて_h
, _hm
, _hms
を追加することで、時間、時間と分、時間と分と秒を読み込むことができる。おそらく、ymd_hms
がもっとも一般的な形式だろう。もしタイムゾーンを指定したければtz=
引数を使う。
> ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
[1] "2011-06-04 12:00:00 NZST"
情報の代入と抽出
second
minute
hour
day
wday
yday
week
month
year
tz
、これらの関数を使って日時から特定の情報を抽出できる。また、これらの関数を使って代入もできる。
> arrive <- ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
> second(arrive)
[1] 0
> second(arrive) <- 25
> second(arrive)
[1] 25
wday
とmonth
はlabel=
引数にTRUE
を指定することで、出力を数値でなく曜日名や月名に変更できる。
> wday(arrive)
[1] 7
> wday(arrive, label = TRUE)
[1] Sat
Levels: Sun < Mon < Tues < Wed < Thurs < Fri < Sat
> month(arrive, label = TRUE)
[1] Jun
12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < ... < Dec
タイムゾーン
with_tz
は特定のタイムゾーンにおける時刻を表示する。例えばオークランドにで2011/7/1 9:00のとき、シカゴでは何時か知りたければ次のようにする。
> meeting <- ymd_hms("2011-07-01 09:00:00", tz = "Pacific/Auckland")
> meeting
[1] "2011-07-01 09:00:00 NZST"
> with_tz(meeting, "America/Chicago")
[1] "2011-06-30 16:00:00 CDT"
日時はそのままで、タイムゾーンのみを変更するにはforce_tz
関数を使う。
> force_tz(meeting, "America/Chicago")
[1] "2011-07-01 09:00:00 CDT"
インターバル
{lubridate}
はインターバルを表現するための専用のクラスを備えている。インターバルクラスのオブジェクトはinterval
関数または二項演算子%--%
を使って作成できる。
> arrive <- ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
> leave <- ymd_hms("2011-08-10 14:00:00", tz = "Pacific/Auckland")
> auckland <- interval(arrive, leave)
> auckland
[1] 2011-06-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
> arrive %--% leave
[1] 2011-06-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
int_overlaps
関数は2つのインターバルに時期的な重なりがあるかどうかを確認する。
> jsm <- interval(ymd(20110720, tz="Pacific/Auckland"), ymd(20110831, tz="Pacific/Auckland"))
> jsm
[1] 2011-07-20 NZST--2011-08-31 NZST
> int_overlaps(auckland, jsm)
[1] TRUE
インターバルオブジェクトに対しては、int_start
int_end
などの専用関数の他、setdiff
union
intersect
のような集合演算も適用できる。
日付の計算
上述のインターバルは2つの特定の日時の間に定義されるものだった。{lubridate}
はこれに加えて、一般的な時間間隔に関するクラスである、durations
とperiods
を持っている。periods
を作成するには、時間の単位を複数形にした関数を使う。
> minutes(2)
[1] "2M 0S"
durations
を作るには、periods
を作成する関数の頭にd
をつける。
> dminutes(2)
[1] "120s (~2 minutes)"
durations
クラスは数学的に正しい結果を返す。すなわち、durations
における1年は常に365日である。一方で、periods
は暦の上で受ける変動と同じ変動を受ける。この違いはうるう年の1月1日に「1年」を足す演算を見るとよく分かる。
> ymd(20120101) + years(1)
[1] "2013-01-01"
> ymd(20120101) + dyears(1)
[1] "2012-12-31"
durations
における1年は365日であるが、periods
における1年はうるう年では366日になる。
インターバルと組み合わせて期間の計算などができる。
> auckland / ddays(1)
[1] 67.08333
商と余りも計算できる。
> auckland %/% months(1)
注: シグネチャー ‘Timespan#Timespan’ を持つメソッドが、関数 ‘%/%’ に対して選ばれました,
対象シグネチャー ‘Interval#Period’ です。
"Interval#ANY", "ANY#Period" もまた有効かもしれません
[1] 2
余りの場合は新しいインターバルが返ってくるので、目的に応じてas.period
やas.duration
で変換すると良いだろう。
> auckland %% months(1)
[1] 2011-08-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
> as.period(auckland %% months(1))
[1] "6d 2H 0M 0S"
> as.duration(auckland %% months(1))
[1] "525600s (~6.08 days)"
月の長さが変わる場合の算術演算
月や年の長さはしばしば変化するので、算術演算が直感的でなくなる場合がある。例えば1月31日に1ヶ月を足す場合を考えてみよう。パッと思いつくだけでも3通りの答があり得る。
- 2月31日 (単純に月に1を足す。こんな日は存在しないが。)
- 3月4日 (1月31日の31日後として計算。)
- 2月28日 (翌月末をうるう年ではないと仮定して計算。)
a + b - b = a
というルールに従えるのは1のみであるが、しかし2月31日は日付として正しくない。{lubridate}
では演算の結果として正しくない日付になる場合、NA
を返すことになっている。これはversion 1.3.0からの変更なので古いバージョンの{lubridate}
を使っていた場合は注意。
> jan31 <- ymd("20130131")
> jan31 + months(0:11)
[1] "2013-01-31" NA "2013-03-31" NA "2013-05-31"
[6] NA "2013-07-31" "2013-08-31" NA "2013-10-31"
[11] NA "2013-12-31"
もし2の結果(31日を足した結果を表示)が欲しければ、floor_date
で月初の日付に変換した後にdays(31)
を足すという手順を取る。
> floor_date(jan31, "month") + months(0:11) + days(31)
[1] "2013-02-01" "2013-03-04" "2013-04-01" "2013-05-02" "2013-06-01"
[6] "2013-07-02" "2013-08-01" "2013-09-01" "2013-10-02" "2013-11-01"
[11] "2013-12-02" "2014-01-01"
3の結果がほしければ、二項演算子%m+%
%m-%
を使うと、月末の日付を適当に調整してくれる。
> jan31 %m+% months(0:11)
[1] "2013-01-31" "2013-02-28" "2013-03-31" "2013-04-30" "2013-05-31"
[6] "2013-06-30" "2013-07-31" "2013-08-31" "2013-09-30" "2013-10-31"
[11] "2013-11-30" "2013-12-31"