備忘ログ

チラシの裏的備忘録&メモ

Rの`{stringi}`でInternational Components for Unicode(ICU)で西暦を和暦に直したり、和暦を西暦に直したりできることを知った、すごいというメモ

Rの{stringi}でInternational Components for Unicode(ICU)で西暦を和暦に直したり、和暦を西暦に直したりできることを知った。

すごいφ(..)メモメモ。

日付データが和暦、西暦混じりのデータを取り扱いやすいように西暦のみにするのに使えそうだと思った。

触った感じ旧暦時代の取り扱いがわからなかった(特に閏月)が、今と同じ暦の範囲なら問題なさそう。

例えば、日付(生年月日等)を手書き調査用紙で自由記載で答えてもらうときの欄を(   年  月  日)みたいにしていると、西暦で書く人と和暦で書く人がいて、とりあえず打ち込むときにはそのまま打ち込んで最終的に西暦に合わせる処理をしたい(入力時に和暦を西暦になおす計算を一つづつやっているとミスが発生する可能性が転記ミスを含めて上がる気がする)とか、ナラティブデータを処理するときに中に和暦と西暦の語りが交じるときがあるがそれを整える処理するために西暦に計算し直すのにとてもよいと思った。

追記:

エクセルで打ち込むと、和暦で打ち込んだ日付もセルが日付型だと判定されると内部で西暦の日付データで持っている?様子?。エクセルの勝手補正は結構困るが、表記が統一されるのは嬉しいかもしれない場合にはこの処理いらない?CSVベタ打ちでデータを持っている場合は使える?

:追記終了

例えば次のように、日付が和暦と西暦混じっているデータで1990年代では下二桁で年数を示すパターンもよくあったので、それも含めてごった煮パターンの日付データとなっているものを想定する。

df <- data.frame(ID = LETTERS[1:6],
                 date = c("昭和60年10月1日", "1988/2/10", "平成元年5月5日", "平成1年7月10日", "平成2年2月1日", "91年4月1日"))

df
##   ID            date
## 1  A 昭和60年10月1日
## 2  B       1988/2/10
## 3  C  平成元年5月5日
## 4  D  平成1年7月10日
## 5  E   平成2年2月1日
## 6  F      91年4月1日

ここで、すでに西暦データはそのまま西暦データとして西暦データとして、和暦データを西暦に直す処理をするとしたら次のようにできる。

purrr::map_chr(df$date, function(x){
  if(stringr::str_detect(x, "^[^[:digit:]]")){
    stringi::stri_datetime_parse(x, format = "Gy年M月d日", locale = "ja-JP-u-ca-japanese") |> 
      lubridate::as_date() |> 
      as.character()
  }else{
    lubridate::as_date(x) |> 
      as.character()
  }
})
## [1] "1985-10-01" "1988-02-10" "1989-05-05" "1989-07-10" "1990-02-01"
## [6] "1991-04-01"

正規表現で最初の文字が数字じゃなければ和暦が含まれているだろうということで、{stringi}のstri_datetime_parse()をつかって、西暦に変換する。

そのまま西暦データは{lubridate}as_date()をつかって、比較的曖昧な(91年4月1日)も処理できるようにして、西暦表記にしている。

最後、文字列にしているのはベクトルで取り出すときに文字列型じゃないと数値型になってしまうことがあるから、回避策。

もっと、例えば和暦にタイポがあり得る(昭和が正和にタイポしていたり)、あり得ない日付がタイポや記載ミスで存在しえるのであれば、更にそれの判定を入れるときれいに処理できそうだと思う。

データフレーム上に直接くっつけるなら、Date型で取り出せそう。

df |> 
  dplyr::mutate(date = dplyr::if_else(stringr::str_detect(date, "^[^[:digit:]]"), 
                                      lubridate::as_date(stringi::stri_datetime_parse(date, format = "Gy年M月d日", locale = "ja-JP-u-ca-japanese")),
                                      lubridate::as_date(date)))
## Warning: 3 failed to parse.

##   ID       date
## 1  A 1985-10-01
## 2  B 1988-02-10
## 3  C 1989-05-05
## 4  D 1989-07-10
## 5  E 1990-02-01
## 6  F 1991-04-01

これは使えそう。