## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
library(arrow)
library(slider) #ウィンドウ処理に便利なパッケージ
# ファイルの読み込み
hotel <- read_parquet("https://github.com/ghmagazine/awesomebook_v2/raw/main/data/hotel.parquet")
reservation <- read_parquet("https://github.com/ghmagazine/awesomebook_v2/raw/main/data/reservation.parquet")
# (2) hotelに(1)の結果を左結合
hotel |>
# (1)address_prefectureとaddress_cityごとにunit_priceの平均を算出
left_join(
hotel |>
group_by(address_prefecture, address_city) |>
summarise(avg_price_within_city = mean(unit_price, na.rm = TRUE)),
by = c("address_prefecture", "address_city")
)
## `summarise()` has grouped output by 'address_prefecture'. You can override
## using the `.groups` argument.
## # A tibble: 5,000 × 40
## hotel_id hotel_name hotel_type address_prefecture address_city address_town
## <int> <chr> <chr> <chr> <chr> <chr>
## 1 1 北飯岡ホテル ビジネス… 岩手県 盛岡市 北飯岡
## 2 2 西二条南温… リゾート… 北海道 中川郡美深町 西二条南
## 3 3 小屋敷ペン… 民宿 青森県 黒石市 小屋敷
## 4 4 中後町民宿 民宿 愛知県 碧南市 中後町
## 5 5 鵜沼台ホテル ビジネス… 岐阜県 各務原市 鵜沼台
## 6 6 新町ホテル ビジネス… 熊本県 宇土市 新町
## 7 7 七ツ山リゾ… リゾート… 宮崎県 東臼杵郡諸… 七ツ山
## 8 8 大山町ペン… 民宿 神奈川県 相模原市緑区 大山町
## 9 9 新道ホテル ビジネス… 北海道 上磯郡木古… 新道
## 10 10 下奈良上ノ… 旅館 京都府 八幡市 下奈良上ノ段
## # ℹ 4,990 more rows
## # ℹ 34 more variables: address_zipcode <chr>, unit_price <int>,
## # user_rating <dbl>, tag_001 <int>, tag_002 <int>, tag_003 <int>,
## # tag_004 <int>, tag_005 <int>, tag_006 <int>, tag_007 <int>, tag_008 <int>,
## # tag_009 <int>, tag_010 <int>, tag_011 <int>, tag_012 <int>, tag_013 <int>,
## # tag_014 <int>, tag_015 <int>, tag_016 <int>, tag_017 <int>, tag_018 <int>,
## # tag_019 <int>, tag_020 <int>, tag_021 <int>, tag_022 <int>, …
hotel |>
group_by(address_prefecture, address_city) |>
mutate(avg_price_within_city = mean(unit_price, na.rm = TRUE)) |>
ungroup()
## # A tibble: 5,000 × 40
## hotel_id hotel_name hotel_type address_prefecture address_city address_town
## <int> <chr> <chr> <chr> <chr> <chr>
## 1 1 北飯岡ホテル ビジネス… 岩手県 盛岡市 北飯岡
## 2 2 西二条南温… リゾート… 北海道 中川郡美深町 西二条南
## 3 3 小屋敷ペン… 民宿 青森県 黒石市 小屋敷
## 4 4 中後町民宿 民宿 愛知県 碧南市 中後町
## 5 5 鵜沼台ホテル ビジネス… 岐阜県 各務原市 鵜沼台
## 6 6 新町ホテル ビジネス… 熊本県 宇土市 新町
## 7 7 七ツ山リゾ… リゾート… 宮崎県 東臼杵郡諸… 七ツ山
## 8 8 大山町ペン… 民宿 神奈川県 相模原市緑区 大山町
## 9 9 新道ホテル ビジネス… 北海道 上磯郡木古… 新道
## 10 10 下奈良上ノ… 旅館 京都府 八幡市 下奈良上ノ段
## # ℹ 4,990 more rows
## # ℹ 34 more variables: address_zipcode <chr>, unit_price <int>,
## # user_rating <dbl>, tag_001 <int>, tag_002 <int>, tag_003 <int>,
## # tag_004 <int>, tag_005 <int>, tag_006 <int>, tag_007 <int>, tag_008 <int>,
## # tag_009 <int>, tag_010 <int>, tag_011 <int>, tag_012 <int>, tag_013 <int>,
## # tag_014 <int>, tag_015 <int>, tag_016 <int>, tag_017 <int>, tag_018 <int>,
## # tag_019 <int>, tag_020 <int>, tag_021 <int>, tag_022 <int>, …
hotel |>
summarise(hotel_cnt = n(), .by = address_prefecture) |>
mutate(ratio = hotel_cnt / sum(hotel_cnt))
## # A tibble: 47 × 3
## address_prefecture hotel_cnt ratio
## <chr> <int> <dbl>
## 1 岩手県 70 0.014
## 2 北海道 365 0.073
## 3 青森県 83 0.0166
## 4 愛知県 318 0.0636
## 5 岐阜県 130 0.026
## 6 熊本県 64 0.0128
## 7 宮崎県 41 0.0082
## 8 神奈川県 114 0.0228
## 9 京都府 261 0.0522
## 10 福井県 69 0.0138
## # ℹ 37 more rows
reservation |>
group_by(customer_id) |>
mutate(reservation_no = dense_rank(reserved_at)) |>
ungroup()
## # A tibble: 2,000,000 × 12
## reservation_id hotel_id customer_id reserved_at checkin_date
## <int> <int> <int> <dttm> <dttm>
## 1 1 2460 53431 2013-12-31 07:00:14 2014-12-31 00:00:00
## 2 2 962 488390 2013-12-31 08:23:35 2014-12-31 00:00:00
## 3 3 558 341335 2013-12-31 09:02:05 2014-12-31 00:00:00
## 4 4 3666 398981 2013-12-31 23:44:54 2014-12-31 00:00:00
## 5 5 2180 220381 2014-01-01 02:47:50 2014-12-31 00:00:00
## 6 6 974 1494 2014-01-01 07:56:58 2015-01-01 00:00:00
## 7 7 2260 24104 2014-01-01 13:17:06 2014-12-30 00:00:00
## 8 8 314 124883 2014-01-01 14:22:01 2014-12-31 00:00:00
## 9 9 2211 45282 2014-01-01 14:59:04 2014-12-31 00:00:00
## 10 10 333 390595 2014-01-01 20:24:34 2014-12-30 00:00:00
## # ℹ 1,999,990 more rows
## # ℹ 7 more variables: checkout_date <dttm>, length_of_stay <int>,
## # people_num <int>, total_price <int>, status <chr>, canceled_at <dttm>,
## # reservation_no <int>
移動平均を算出するにはいくつかの方法がある。ここでは以下の三つのやり方を紹介する。
# 必要なパッケージを読み込み
library(zoo) #rollmean
reservation |>
# (1) キャンセルを除去
dplyr::filter(status != "canceled") |>
# (2) checkout_dateを月周期のPeriodに変換
mutate(month = floor_date(as_date(checkout_date), "month")) |>
# (3) monthごとにtotal_priceの総和を計算(集計結果はmonth順に並ぶ)
summarise(sales = sum(total_price, na.rm = TRUE), .by =month) |>
arrange(month) |>
# (4) salesの直近3行分の平均を計算
mutate(moving_average_sales = rollmean(sales, 3, fill = NA, align = "right"))
## # A tibble: 60 × 3
## month sales moving_average_sales
## <date> <dbl> <dbl>
## 1 2015-01-01 730844200 NA
## 2 2015-02-01 650411000 NA
## 3 2015-03-01 1447995200 943083467.
## 4 2015-04-01 697578500 931994900
## 5 2015-05-01 2185718000 1443763900
## 6 2015-06-01 697210900 1193502467.
## 7 2015-07-01 1465165300 1449364733.
## 8 2015-08-01 2910478200 1690951467.
## 9 2015-09-01 708236200 1694626567.
## 10 2015-10-01 733080000 1450598133.
## # ℹ 50 more rows
# 必要なパッケージを読み込み
library(RcppRoll) #rollmean
reservation |>
# (1) キャンセルを除去
dplyr::filter(status != "canceled") |>
# (2) checkout_dateを月周期のPeriodに変換
mutate(month = floor_date(as_date(checkout_date), "month")) |>
# (3) monthごとにtotal_priceの総和を計算(集計結果はmonth順に並ぶ)
summarise(sales = sum(total_price, na.rm = TRUE), .by =month) |>
arrange(month) |>
# (4) salesの直近3行分の平均を計算
mutate(moving_average_sales = roll_mean(sales, n = 3, fill = NA, align = "right"))
## # A tibble: 60 × 3
## month sales moving_average_sales
## <date> <dbl> <dbl>
## 1 2015-01-01 730844200 NA
## 2 2015-02-01 650411000 NA
## 3 2015-03-01 1447995200 943083467.
## 4 2015-04-01 697578500 931994900
## 5 2015-05-01 2185718000 1443763900
## 6 2015-06-01 697210900 1193502467.
## 7 2015-07-01 1465165300 1449364733.
## 8 2015-08-01 2910478200 1690951467.
## 9 2015-09-01 708236200 1694626567.
## 10 2015-10-01 733080000 1450598133.
## # ℹ 50 more rows
# 必要なパッケージを読み込み
library(slider)
tictoc::tic()
reservation |>
# (1) キャンセルを除去
dplyr::filter(status != "canceled") |>
# (2) checkout_dateを月周期のPeriodに変換
mutate(month = floor_date(as_date(checkout_date), "month")) |>
# (3) monthごとにtotal_priceの総和を計算(集計結果はmonth順に並ぶ)
summarise(sales = sum(total_price, na.rm = TRUE), .by =month) |>
arrange(month) |>
# (4) salesの直近3行分の平均を計算
mutate(moving_average_sales = slide_vec(.x = sales, .f = mean, .before = 2, .complete = TRUE))
## # A tibble: 60 × 3
## month sales moving_average_sales
## <date> <dbl> <dbl>
## 1 2015-01-01 730844200 NA
## 2 2015-02-01 650411000 NA
## 3 2015-03-01 1447995200 943083467.
## 4 2015-04-01 697578500 931994900
## 5 2015-05-01 2185718000 1443763900
## 6 2015-06-01 697210900 1193502467.
## 7 2015-07-01 1465165300 1449364733.
## 8 2015-08-01 2910478200 1690951467.
## 9 2015-09-01 708236200 1694626567.
## 10 2015-10-01 733080000 1450598133.
## # ℹ 50 more rows
## 7.25 sec elapsed
reservation |>
#(1)キャンセル済みのデータを除去
dplyr::filter(status != "canceled") |>
#(2)reserved_atの昇順(時系列順)にソート
arrange(reserved_at) |>
mutate(
#(3)customer_idごとにtotal_priceを時系列順に1行分ずらす
#(4)customer_idごとにprev_total_priceの累計和を計算
past_total_spent = total_price |>
dplyr::lag(n = 1) |>
coalesce(0) |>
cumsum(),
.by = customer_id
)
## # A tibble: 1,799,589 × 12
## reservation_id hotel_id customer_id reserved_at checkin_date
## <int> <int> <int> <dttm> <dttm>
## 1 1 2460 53431 2013-12-31 07:00:14 2014-12-31 00:00:00
## 2 2 962 488390 2013-12-31 08:23:35 2014-12-31 00:00:00
## 3 3 558 341335 2013-12-31 09:02:05 2014-12-31 00:00:00
## 4 4 3666 398981 2013-12-31 23:44:54 2014-12-31 00:00:00
## 5 5 2180 220381 2014-01-01 02:47:50 2014-12-31 00:00:00
## 6 6 974 1494 2014-01-01 07:56:58 2015-01-01 00:00:00
## 7 7 2260 24104 2014-01-01 13:17:06 2014-12-30 00:00:00
## 8 8 314 124883 2014-01-01 14:22:01 2014-12-31 00:00:00
## 9 9 2211 45282 2014-01-01 14:59:04 2014-12-31 00:00:00
## 10 10 333 390595 2014-01-01 20:24:34 2014-12-30 00:00:00
## # ℹ 1,799,579 more rows
## # ℹ 7 more variables: checkout_date <dttm>, length_of_stay <int>,
## # people_num <int>, total_price <int>, status <chr>, canceled_at <dttm>,
## # past_total_spent <dbl>
総和を求めてから対象行の金額を引く方法。こっちの方が圧倒的に速い。
reservation |>
# (1) キャンセル済みのデータを除去
dplyr::filter(status != "canceled") |>
# (2) reserved_atの昇順(時系列順)にソート
arrange(reserved_at) |>
# (3) customer_idごとにtotal_priceの累計和を計算し、現在の行の値を減算
mutate(
past_total_spent = cumsum(total_price) - total_price,
.by = customer_id
)
## # A tibble: 1,799,589 × 12
## reservation_id hotel_id customer_id reserved_at checkin_date
## <int> <int> <int> <dttm> <dttm>
## 1 1 2460 53431 2013-12-31 07:00:14 2014-12-31 00:00:00
## 2 2 962 488390 2013-12-31 08:23:35 2014-12-31 00:00:00
## 3 3 558 341335 2013-12-31 09:02:05 2014-12-31 00:00:00
## 4 4 3666 398981 2013-12-31 23:44:54 2014-12-31 00:00:00
## 5 5 2180 220381 2014-01-01 02:47:50 2014-12-31 00:00:00
## 6 6 974 1494 2014-01-01 07:56:58 2015-01-01 00:00:00
## 7 7 2260 24104 2014-01-01 13:17:06 2014-12-30 00:00:00
## 8 8 314 124883 2014-01-01 14:22:01 2014-12-31 00:00:00
## 9 9 2211 45282 2014-01-01 14:59:04 2014-12-31 00:00:00
## 10 10 333 390595 2014-01-01 20:24:34 2014-12-30 00:00:00
## # ℹ 1,799,579 more rows
## # ℹ 7 more variables: checkout_date <dttm>, length_of_stay <int>,
## # people_num <int>, total_price <int>, status <chr>, canceled_at <dttm>,
## # past_total_spent <int>
これはsliderパッケージのslider::slide_*()関数が便利。
reservation |>
dplyr::filter(customer_id < 10) |>
mutate(reserved_at_date = as_date(reserved_at)) |>
# (1) キャンセル済みのデータを除去
dplyr::filter(status != "canceled") %>%
# (2) reserved_atの昇順(時系列順)にソート
arrange(reserved_at) %>%
# (3)
mutate(
total_spent_last_90days = slide_period_dbl(
.x = total_price, # 累積合計を計算する変数
.i = reserved_at_date, # インデックス変数
.period = "day", # 期間の単位 ("day", "month", "year"など)
.before = 90L, # 過去90日間の期間
.complete = FALSE, # 完全な期間でない場合でも計算を行う
~ sum(.x, na.rm = TRUE) # 関数: ここで累積合計が計算される
) - total_price,
.by = customer_id
)
## # A tibble: 47 × 13
## reservation_id hotel_id customer_id reserved_at checkin_date
## <int> <int> <int> <dttm> <dttm>
## 1 12086 1282 1 2014-04-16 13:39:41 2015-03-03 00:00:00
## 2 19584 2498 2 2014-05-12 23:16:19 2015-03-15 00:00:00
## 3 128306 2889 4 2014-10-26 00:02:54 2015-08-01 00:00:00
## 4 159766 1454 7 2014-11-28 01:33:41 2015-11-14 00:00:00
## 5 178009 848 9 2014-12-15 21:34:58 2015-07-01 00:00:00
## 6 192936 58 6 2014-12-29 18:40:24 2015-07-09 00:00:00
## 7 225337 2977 6 2015-01-28 03:02:40 2015-09-23 00:00:00
## 8 244272 177 7 2015-02-14 08:00:28 2015-08-16 00:00:00
## 9 335066 2462 7 2015-05-07 14:26:31 2015-12-28 00:00:00
## 10 348951 1219 2 2015-05-20 13:27:27 2015-08-03 00:00:00
## # ℹ 37 more rows
## # ℹ 8 more variables: checkout_date <dttm>, length_of_stay <int>,
## # people_num <int>, total_price <int>, status <chr>, canceled_at <dttm>,
## # reserved_at_date <date>, total_spent_last_90days <dbl>
普通に考えるとslice_min()を使いたくなるが、これは実装の問題で処理が遅い。
reservation |>
select(customer_id, reserved_at, hotel_id) |>
group_by(customer_id) |>
slice_min(reserved_at, n = 1)
## # A tibble: 417,368 × 3
## # Groups: customer_id [417,368]
## customer_id reserved_at hotel_id
## <int> <dttm> <int>
## 1 1 2014-04-16 13:39:41 1282
## 2 2 2014-05-12 23:16:19 2498
## 3 3 2016-02-13 14:07:40 1649
## 4 4 2014-10-26 00:02:54 2889
## 5 5 2016-05-06 11:31:47 3306
## 6 6 2014-12-29 18:40:24 58
## 7 7 2014-11-28 01:33:41 1454
## 8 8 2015-10-10 14:45:54 4341
## 9 9 2014-12-15 21:34:58 848
## 10 10 2018-08-31 06:45:30 3111
## # ℹ 417,358 more rows
予約順の番号を付与して、1番を抽出する方が処理が速い。
reservation |>
# (1) 顧客ごとに予約順の番号を付与
group_by(customer_id) |>
mutate(rn = dense_rank(reserved_at)) |>
# (2) 顧客の最初の予約のみ抽出
dplyr::filter(rn == 1) |>
select(customer_id, reserved_at, hotel_id)
## # A tibble: 417,368 × 3
## # Groups: customer_id [417,368]
## customer_id reserved_at hotel_id
## <int> <dttm> <int>
## 1 53431 2013-12-31 07:00:14 2460
## 2 488390 2013-12-31 08:23:35 962
## 3 341335 2013-12-31 09:02:05 558
## 4 398981 2013-12-31 23:44:54 3666
## 5 220381 2014-01-01 02:47:50 2180
## 6 1494 2014-01-01 07:56:58 974
## 7 24104 2014-01-01 13:17:06 2260
## 8 124883 2014-01-01 14:22:01 314
## 9 45282 2014-01-01 14:59:04 2211
## 10 390595 2014-01-01 20:24:34 333
## # ℹ 417,358 more rows
10%のデータをランダムサンプリング
## # A tibble: 500 × 39
## hotel_id hotel_name hotel_type address_prefecture address_city address_town
## <int> <chr> <chr> <chr> <chr> <chr>
## 1 4571 光珠内東山… 旅館 北海道 美唄市 光珠内東山
## 2 1614 三ツ橋ペン… 民宿 新潟県 上越市 三ツ橋
## 3 1538 豊玉町志多… 旅館 長崎県 対馬市 豊玉町志多浦
## 4 3104 こも原町ビ… ビジネス… 愛知県 名古屋市西区 こも原町
## 5 3303 北方町板上… リゾート… 宮崎県 延岡市 北方町板上
## 6 3658 水橋市江旅館 旅館 富山県 富山市 水橋市江
## 7 1541 上の原ホテル リゾート… 東京都 東久留米市 上の原
## 8 2726 大篠ペンシ… 民宿 岡山県 津山市 大篠
## 9 3560 小平旅館 旅館 宮城県 亘理郡山元町 小平
## 10 2704 中津原ホテル ビジネス… 新潟県 村上市 中津原
## # ℹ 490 more rows
## # ℹ 33 more variables: address_zipcode <chr>, unit_price <int>,
## # user_rating <dbl>, tag_001 <int>, tag_002 <int>, tag_003 <int>,
## # tag_004 <int>, tag_005 <int>, tag_006 <int>, tag_007 <int>, tag_008 <int>,
## # tag_009 <int>, tag_010 <int>, tag_011 <int>, tag_012 <int>, tag_013 <int>,
## # tag_014 <int>, tag_015 <int>, tag_016 <int>, tag_017 <int>, tag_018 <int>,
## # tag_019 <int>, tag_020 <int>, tag_021 <int>, tag_022 <int>, …
address_prefecture列の値ごとに10%のデータをランダムサンプリング
## # A tibble: 483 × 39
## # Groups: address_prefecture [47]
## hotel_id hotel_name hotel_type address_prefecture address_city address_town
## <int> <chr> <chr> <chr> <chr> <chr>
## 1 2376 坂本旅館 旅館 三重県 多気郡明和町 坂本
## 2 4051 藤原町石川… 民宿 三重県 いなべ市 藤原町石川
## 3 4293 薦生温泉ホ… リゾートホ… 三重県 名張市 薦生
## 4 930 星見ケ丘温… 旅館 三重県 桑名市 星見ケ丘
## 5 1820 掛樋温泉ホ… リゾートホ… 三重県 桑名市 掛樋
## 6 197 長深ホテル ビジネスホ… 三重県 員弁郡東員町 長深
## 7 2435 神島町ビジ… ビジネスホ… 三重県 鳥羽市 神島町
## 8 3569 佐田ビジネ… ビジネスホ… 三重県 度会郡玉城町 佐田
## 9 212 田中大堰町… 旅館 京都府 京都市左京区 田中大堰町
## 10 94 北町温泉ホ… リゾートホ… 京都府 京都市下京区 北町
## # ℹ 473 more rows
## # ℹ 33 more variables: address_zipcode <chr>, unit_price <int>,
## # user_rating <dbl>, tag_001 <int>, tag_002 <int>, tag_003 <int>,
## # tag_004 <int>, tag_005 <int>, tag_006 <int>, tag_007 <int>, tag_008 <int>,
## # tag_009 <int>, tag_010 <int>, tag_011 <int>, tag_012 <int>, tag_013 <int>,
## # tag_014 <int>, tag_015 <int>, tag_016 <int>, tag_017 <int>, tag_018 <int>,
## # tag_019 <int>, tag_020 <int>, tag_021 <int>, tag_022 <int>, …
20000件をランダムサンプリング
## # A tibble: 20,000 × 11
## reservation_id hotel_id customer_id reserved_at checkin_date
## <int> <int> <int> <dttm> <dttm>
## 1 1471710 2763 390797 2018-03-11 16:01:44 2018-10-06 00:00:00
## 2 886330 3850 427801 2016-09-23 10:43:16 2017-06-27 00:00:00
## 3 451908 2521 185794 2015-08-22 23:57:39 2015-12-26 00:00:00
## 4 1474357 1522 497835 2018-03-14 02:08:36 2018-12-01 00:00:00
## 5 1660033 777 420582 2018-08-30 02:56:50 2019-08-27 00:00:00
## 6 30143 2362 405702 2014-06-07 13:18:50 2015-03-26 00:00:00
## 7 1840659 650 219057 2019-02-12 02:11:04 2019-08-04 00:00:00
## 8 892152 1196 363390 2016-09-28 19:17:02 2017-03-05 00:00:00
## 9 289641 3041 454994 2015-03-27 04:10:09 2015-10-31 00:00:00
## 10 1186405 2805 301936 2017-06-24 03:22:36 2017-12-28 00:00:00
## # ℹ 19,990 more rows
## # ℹ 6 more variables: checkout_date <dttm>, length_of_stay <int>,
## # people_num <int>, total_price <int>, status <chr>, canceled_at <dttm>
# (1)顧客をサンプリング
sampled_customers <- reservation |>
distinct(customer_id) |>
slice_sample(prop = 0.01)
# (2) サンプリングした顧客の予約を抽出する
reservation |>
dplyr::filter(customer_id %in% sampled_customers$customer_id)|>
summarise(n = n(), .by = customer_id) |>
summarise(mean = mean(n))
## # A tibble: 1 × 1
## mean
## <dbl>
## 1 4.86
以上です。