Rの日付関数群のざっとした説明
日時の入ったデータは多いわけで、日付処理に手馴れておくのに越したことはありません。勘所になる多数ある日時を格納するクラスの相互変換を確認しておきましょう。
- 1 POSIXct → POSIXlt
- 2 POSIXct or POSIXlt → numeric
- 3 numeric → POSIXct, POSIXlt
- 4 C言語のmktime風にPOSIXctに変換
- 5 POSIXct or POSIXlt → Date型
- 6 Date型 → POSIXct or POSIXlt
- 7 POSIXct or POSIXlt → 文字列
- 8 文字列 → POSIXct
- 9 日時の桁上がり桁下がり処理
- 10 roundで丸め処理ができる
- 11 シーケンス生成もできる
- 12 応用例① 来年まであと何日?
- 13 応用例② {年/月}の形式を月末日でPOSIXltに変更
Rの日時処理は一見すると複雑怪奇に思えるかも知れません。Date型、POSIXct型、POSIXlt型と日時を格納するクラスが何種類もありますし、実際にデータを格納するときは文字列としてテキストファイルに書き込むからです。全てテキスト型か数値型として処理したくなりますが、日付の処理は変則的なことが多いので、バグの発生源になります1。来年や来年度まであと何日か数えるのは意外に手間暇ですが、最近はlubridateパッケージを使うのが一般的のようですが、日時クラスを使うと簡単に処理できます。
1 POSIXct → POSIXlt
まずは、タイムゾーンUTCでのUNIX秒を保持するクラスPOSIXctと、日付構造体になっているリストPOSIXlt型の変換です。変換に意外性はないですが、POSIXctとPOSIXltの存在を覚えておきましょう。
<- Sys.time() # 現在時間を取得すると、POSIXct型で戻る
now_ct <- as.POSIXlt(now_ct)
now_lt now_lt
[1] "2022-11-14 08:00:49 JST"
なお、POSIXctとPOSIXltのスーパークラスがPOSIXltになります。
class(now_ct)
[1] "POSIXct" "POSIXt"
class(now_lt)
[1] "POSIXlt" "POSIXt"
2 POSIXct or POSIXlt → numeric
POSIXctとPOSIXltは数値型に変換することができます。
<- as.numeric(now_ct)
now_unix_time now_unix_time
[1] 1668380450
3 numeric → POSIXct, POSIXlt
数値型をPOSIXctもしくはPOSIXltに変換するのも簡単です。
<- as.POSIXct(now_unix_time, origin="1970-01-01", tz="Japan")
now_ct <- as.POSIXlt(now_unix_time, origin="1970-01-01", tz="Japan") now_lt
tzの引数はOlsonNames()
が返す値になります。tzを省略するとデフォルトのタイムゾーンになります。
4 C言語のmktime風にPOSIXctに変換
桁上がり、桁下がり処理をしてくれないので、ISOdate(2022, 30, 1)
などと引数に入れるとエラーになります。
<- ISOdate(1997, 4, 3, tz="Japan")
now_ct <- ISOdatetime(1997, 4, 3, 13, 34, 45, tz="Japan") now_ct
5 POSIXct or POSIXlt → Date型
タイムゾーンの情報が落ちますが、Date型に変換もできます。POSIXltを変換します。
<- as.Date(now_lt); d d
6 Date型 → POSIXct or POSIXlt
Date型から戻すとJSTがUTCに。
as.POSIXlt(d_lt)
7 POSIXct or POSIXlt → 文字列
文字列に直すときにはフォーマット関数が使えます。
format(now_lt, "%Y/%m/%d %H:%M:%S") # helpはstrftimeを参照
[1] "2022/11/14 08:00:49"
8 文字列 → POSIXct
文字列を日時型に変換するのも簡単です。引数format
を省略するとデフォルトのtryFormats
(e.g. %Y/%m/%d
)が試されます。
as.POSIXct("1997*4+3", format="%Y*%m+%d", tz="Japan")
[1] "1997-04-03 JST"
as.POSIXlt("1997*4+3", format="%Y*%m+%d", tz="Japan")
[1] "1997-04-03 JST"
正体はseq.Date
とseq.POSIXt
なので、ヘルプはそちらを参照しましょう。
methods(seq)
[1] seq.Date seq.default seq.POSIXt
see '?methods' for accessing help and source code
9 日時の桁上がり桁下がり処理
POSIXlt型にしておくと、30日後や100時間後のカレンダーの日付が簡単に得られます。ただし$year
が年から1900を引いた値(e.g. 2022年だと122),$mon
が月から1を引いた値(e.g. 12月だと11)なのには注意しましょう。
format(now_lt, "%Y/%m/%d %H:%M:%S")
[1] "2022/11/14 08:00:49"
$mday <- now_lt$mday + 30 # 30日後にセット
now_ltformat(now_lt, "%Y/%m/%d %H:%M:%S")
[1] "2022/12/14 08:00:49"
$hours <- now_lt$hours + 100 # さらに100時間後にセット
now_ltformat(now_lt, "%Y/%m/%d %H:%M:%S")
[1] "2022/12/14 08:00:49"
10 roundで丸め処理ができる
methods(round)
をするとround.POSIXt
が正体だと分かり、?round.POSIXt
でヘルプに辿り着けますが、“secs”,
“mins”, “hours”, “days”, “months”, “years”で丸め処理ができます。
round(as.POSIXlt("1997-4-3 12:34:56"), "mins")
[1] "1997-04-03 12:35:00 JST"
11 シーケンス生成もできる
毎週の日付、計算するのは大変ですが、簡単に出ます。
seq(as.Date("2022/2/24"), as.Date("2022/10/13"), "weeks")
[1] "2022-02-24" "2022-03-03" "2022-03-10" "2022-03-17" "2022-03-24"
[6] "2022-03-31" "2022-04-07" "2022-04-14" "2022-04-21" "2022-04-28"
[11] "2022-05-05" "2022-05-12" "2022-05-19" "2022-05-26" "2022-06-02"
[16] "2022-06-09" "2022-06-16" "2022-06-23" "2022-06-30" "2022-07-07"
[21] "2022-07-14" "2022-07-21" "2022-07-28" "2022-08-04" "2022-08-11"
[26] "2022-08-18" "2022-08-25" "2022-09-01" "2022-09-08" "2022-09-15"
[31] "2022-09-22" "2022-09-29" "2022-10-06" "2022-10-13"
seq(as.POSIXlt("1997-4-3 12:34:56"), as.POSIXlt("1997-4-5 12:34:56"), "12 hours")
[1] "1997-04-03 12:34:56 JST" "1997-04-04 00:34:56 JST"
[3] "1997-04-04 12:34:56 JST" "1997-04-05 00:34:56 JST"
[5] "1997-04-05 12:34:56 JST"
12 応用例① 来年まであと何日?
こんな感じで。月や日を増減する場合はPOSIXlt型を書き替えて、format
で出さないと桁上がり桁下がり処理がされません。
<- as.POSIXlt(Sys.time())
now_lt # 今年(now_lt$year + 1900) + 1は来年
<- ISOdate(now_lt$year + 1900 + 1, 1, 1, tz="Japan")
newyear_ct # 差分をとる前にDate型にして端数を抑制
difftime(as.Date(newyear_ct), as.Date(now_lt), units="days")
Time difference of 48 days
13 応用例② {年/月}の形式を月末日でPOSIXltに変更
月別の時系列データはそこそこあるのですが、当然、1994/01
と言うように日は入っていません。日付型として扱うのを諦めてもよいのですが、月初や月末の日付にしておく方がプロットなどがしやすいときもあります。
<- "1994/01"
ymString <- as.POSIXlt(sub("([0-9]+)/([0-9]+)", "\\1/\\2/1", ymString))
ymd_begin <- ymd_begin; ymd_end$mon <- ymd_end$mon + 1; ymd_end$mday <- 0
ymd_end paste("月初", format(ymd_begin))
[1] "月初 1994-01-01"
paste("月末", format(ymd_end))
[1] "月末 1994-01-31"
2000年問題がありました。↩︎