時間のデータの処理

Wednesday, Oct 28, 2020| Tags: R, time-series

率直に言うと,時間データの処理はややこしいです。データファイル,OS,言語エンコーディングによって時間データのフォーマットは異なります。 ベースRでは,POSIXltPOSIXct のクラス(class) があります。strftime()strptime() などをつかって,時間データの変換は可能です。ここでは,lubridate パッケージの関数の使いかたを紹介します。このパッケージに多数の便利な関数が あります。研究室のコード開発には,lubridate のパッケージをつかってください。。

パッケージの読み込み

まずは実用なパッケージを読み込みます。

library(tidyverse) # データ処理や作図に使う関数はここにある
library(lubridate) # 時間データ処理に使う関数はここ

Rにおける日時について

実際には,Rにおける時間データ(デートタイム) は秒で記録されています。正確には, 1970-01-01 00:00:00 UTC1 から経過した秒数です。

例えば,1542498800 を時間データに変換すると,次の通りです。このデータのクラスは POSIXctPOSIXlt です。

out = as_datetime(1542498000)
out
## [1] "2018-11-17 23:40:00 UTC"
class(out)
## [1] "POSIXct" "POSIXt"

年月日データ1970-01-01 からの日数です。このデータのクラスは Date です。

out = as_date(17850)
out
## [1] "2018-11-15"
class(out)
## [1] "Date"

時分秒データ00:00:00 からの秒数です。hms も時間データ処理用のパッケージです。lubridate はこのパッケージの関数を使用しています。 このデータのクラスは hmsdifftime です。

out = hms::as.hms(34000)
## Warning: as.hms() is deprecated, please use as_hms().
## This warning is displayed once per session.
out
## 09:26:40
class(out)
## [1] "hms"      "difftime"

デートタイムのパース

パース (parse) は,構文解析をすることです。つまり,文字列の内容を解析し,構成要素を明らかにすることです。lubridate には文字列として記述した デートタイムデータをパースするための関数が数種類ありますが,もっとも使いやすい関数は次の通りです。

  • ymd_hms(), ymd_hm(), ymd_h(): 年(y),月(m),日(d), 時(h),分(m),秒(s) ymd_hms("2017-01-21 01:10:05")
  • ydm_hms(), ydm_hm(), ydm_h(): 年(y),日(d),月(m), 時(h),分(m),秒(s) ydm_hm("2017-21-01 01:10")
  • mdy_hms(), mdy_hm(), mdy_h(): 月(m),日(d),年(y), 時(h),分(m),秒(s) mdy_h("01/21/2017 01")
  • dmy_hms(), dmy_hm(), dmy_h(): 日(d),月(m),年(y), 時(h),分(m),秒(s) dmy_hms("21 Jan 2017 01:10:05")
  • ymd(), ydm(), mdy(), myd(), ymd(), dmy(), dym() : ymd(20180108), dmy("2018-01-08")
  • date_decimal(): date_decimal(2018.9)
  • now(tzone = ""): 今日の日時, tzone を渡せば地方時の指定ができます。 now(), now("UTC")
  • today(tzone = ""): 今日年月日 today()
  • fast_strptime(): ベースRの strptime() よりも処理が早い。fast_strptime("2018/02/20", "%y/%m/%d")
  • parse_date_time(): ベースRの strptime() より使いやすい。 parse_date_time("2018/02/20", "ymd")

OS によりデートタイムの表示が異なります。

サーバのOSは Linux です。時間のロケール (locale) は日本語に設定されているかもしれないです。次のコードを実行すれば,確認できます。

Sys.getlocale("LC_TIME")
## [1] "ja_JP.UTF-8"

日本語の場合,デートタイムの地方時は日本 (JST) です。

now()
## [1] "2020-10-30 08:27:49 JST"

曜日や月などを抽出するとき,日本語で表示されます。

now() %>% wday(label = TRUE)
## [1] 金
## Levels: 日 < 月 < 火 < 水 < 木 < 金 < 土
now() %>% month(label = TRUE)
## [1] 10月
## 12 Levels:  1月 <  2月 <  3月 <  4月 <  5月 <  6月 <  7月 <  8月 < ... < 12月

英語にする場合,ロケールを英語に変更します。

Sys.setlocale("LC_TIME", "en_US.UTF-8") # アメリカ英語に設定
## [1] "en_US.UTF-8"
now() %>% wday(label = TRUE)
## [1] Fri
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
now() %>% month(label = TRUE)
## [1] Oct
## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec

デートタイムの要素を抽出

デートタイムデータから特定の要素を抽出したいときに使う関数です。

  • date(): 年月日
  • year(): 年
  • month(): 月
  • day(): 日
  • wday(): 曜日
  • hour(): 時
  • minute(): 分
  • second(): 秒
  • week(): 週
  • am(), pm(): 午前か午後を確認し,論理値を返す。
  • leap_year(): うるう年か確認し,論理値を返す。

四捨五入,切り上げ,切り下げ

x にデートタイムオブジェクトを渡し,unit に単位を渡す。単位に,year, month, day, hour, minute, second は有効ですが, 10 minute などにすると 10分間隔の単位になります。

  • floor_date(x, unit): 切り下げ
  • round_date(x, unit): 四捨五入
  • ceiling_date(x, unit): 切り上げ
x = now()
x
## [1] "2020-10-30 08:27:49 JST"
floor_date(x, unit = "minute")
## [1] "2020-10-30 08:27:00 JST"
floor_date(x, unit = "20 minute")
## [1] "2020-10-30 08:20:00 JST"
ceiling_date(x, unit = "month")
## [1] "2020-11-01 JST"
floor_date(x, unit = "month")
## [1] "2020-10-01 JST"

標準時 (Time zones)

Rは 600 以上の地方時の認識ができます。

OlsonNames() の関数をつかえば,認識されている地方時の名前が返ってきます。日本の場合は,次の通りです。

OlsonNames() %>% grep(pattern = "Jap|Tok", value = TRUE)
## [1] "Asia/Tokyo" "Japan"

デートタイムを用いた計算

デートタイムの算数は本当にややこしいです。デートタイムを記録するために,lubridate は 3 つのクラスを用意しています。

  • period: 時計で経過した時間
  • duration: 実際の時間の経過
  • interval: 開始・終了時間の間の期間

これは夏時間 (daylight savings time; サマータイム) の時に大きな影響があります。次のアメリカの EST/EDT を参考にしてください。

x = ymd_hms("2018-03-11 01:30:00", tz = "US/Eastern")
x + minutes(90)
## [1] "2018-03-11 03:00:00 EDT"
x + dminutes(90)
## [1] "2018-03-11 04:00:00 EDT"
interval(x, x + minutes(90))
## [1] 2018-03-11 01:30:00 EST--2018-03-11 03:00:00 EDT
x = ymd_hms("2018-11-04 00:30:00", tz = "US/Eastern")
x + minutes(90)
## [1] "2018-11-04 02:00:00 EST"
x + dminutes(90)
## [1] "2018-11-04 01:00:00 EST"
interval(x, x + minutes(90))
## [1] 2018-11-04 00:30:00 EDT--2018-11-04 02:00:00 EST

うるう年の計算にも影響がでます。

x = ymd("2019-03-01")
x + years(1)
## [1] "2020-03-01"
x + dyears(1)
## [1] "2020-02-29 06:00:00 UTC"
interval(x, x + years(1))
## [1] 2019-03-01 UTC--2020-03-01 UTC

ときに,months() で月を加算すると imaginary date の問題がでます。

ymd("2018-01-31") + months(1)
## [1] NA
ymd("2018-01-31") %m+% months(1)
## [1] "2018-02-28"
ymd("2018-03-30") - months(1)
## [1] NA
ymd("2018-03-30") %m-% months(1)
## [1] "2018-02-28"

時計時間には period, 経過時間には duration

時計時間の場合,period クラスを使用してください。

out = years(1) + months(3) + minutes(20)
out
## [1] "1y 3m 0d 0H 20M 0S"

241ヶ月の期間

out = period(241, units = "months")
out
## [1] "241m 0d 0H 0M 0S"
period_to_seconds(out) # 秒に変換する
## [1] 633781800

intervalperiod に変換する。

out = now()
out = interval(out, out + minutes(90))
out
## [1] 2020-10-30 08:27:50 JST--2020-10-30 09:57:50 JST
as.period(out, unit = "seconds")
## [1] "5400S"
as.period(out, unit = "minutes")
## [1] "90M 0S"

経過時間の場合は,duration クラスを使用してください。

out = ddays(13)
out
## [1] "1123200s (~1.86 weeks)"
duration(13, units = "days")
## [1] "1123200s (~1.86 weeks)"

durationperiod に変換する。

out = now()
out = interval(out, out + minutes(90))
out
## [1] 2020-10-30 08:27:50 JST--2020-10-30 09:57:50 JST
as.duration(out)
## [1] "5400s (~1.5 hours)"

時間データの表記に使うフォーマット指定方法

日付関係のフォーマット記号

  • %a: 曜日の省略
  • %A: 曜日
  • %b: 月名の省略
  • %B: 月名
  • %c: %a %b %e %H:%M:%S %Y の省略
  • %d: 日,1 から 9 日は 1 桁目に 0
  • %D: %m/%d/%y の省略
  • %e: 日,1 から 9 日は 1 桁目にスペース
  • %F: %Y-%m-%d の省略
  • %H: 時,24時間表記,1 桁目に 0
  • %I: 時,12時間表記,1 桁目に 0
  • %j: 年日,001 から 366
  • %m: 月,1 から 12 は 1 桁目に 0
  • %M: 分,1 桁目に 0
  • %p: ロケールにおける AM/PM に相当する文字列(日本語の場合は午前・午後)
  • %S: 秒,0 から 61 は 1 桁目に 0
  • %y: 西暦の下 2 桁
  • %Y: 西暦 4 桁

  1. Coordinated Universal Time; 協定世界時↩︎

Contact me for more information.

Contact