juliaで前処理大全 10.日時型
juliaで前処理大全その9です。今回は日時型を取り扱います。
日時型、日付型への変換
Q 日時型、日付型への変換
ホテルの予約レコードから、予約日時の列を日時型、日付型へ変換する問題です。 標準パッケージのDates.jl
を用いることで簡単に実装できます。 一度、文字列をDateTime
型に変換し、その後、Date
型・Time
型に変換を行います。 文字列をDateTime
型に変換する際に、 ``dateformat``を指定するのがポイントでしょう。
using DataFrames, CSV, Chain, Downloads, Dates
reserve_url = "https://raw.githubusercontent.com/hanafsky/awesomebook/master/data/reserve.csv"
reserve_df = @chain reserve_url begin
Downloads.download
CSV.File
DataFrame
transform(:reserve_datetime=>ByRow(d->DateTime(d, dateformat"yyyy-mm-dd H:M:S"))=>:reserve_datetime)
transform(:reserve_datetime=>ByRow(Date)=>:reserve_date,
:reserve_datetime=>ByRow(Time)=>:reserve_time)
end
first(reserve_df, 10) |> println
10×11 DataFrame
Row │ reserve_id hotel_id customer_id reserve_datetime checkin_date checkin_time checkout_date people_num total_price reserve_date reserve_time
│ String7 String7 String7 Dates.DateTime Dates.Date Dates.Time Dates.Date Int64 Int64 Dates.Date Dates.Time
─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ r1 h_75 c_1 2016-03-06T13:09:42 2016-03-26 10:00:00 2016-03-29 4 97200 2016-03-06 13:09:42
2 │ r2 h_219 c_1 2016-07-16T23:39:55 2016-07-20 11:30:00 2016-07-21 2 20600 2016-07-16 23:39:55
3 │ r3 h_179 c_1 2016-09-24T10:03:17 2016-10-19 09:00:00 2016-10-22 2 33600 2016-09-24 10:03:17
4 │ r4 h_214 c_1 2017-03-08T03:20:10 2017-03-29 11:00:00 2017-03-30 4 194400 2017-03-08 03:20:10
5 │ r5 h_16 c_1 2017-09-05T19:50:37 2017-09-22 10:30:00 2017-09-23 3 68100 2017-09-05 19:50:37
6 │ r6 h_241 c_1 2017-11-27T18:47:05 2017-12-04 12:00:00 2017-12-06 3 36000 2017-11-27 18:47:05
7 │ r7 h_256 c_1 2017-12-29T10:38:36 2018-01-25 10:30:00 2018-01-28 1 103500 2017-12-29 10:38:36
8 │ r8 h_241 c_1 2018-05-26T08:42:51 2018-06-08 10:00:00 2018-06-09 1 6000 2018-05-26 08:42:51
9 │ r9 h_217 c_2 2016-03-05T13:31:06 2016-03-25 09:30:00 2016-03-27 3 68400 2016-03-05 13:31:06
10 │ r10 h_240 c_2 2016-06-25T09:12:22 2016-07-14 11:00:00 2016-07-17 4 320400 2016-06-25 09:12:22
年/月/日/時刻/分/秒/曜日への変換
各日時要素の取り出し
DateTime
型から各日時要素の取り出しもDates.jl
の関数で実装されています。 任意の文字列に変換するにはDates.format
関数を利用する必要があります。(exportされていないので、パッケージの指定が必要です。)
dayname関数はlocaleを指定できるようになっていますが、英語にしか対応していないようです。 公式ドキュメントを見るとフランス語バージョンが書いてあってので、必要な場合は勝手に拡張しろということなのでしょう。
@chain reserve_df begin
select(:reserve_datetime=>ByRow(year)=>:year,
:reserve_datetime=>ByRow(month)=>:month,
:reserve_datetime=>ByRow(day)=>:wday,
:reserve_datetime=>ByRow(d->dayname(d,locale="english"))=>:weekday,
:reserve_datetime=>ByRow(hour)=>:hour,
:reserve_datetime=>ByRow(minute)=>:minute,
:reserve_datetime=>ByRow(second)=>:second,
:reserve_datetime=>ByRow(d->Dates.format(d, "yyyy-mm-ss H:M:S"))=>:format_str,
)
first(10)
println
end
10×8 DataFrame
Row │ year month wday weekday hour minute second format_str
│ Int64 Int64 Int64 String Int64 Int64 Int64 String
─────┼────────────────────────────────────────────────────────────────────────────
1 │ 2016 3 6 Sunday 13 9 42 2016-03-00 13:9:42
2 │ 2016 7 16 Saturday 23 39 55 2016-07-00 23:39:55
3 │ 2016 9 24 Saturday 10 3 17 2016-09-00 10:3:17
4 │ 2017 3 8 Wednesday 3 20 10 2017-03-00 3:20:10
5 │ 2017 9 5 Tuesday 19 50 37 2017-09-00 19:50:37
6 │ 2017 11 27 Monday 18 47 5 2017-11-00 18:47:5
7 │ 2017 12 29 Friday 10 38 36 2017-12-00 10:38:36
8 │ 2018 5 26 Saturday 8 42 51 2018-05-00 8:42:51
9 │ 2016 3 5 Saturday 13 31 6 2016-03-00 13:31:6
10 │ 2016 6 25 Saturday 9 12 22 2016-06-00 9:12:22
日時差への変換
日時差の計算
予約日時とチェックイン日時の差を求め、それを年単位、月単位、日単位から秒単位まで差を求めるお題です。 年や月単位であれば、先にDateTime
型から年や月を取得して変換することになります。 DateTime
型の差分はMillisecond
型になるため、日、時間、分、秒単位にするには少し工夫が必要です。
t1 = DateTime(2022, 1, 12, 12, 23, 34)
t2 = DateTime(2021, 12, 24, 23, 34, 45)
dt = t1 - t2
println(dt)
1601329000 milliseconds
日単位であれば、Dates.days
関数が単位変換に利用可能です。秒・分・時間に関しては自分で関数を作る必要があります。 Dates.days
関数の実装を参考に秒・分・時間について変換する関数を作ってみました。
tosecond(dt::Millisecond) = div(Dates.value(dt), 1000)
tominute(dt::Millisecond) = div(Dates.value(dt), 60000)
tohour(dt::Millisecond) = div(Dates.value(dt), 3600000)
@show tohour(dt)
tohour(dt) = 444
@chain reserve_df begin
transform([:checkin_date, :checkin_time]=>ByRow(+)=>:checkin_datetime)
select(
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->year(x)-year(y))=>:diff_year,
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->12year(x)+month(x)-12year(y)-month(y))=>:diff_month,
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->Dates.days(x-y))=>:diff_days,
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->tohour(x-y))=>:diff_hours,
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->tominute(x-y))=>:diff_minutes,
[:checkin_datetime, :reserve_datetime]=>ByRow((x, y)->tosecond(x-y))=>:diff_seconds,
)
first(10)
println
end
10×6 DataFrame
Row │ diff_year diff_month diff_days diff_hours diff_minutes diff_seconds
│ Int64 Int64 Int64 Int64 Int64 Int64
─────┼──────────────────────────────────────────────────────────────────────────
1 │ 0 0 19 476 28610 1716618
2 │ 0 0 3 83 5030 301805
3 │ 0 1 24 598 35936 2156203
4 │ 0 0 21 511 30699 1841990
5 │ 0 0 16 398 23919 1435163
6 │ 0 1 6 161 9672 580375
7 │ 1 1 26 647 38871 2332284
8 │ 0 1 13 313 18797 1127829
9 │ 0 0 19 475 28558 1713534
10 │ 0 1 19 457 27467 1648058
日時型の増減
日時の増減処理
日時のデータを1日、1時間、1分、1秒増やすという問題です。 DateTime
型の列のそれぞれの行について、Period
型のデータを足すと実現可能です。 可読性も高く、Awesomeでしょう。
@chain reserve_df begin
select(
:reserve_datetime => ByRow(d->d+Day(1)) => :reserve_datetime_1d,
:reserve_date => ByRow(d->d+Day(1)) => :reserve_date_1d,
:reserve_datetime => ByRow(d->d+Hour(1)) => :reserve_datetime_1h,
:reserve_datetime => ByRow(d->d+Minute(1)) => :reserve_datetime_1m,
:reserve_datetime => ByRow(d->d+Second(1)) => :reserve_datetime_1s
)
first(10)
println
end
10×5 DataFrame
Row │ reserve_datetime_1d reserve_date_1d reserve_datetime_1h reserve_datetime_1m reserve_datetime_1s
│ Dates.DateTime Dates.Date Dates.DateTime Dates.DateTime Dates.DateTime
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ 2016-03-07T13:09:42 2016-03-07 2016-03-06T14:09:42 2016-03-06T13:10:42 2016-03-06T13:09:43
2 │ 2016-07-17T23:39:55 2016-07-17 2016-07-17T00:39:55 2016-07-16T23:40:55 2016-07-16T23:39:56
3 │ 2016-09-25T10:03:17 2016-09-25 2016-09-24T11:03:17 2016-09-24T10:04:17 2016-09-24T10:03:18
4 │ 2017-03-09T03:20:10 2017-03-09 2017-03-08T04:20:10 2017-03-08T03:21:10 2017-03-08T03:20:11
5 │ 2017-09-06T19:50:37 2017-09-06 2017-09-05T20:50:37 2017-09-05T19:51:37 2017-09-05T19:50:38
6 │ 2017-11-28T18:47:05 2017-11-28 2017-11-27T19:47:05 2017-11-27T18:48:05 2017-11-27T18:47:06
7 │ 2017-12-30T10:38:36 2017-12-30 2017-12-29T11:38:36 2017-12-29T10:39:36 2017-12-29T10:38:37
8 │ 2018-05-27T08:42:51 2018-05-27 2018-05-26T09:42:51 2018-05-26T08:43:51 2018-05-26T08:42:52
9 │ 2016-03-06T13:31:06 2016-03-06 2016-03-05T14:31:06 2016-03-05T13:32:06 2016-03-05T13:31:07
10 │ 2016-06-26T09:12:22 2016-06-26 2016-06-25T10:12:22 2016-06-25T09:13:22 2016-06-25T09:12:23
季節への変換
季節への変換
こちらはホテルの予約レコードから予約月を季節に変換する問題です。 数値から季節の文字コードに変換するのは、本と同じですが、合成関数を利用して、簡潔にかけたと思います。
using CategoricalArrays
function to_season(m)
3≤m<6 ? "spring" :
6≤m<9 ? "summer" :
9≤m<12 ? "autumn" : "winter"
end
@chain reserve_df begin
select(:reserve_datetime => ByRow(to_season∘month) => :reserve_datetime_season)
transform(:reserve_datetime_season => categorical => :reserve_datetime_season)
first(10)
println
end
10×1 DataFrame
Row │ reserve_datetime_season
│ CategoricalValue…
─────┼─────────────────────────
1 │ spring
2 │ summer
3 │ autumn
4 │ spring
5 │ autumn
6 │ autumn
7 │ winter
8 │ spring
9 │ spring
10 │ summer
時間帯への変換
時間帯への変換はコードが記載されていないので割愛します。
平日/休日への変換
休日フラグの付与
こちらは、ホテルの予約レコードと休日のマスターレコードを内部結合するだけです。 技術的に新しいものはありません。
holiday_url = "https://raw.githubusercontent.com/hanafsky/awesomebook/master/data/holiday_mst.csv"
holiday_mst = @chain holiday_url begin
Downloads.download
CSV.File
DataFrame
end
@chain reserve_df begin
innerjoin(holiday_mst, on=[:checkin_date => :target_day ])
first(10)
println
end
10×13 DataFrame
Row │ reserve_id hotel_id customer_id reserve_datetime checkin_date checkin_time checkout_date people_num total_price reserve_date reserve_time holidayday_flg nextday_is_holiday_flg
│ String7 String7 String7 Dates.DateTime Dates.Date Dates.Time Dates.Date Int64 Int64 Dates.Date Dates.Time Bool Bool
─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ r1 h_75 c_1 2016-03-06T13:09:42 2016-03-26 10:00:00 2016-03-29 4 97200 2016-03-06 13:09:42 true true
2 │ r2 h_219 c_1 2016-07-16T23:39:55 2016-07-20 11:30:00 2016-07-21 2 20600 2016-07-16 23:39:55 false false
3 │ r3 h_179 c_1 2016-09-24T10:03:17 2016-10-19 09:00:00 2016-10-22 2 33600 2016-09-24 10:03:17 false false
4 │ r4 h_214 c_1 2017-03-08T03:20:10 2017-03-29 11:00:00 2017-03-30 4 194400 2017-03-08 03:20:10 false false
5 │ r5 h_16 c_1 2017-09-05T19:50:37 2017-09-22 10:30:00 2017-09-23 3 68100 2017-09-05 19:50:37 false true
6 │ r6 h_241 c_1 2017-11-27T18:47:05 2017-12-04 12:00:00 2017-12-06 3 36000 2017-11-27 18:47:05 false false
7 │ r7 h_256 c_1 2017-12-29T10:38:36 2018-01-25 10:30:00 2018-01-28 1 103500 2017-12-29 10:38:36 false false
8 │ r8 h_241 c_1 2018-05-26T08:42:51 2018-06-08 10:00:00 2018-06-09 1 6000 2018-05-26 08:42:51 false true
9 │ r9 h_217 c_2 2016-03-05T13:31:06 2016-03-25 09:30:00 2016-03-27 3 68400 2016-03-05 13:31:06 false true
10 │ r10 h_240 c_2 2016-06-25T09:12:22 2016-07-14 11:00:00 2016-07-17 4 320400 2016-06-25 09:12:22 false false
つづく