R_tidyverse

lubridateを極める (1) 日時値のパース

最近はRやpythonを使う機会が増えており、まだSASを使う頻度がずっと多いものの使う時間が少しずつ減ってきました。
今回は私の勉強も兼ねて以前からやりたかったlubridateの紹介をやっていきたいと思います。

私のブログではSASと比較した場合の長所、短所および想定される実務上の利用例を考察していきます。

lubridateとは

lubridateは日時値を取り扱う時に有用な関数およびオブジェクトを操作するためのRパッケージです。tidyverseに含まれるパッケージの一つなので、
tidyverseを導入していればすぐに使うことができます。

SASではSAS日時値にフォーマットを当てることで任意の日時・時刻を表現します。時刻演算や比較はSAS日時値の数値演算で実施できます。
それに対しlubridateは日時や期間を特別なオブジェクトとして管理することで、日時や時刻に関する演算が簡単に実施できます。

SASはオブジェクトという概念がないので複雑な演算はユーザーが定義しないと実施できませんが、lubridateはオブジェクトに付随するメゾッドを使用することで
演算定義の手間を軽減することができます。

文字列を日時値に変換する

lubridateには文字列の構造を解析(パース)し、日時値に変換する関数が複数用意されています。例えばymd_hms関数は年、月、日、時刻の順に入力された文字列を日時値に変換します。
関数名は時刻情報の順番を表しているのでわかりやすいですね。このような関数が複数用意されています。ISO形式の変換もできますからCdiscも問題なさそう。

library(lubridate)
> ymd_hms("2022/5/11 12:11:34")
 "2022-05-11 12:11:34 UTC"
> ymd_hms("20210915T155300")
 "2021-09-15 15:53:00 UTC"
> mdy("12/31/2021")
 "2021-12-31"
> dmy(" 14 July 2020")
 "2020-07-14"

parse_date_timeを使おう

この関数群は便利なのですが、実務ですとちょっと面倒な問題が発生します。というのも複数の日付フォーマットが存在するデータだとこの関数だとうまくいきません。
例えばymd_hms関数はymd_hms形式以外のフォーマット文字列が引数に含まれていると警告を出力し、パースできません。

eCRFのような入力制御のある電子システムで収集されたデータの場合は1つのフォーマットで日時が記録されますが、そういう制御がないうえに人力で入力されたデータは
複数の日付フォーマットが含まれる可能性があります。西暦と和暦が混在していたらもう最悪です。

このような場合に対応するため、lubridateはparse_date_timeという専用の関数が用意されています。

この関数は第2引数にフォーマット文字列を指定するのですが、ベクトルも引数とすることができます。その場合は、ベクトルの第一要素のフォーマットでパースが失敗した場合第2要素のフォーマットでパースしてくれます。

すなわち複数のフォーマットをベクトルで指定でき、ベクトルの要素順にパースを実行します。パースが成功した最初のフォーマットを適用した結果を返します。

SASの場合ですと、ANYDTDTM.のような対応文字列の多い汎用インフォーマットを使うか、正規表現をつかって文字列パターンに応じて使用フォーマットを切り替えるといった方法が考えられます。
SASの汎用インフォーマットを使う方法はlubridateよりも簡単ですが、パースの挙動を制御することは難しいです。柔軟にパースしたいのであれば正規表現を使うしかないと思います。

それに対しlubridateはフォーマット文字列さえ適切な順序で指定できれば正規表現を知らなくても柔軟なパースが可能となります。

テストデータで試してみましょう。

以下のような複数の日付フォーマットを含むカラムEXSTDTがあります。これを日時値に変換したカラムEXSTDT2を追加します。

EXSTDT
2022/2/1
1 Jan 2021 23:59:59
20220415T090045
5/1/2022
令和4年3月15日

使用されるフォーマット文字列をベクトルとして指定するだけです。この時フォーマット文字列は大文字小文字の区別がある点には注意しましょう。

library(tidyverse)
library(lubridate)
library(readxl)
df1 <- read_excel("date_parse.xlsx") %>%
mutate(EXSTDT2 = parse_date_time(EXSTDT,c("ymd","ymdHMS", "dmyHMS", "mdy")))

実行結果は以下の通りです。複数のフォーマットが混在していても変換できました。

和暦には未対応

上記のパース結果を見てもらうとわかりますが、和暦を含んだ文字列だとパースがうまくいかず全く違う日付が出力されています。
このケースの場合パース自体は成功しているため警告はでませんので注意が必要です。

lubridateは和暦には対応していませんので、zipanguのような和暦を取り扱うことができるパッケージで事前に処理する必要があります。

uribo.hatenablog.com

SASには和暦を取り扱うインフォーマットが用意されていますのでこの点についてはSASのほうが優秀ですが、今回のように西暦和暦が混在している場合汎用インフォーマット単体では処理できないので、
正規表現による条件分岐が必要になりそうです。