LoginSignup
11
4

More than 5 years have passed since last update.

関数型でデータサイエンス番外編:様々な日時文字列を扱えるようにする

Last updated at Posted at 2018-06-10

(この記事は、「fukuoka.ex(その2) Elixir Advent Calendar 2017」の22日目、
  および「ディープラーニングのエンジニアリング Advent Calendar 2017」の4日目です)

昨日は、@takasehideki さんの「Erlang/OTPのソースビルドでHiPEが入らなかった時の対処法」でした


fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます:bow:

今回は、次回予定の「関数型でデータサイエンス#3:インプットしたデータを集約する(前処理②)」の前段となる、「様々な日時文字列を扱えるようにする」という小ネタを番外編としてお送りします


:shamrock::shamrock::shamrock::shamrock: お礼:各種ランキングに83回のランクインを達成しました :shamrock::shamrock::shamrock::shamrock:

4/27から、44日間に渡り、毎日お届けしている「季節外れのfukuoka.ex Elixir Advent Calendar」「季節外れのfukuoka.ex(その2) Elixir Advent Calender」ですが、Qiitaトップページトレンドランキングに13回入賞、Elixirウィークリーランキングでは7週連続で1/2/3フィニッシュを飾り、各種ランキング通算で、トータル87回ものランクインを達成しています

みなさまの暖かい応援に励まされ、合計616件ものQiita「いいね」もいただき、fukuoka.exアドバイザーズとfukuoka.exキャストの一同、ますます季節外れのfukuoka.ex Advent Calendar、頑張っていきます:rocket:

image.png

日時文字列をElixirで扱える日時型に変換

日時文字列を、Elixir内の日時型(NaiveDateTime)として扱うためには、@kobatako さんの「Slack botで通知したい投稿日のものを通知する with Qiita API」でも出てきた「Timex」を使うと簡単です

iex> { :ok, dt } = "2018/6/11 1:35:4" |> Timex.parse( "%Y/%_m/%_d %_H:%_M:%_S", :strftime )
{:ok, ~N[2018-06-11 01:35:04]}
iex> dt
~N[2018-06-11 01:35:04]

なお、NaiveDateTimeは、キーがアトムのマップ(正しくは構造体)なので、ドット「.」で年月日や時分秒にアクセスできますし、NaiveDateTimeの各種関数が利用可能です

iex> is_map( dt )
true
iex> dt.year
2018
iex> "#{ dt.month }/#{ dt.day }"
"6/11"
iex> dt |> NaiveDateTime.to_date
~D[2018-06-11]
iex> dt |> NaiveDateTime.to_time
~T[01:35:04]
dt |> NaiveDateTime.to_erl
{{2018, 6, 11}, {1, 35, 4}}

様々なバリエーションの日時文字列を一括で変換するには?

インプットデータには、様々な形式の日付文字列が、CSVやAPIで渡されてきます

たとえば、以下のような日時文字列群です

"2018/1"
"2018/ 1"
"2018/01"
"2018/1/2"
"2018/1/2 3:4"
"2018/1/2 3:4:5"
"2018/1/2 03:04"
"2018/1/2 03:04:05"
"2018/ 1/ 2"
"2018/1/ 2"
"2018/01/ 2"
"2018/01/02"
"2018/01/02 23:44"
"2018/01/02 23:44:09"
"2018/01/02 23:44:09.005"
"2018-01-02"
"2018-01-02 23:44"
"2018-01-02 23:44:09"
"2018-01-02 23:44:09.005"
"Jan-02-2018"
"Jan-02-2018 23:44"
"Jan-02-2018 23:44:09"
"Jan-02-2018 23:44:09.005"
"Jan-02-18"
"Jan-02-18 23:44"
"Jan-02-18 23:44:09"
"Jan-02-18 23:44:09.005"
"January-02-2018"
"January-02-2018 23:44"
"January-02-2018 23:44:09"
"January-02-2018 23:44:09.005"
"2018-01-02 23:44:09Z"
"2018-01-02T23:44:09Z"

これらのバリエーション違いを一通り収容するには、以下のような、各日時形式に合わせた変換をかける必要があります

defmodule Dt do
  def to_datetime( str ) when is_binary( str ) do
    [
      "%Y/%_m", 
      "%Y/%_m/%_d", 
      "%Y/%_m/%_d %_H:%_M", 
      "%Y/%_m/%_d %_H:%_M:%_S", 
      "%Y/%_m/%_d %_H:%_M:%_S.%_L", 
      "%Y-%_m-%_d", 
      "%Y-%_m-%_d %_H:%_M", 
      "%Y-%_m-%_d %_H:%_M:%_S", 
      "%Y-%_m-%_d %_H:%_M:%_S.%_L", 
      "%b-%_d-%Y", 
      "%b-%_d-%Y %_H:%_M", 
      "%b-%_d-%Y %_H:%_M:%_S", 
      "%b-%_d-%Y %_H:%_M:%_S.%_L", 
      "%b-%_d-%y", 
      "%b-%_d-%y %_H:%_M", 
      "%b-%_d-%y %_H:%_M:%_S", 
      "%b-%_d-%y %_H:%_M:%_S.%_L", 
      "%B-%_d-%Y", 
      "%B-%_d-%Y %_H:%_M", 
      "%B-%_d-%Y %_H:%_M:%_S", 
      "%B-%_d-%Y %_H:%_M:%_S.%_L", 
      "%Y-%_m-%_d %_H:%_M:%_SZ", 
      "%Y-%_m-%_d %_H:%_M:%_S.%_LZ", 
      "%Y-%_m-%_dT%_H:%_M:%_SZ", 
      "%Y-%_m-%_dT%_H:%_M:%_S.%_LZ", 
    ]
    |> Enum.map( &( str |> Timex.parse( &1, :strftime ) ) )
    |> Enum.filter( &elem( &1, 0 ) == :ok )
    |> List.first
    |> Tpl.ok
  end
end

上記関数を使い、日時文字列群を各自変換した結果が以下です

いずれも、Elixirで扱える日時型に変換されています

iex> Dt.to_datetime( "2018/1" )
~N[2018-01-01 00:00:00]
iex> Dt.to_datetime( "2018/ 1" )
~N[2018-01-01 00:00:00]
iex> Dt.to_datetime( "2018/01" )
~N[2018-01-01 00:00:00]
iex> Dt.to_datetime( "2018/1/2" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018/1/2 3:4" )
~N[2018-01-02 03:04:00]
iex> Dt.to_datetime( "2018/1/2 3:4:5" )
~N[2018-01-02 03:04:05]
iex> Dt.to_datetime( "2018/1/2 03:04" )
~N[2018-01-02 03:04:00]
iex> Dt.to_datetime( "2018/1/2 03:04:05" )
~N[2018-01-02 03:04:05]
iex> Dt.to_datetime( "2018/ 1/ 2" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018/1/ 2" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018/01/ 2" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018/01/02" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018/01/02 23:44" )
~N[2018-01-02 23:44:00]
iex> Dt.to_datetime( "2018/01/02 23:44:09" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "2018/01/02 23:44:09.005" )
~N[2018-01-02 23:44:09.005]
iex> Dt.to_datetime( "2018-01-02" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "2018-01-02 23:44" )
~N[2018-01-02 23:44:00]
iex> Dt.to_datetime( "2018-01-02 23:44:09" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "2018-01-02 23:44:09.005" )
~N[2018-01-02 23:44:09.005]
iex> Dt.to_datetime( "Jan-02-2018" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "Jan-02-2018 23:44" )
~N[2018-01-02 23:44:00]
iex> Dt.to_datetime( "Jan-02-2018 23:44:09" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "Jan-02-2018 23:44:09.005" )
~N[2018-01-02 23:44:09.005]
iex> Dt.to_datetime( "Jan-02-18" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "Jan-02-18 23:44" )
~N[2018-01-02 23:44:00]
iex> Dt.to_datetime( "Jan-02-18 23:44:09" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "Jan-02-18 23:44:09.005" )
~N[2018-01-02 23:44:09.005]
iex> Dt.to_datetime( "January-02-2018" )
~N[2018-01-02 00:00:00]
iex> Dt.to_datetime( "January-02-2018 23:44" )
~N[2018-01-02 23:44:00]
iex> Dt.to_datetime( "January-02-2018 23:44:09" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "January-02-2018 23:44:09.005" )
~N[2018-01-02 23:44:09.005]
iex> Dt.to_datetime( "2018-01-02 23:44:09Z" )
~N[2018-01-02 23:44:09]
iex> Dt.to_datetime( "2018-01-02T23:44:09Z" )
~N[2018-01-02 23:44:09]

終わり

今回は、「データ変換」で使える、様々なバリエーションの日時文字列を扱うテクニックをお伝えしました

なお今回の実装内容は、小粒でピリリなユーティリティライブラリ「smallex」のDtモジュールにある、to_datetime()という関数にて既に搭載していますので、次回以降のコラムにて実際に利用したいと思います

次回は、「インプットしたデータの集約」について解説します

明日は @tuchiro さんの「ElixirでSI開発入門 #9 Railsからのモデルの移行2(DDLをパースする)」です

:stars::stars::stars::stars::stars: お知らせ:Elixir MeetUpを6月末に開催します :stars::stars::stars::stars::stars:

「fukuoka.ex#11:DB/データサイエンスにコネクトするElixir」を6/22(金)19時に開催します

fukuoka.exの発足から、ちょうど1周年となる、記念的なイベントでもあるため、このコラムを気に入っていただいた方と、ぜひ一緒に盛り上がりたいです

福岡近辺にお住まいの方であれば、遊びに来てください

大人気により、一度は満席となりましたが、増枠しましたので、下記URLよりご参加ください
https://fukuokaex.connpass.com/event/87241

特別ゲストは、Erlang/Elixirの両面で世界的に有名な「力武 健次さん」と、北九州の飯塚市で「e-ZUKA Tech Night」を6年間主催し続けるハウインターナショナルの「谷口 耕平さん」のお二人と、実に豪華なイベントです

私は、今回のシリーズを踏まえた、「1家に1台、パーソナルなデータ分析AIを全員が持つ2020年を作る」というタイトルで、Elixirによる、ブラウザUI上からサクっとデータ分析プラットフォームを披露するLTをお届けします

image.png

p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の image.pngimage.png のクリックをお願いしますー:bow:

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4