axjack's blog

axjack is said to be an abbreviation for An eXistent JApanese Cool Klutz.

データサイエンス100本ノックをRでやってみた

github.com

1日1~2時間×1週間ぐらいで終えました。

感想

  • tidyverseに感謝
  • 日付の計算はstrptimeよりlubridate使った方が簡単
  • inner_joinはbyを指定しないときnatural joinになって便利
  • 正規表現を使う問題はstr_subやstr_detectを用いても代替可能(問題の趣旨に反するが・・・。)
  • slice_*系でデータ取り出し、が失敗することもあるのでそんな時はheadを利用

つまずいた問題たち

供養と復習を兼ねて書いておきます。どの問題も実用的なのでスラスラと書けるようになりたいですね。

R-029

R-029: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに商品コード(product_cd)の最頻値を求めよ。

最頻値ってどうやって出すのっていうか最頻値出す関数無くない?と一時停止した問題です。inner_joinを駆使してなんとかひねり出した自分の答えに対してAnswerの解法はエレガントでした。勉強も兼ねてrecodeしたものが以下です。

df_receipt %>%
  group_by(store_cd, product_cd) %>%
  summarise(cnt = n(), .groups = 'drop') %>%
  group_by(store_cd) %>%
  filter( cnt == max(cnt) ) # Ans の n == n %>% max() は、 n == max(n) と同じ。

R-058

R-058: 顧客データフレーム(df_customer)の性別コード(gender_cd)をダミー変数化し、顧客ID(customer_id)とともに抽出せよ。結果は10件表示させれば良い。

ダミー変数化って要するにどうなるんだっけ?と思考停止した問題です。変数の取りうる値の範囲が事前に分かっていればdecodeすれば良いのだけど、そうではない時にはAnswerにある通りdummyVars()を使うそうです。

# ダミー変数化がわからなかったのでAnsを参照した。
# すると、gender_cd = {0,1,9} を gender_cd0,gender_cd1,gender_cd9と3つの変数に分離することらしい。
# 横持ち形式ということかなぁ。
# で、dummyVars()というのは{caret}パッケージの関数であるもよう。

# if_else で書けるといえば書けるのだが、
# よく考えると「取りうる値」が事前に分かっていないとコーディングできないね。
# ということで、「取りうる値」が分からない場合はdummyVars()を使うしかないわね。
df_customer %>% 
    mutate(
        gender_cd0 = if_else(gender_cd == 0,1,0)
        ,gender_cd1 = if_else(gender_cd == 1,1,0)
        ,gender_cd9 = if_else(gender_cd == 9,1,0)
        ) %>%
    select(customer_id, starts_with("gender")) %>%
    slice(1:10)

R-087

R-087: 顧客データフレーム(df_customer)では、異なる店舗での申込みなどにより同一顧客が複数登録されている。名前(customer_name)と郵便番号(postal_cd)が同じ顧客は同一顧客とみなし、1顧客1レコードとなるように名寄せした名寄顧客データフレーム(df_customer_u)を作成せよ。ただし、同一顧客に対しては売上金額合計が最も高いものを残すものとし、売上金額合計が同一もしくは売上実績の無い顧客については顧客ID(customer_id)の番号が小さいものを残すこととする。

SQLだったらwindow関数とpartition by でうまくいくのになぁとモヤモヤした問題です。Answerはとてもエレガントでdistinct関数をうまく利用した解法です。自分で出した解答は確かに問題文の題意をそのまま愚直に書き下した感がありエレガントではないでした。一番悔しかった問題です。

df_receipt %>% 
    group_by(customer_id) %>% 
    summarise(sum_amount = sum(amount) , .groups = 'drop' ) -> dfr

df_customer %>% left_join(dfr) %>% 
    mutate(sum_amount = replace_na(sum_amount,0) ) %>%
    group_by(customer_name, postal_cd) %>%
    mutate(
        rank_amount = min_rank(desc(sum_amount))
        ,rank_cust = min_rank(customer_id)
        ,num_amount = n_distinct(sum_amount) # 売上金額合計の種類の数
    ) %>%
    filter(  ( (num_amount > 1) & (rank_amount == 1) ) | ( (num_amount == 1)&( rank_cust == 1 )   )  ) %>%
    select(-rank_amount,-rank_cust,-num_amount) -> df_customer_u2
axjack is said to be an abbreviation for An eXistent JApanese Cool Klutz.