Featured image of post [R & SQL] データサイエンス100本ノック+α - R-058 (解説)

[R & SQL] データサイエンス100本ノック+α - R-058 (解説)

性別コードをダミー変数化

「データサイエンス100本ノック(構造化データ加工編)」の R と SQL 実装を丁寧に解説するシリーズです。 本編にはない、R によるデータベース操作の方法についても、あわせて紹介しています。

R のデータ加工は dplyr をベースとしており、データフレーム操作とデータベース操作の両面から学べる構成になっています。

RStudio や VSCode など、一般的な R の開発環境でそのまま実行できるようにしているので、実践的なスキルを身につけたい方におすすめです。

参考情報:
[R & SQL] データサイエンス100本ノック+α - 概要・導入

演習問題

難易度
顧客データ(customer)の性別コード(gender_cd)をダミー変数化し、顧客ID(customer_id)とともに10件表示せよ。

出力イメージ:

   customer_id    gender_cd_X0 gender_cd_X1 gender_cd_X9
 1 CS021313000114            0            1            0
 2 CS037613000071            0            0            1
 3 CS031415000172            0            1            0
 4 CS028811000001            0            1            0
 5 CS001215000145            0            1            0
 6 CS020401000016            1            0            0
 7 CS015414000103            0            1            0
 8 CS029403000008            1            0            0
 9 CS015804000004            1            0            0
10 CS033513000180            0            1            0

実行環境の構築について

R コード実行環境の構築に必要な手順は こちらから確認できます 。 セットアップが完了すると、必要なパッケージとデータを使用できるようになります。


解答例を以下に示します。

R (データフレーム操作)

利用するデータ

以下のデータを利用します。

  • df_customer の顧客ID (customer_id)、性別コード (gender_cd)

主要なカラムを抜き出してデータの一部を確認します。

R
df_customer %>% select(customer_id, gender_cd)
# A tibble: 21,971 × 2
   customer_id    gender_cd
   <chr>          <chr>    
 1 CS021313000114 1        
 2 CS037613000071 9        
 3 CS031415000172 1        
 4 CS028811000001 1        
 5 CS001215000145 1        
 6 CS020401000016 0        
 7 CS015414000103 1        
...

解答例と実行結果

R
df_customer %>% 
  mutate(across(gender_cd, ~ as.factor(.x))) %>% 
  recipes::recipe() %>% 
  step_select(customer_id, gender_cd) %>% 
  step_dummy(gender_cd, one_hot = TRUE) %>% 
  prep() %>% 
  bake(new_data = NULL) %>% 
  head(10)
# A tibble: 10 × 4
   customer_id    gender_cd_X0 gender_cd_X1 gender_cd_X9
   <chr>                 <dbl>        <dbl>        <dbl>
 1 CS021313000114            0            1            0
 2 CS037613000071            0            0            1
 3 CS031415000172            0            1            0
 4 CS028811000001            0            1            0
 5 CS001215000145            0            1            0
 6 CS020401000016            1            0            0
 7 CS015414000103            0            1            0
 8 CS029403000008            1            0            0
 9 CS015804000004            1            0            0
10 CS033513000180            0            1            0

解説

  • mutate(across(gender_cd, ~ as.factor(.x)))
    gender_cd因子型 (factor) に変換します。recipes::step_dummy() は因子型に対してダミー変数を作成するため、事前にこの変換が必要です。

  • recipes::recipe()
    モデル化のためのレシピオブジェクトを作成します。この時点ではまだ前処理の定義のみです。

  • step_select(customer_id, gender_cd)
    使用するカラムを customer_idgender_cd に限定しています。

  • step_dummy(gender_cd, one_hot = TRUE)
    gender_cd に対して One-Hot Encoding を適用します。one_hot = TRUE にすることで、基準カテゴリを除外せずにすべてのカテゴリのダミー変数が作られます。(デフォルトでは一つのカテゴリが除かれる。)

  • prep()
    前処理レシピを学習します。これにより、gender_cd に存在する値(例:0, 1, 9)に対応するダミー列を用意します。

  • bake(new_data = NULL)
    学習済みレシピをデータに適用して、最終的な整形済みデータを出力します。

  • head(10)
    先頭の10行を抽出します。

R (データベース操作)

利用するデータ

以下のデータを利用します。

  • db_customer の顧客ID (customer_id)、性別コード (gender_cd)

主要なカラムを抜き出してデータの一部を確認します。

R
db_customer %>% select(customer_id, gender_cd)
   customer_id    gender_cd
   <chr>          <chr>    
 1 CS021313000114 1        
 2 CS037613000071 9        
 3 CS031415000172 1        
 4 CS028811000001 1        
 5 CS001215000145 1        
 6 CS020401000016 0        
 7 CS015414000103 1        
 ...

解答例と実行結果

R
db_result = db_customer %>% 
  select(
    customer_id, gender_cd
  ) %>% 
  mutate(
    gender_cd_0 = if_else(gender_cd == "0", 1L, 0L), 
    gender_cd_1 = if_else(gender_cd == "1", 1L, 0L), 
    gender_cd_9 = if_else(gender_cd == "9", 1L, 0L), 
    .keep = "unused"
  ) %>% 
  head(10)

db_result %>% collect()
# A tibble: 10 × 4
   customer_id    gender_cd_0 gender_cd_1 gender_cd_9
   <chr>                <int>       <int>       <int>
 1 CS021313000114           0           1           0
 2 CS037613000071           0           0           1
 3 CS031415000172           0           1           0
 4 CS028811000001           0           1           0
 5 CS001215000145           0           1           0
 6 CS020401000016           1           0           0
 7 CS015414000103           0           1           0
 8 CS029403000008           1           0           0
 9 CS015804000004           1           0           0
10 CS033513000180           0           1           0

解説

  • select(customer_id, gender_cd)
    customer_idgender_cd の列を選択します。

  • mutate() によるダミー変数生成
    gender_cd の値に応じて以下の3列を作成します。

    • gender_cd_0: gender_cd == "0" のとき 1、それ以外は 0
    • gender_cd_1: gender_cd == "1" のとき 1、それ以外は 0
    • gender_cd_9: gender_cd == "9" のとき 1、それ以外は 0

    ※ gender_cd が NA の場合は NA となります。

  • .keep = "unused"
    mutate() の引数で、使い終わった gender_cd 列を自動的に削除します。

  • head(10)
    上位10行のみを取得します。

  • db_result %>% collect()
    データベース操作の結果を R のデータフレーム (tibble) として取得します。

SQL

利用するデータ

以下のデータを利用します。

  • customer テーブル の顧客ID (customer_id)、性別コード (gender_cd)

主要なカラムを抜き出してデータの一部を確認します。

R
"SELECT customer_id, gender_cd FROM customer" %>% db_get_query(con)
# A tibble: 21,971 × 2
   customer_id    gender_cd
   <chr>          <chr>    
 1 CS021313000114 1        
 2 CS037613000071 9        
 3 CS031415000172 1        
 4 CS028811000001 1        
 5 CS001215000145 1        
 6 CS020401000016 0        
 7 CS015414000103 1        
...

自動生成された SQL クエリ

データベース操作による結果 (db_result) に基づき、自動生成された SQLクエリを show_query() で確認できます。

R
db_result %>% show_query()
SQL
SELECT
  customer_id,
  CASE WHEN (gender_cd = '0') THEN 1 WHEN NOT (gender_cd = '0') THEN 0 END AS gender_cd_0,
  CASE WHEN (gender_cd = '1') THEN 1 WHEN NOT (gender_cd = '1') THEN 0 END AS gender_cd_1,
  CASE WHEN (gender_cd = '9') THEN 1 WHEN NOT (gender_cd = '9') THEN 0 END AS gender_cd_9
FROM customer
LIMIT 10

解答例

NULL を考慮した回答例は次のようになります。

SQL
SELECT
  customer_id,
  CASE 
    WHEN gender_cd = '0' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_0,
  CASE 
    WHEN gender_cd = '1' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_1,
  CASE 
    WHEN gender_cd = '9' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_9
FROM customer
LIMIT 10

解説

  • CASE WHEN gender_cd = '0' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0

    • gender_cd'0' のときは 1
    • NULL のときは NULL (欠損値を維持)
    • その他は 0
    • gender_cd_0 列に格納
  • 同様に、gender_cd_1gender_cd_9 についても、それぞれの値に基づいて 1、0、または NULL を割り当てています。

  • NULL の扱いについて

    • NULL のまま残すことで、欠損データを除外・無視することなく、後続の集計・モデリング処理で明示的に扱うことができます。

    • 条件分岐で NULL を明示的にチェックしない場合、比較演算 (=) は常に FALSE と評価されてしまうため、gender_cd IS NULL THEN NULL のような処理が必要です。

実行結果の確認

この SQLクエリの実行結果は、次のようにして確認できます。

R
query = sql("
SELECT
  customer_id,
  CASE 
    WHEN gender_cd = '0' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_0,
  CASE 
    WHEN gender_cd = '1' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_1,
  CASE 
    WHEN gender_cd = '9' THEN 1 WHEN gender_cd IS NULL THEN NULL ELSE 0 
  END AS gender_cd_9
FROM customer
LIMIT 10
"
)

query %>% db_get_query(con)
# A tibble: 10 × 4
   customer_id    gender_cd_0 gender_cd_1 gender_cd_9
   <chr>                <int>       <int>       <int>
 1 CS021313000114           0           1           0
 2 CS037613000071           0           0           1
 3 CS031415000172           0           1           0
 4 CS028811000001           0           1           0
 5 CS001215000145           0           1           0
 6 CS020401000016           1           0           0
 7 CS015414000103           0           1           0
 8 CS029403000008           1           0           0
 9 CS015804000004           1           0           0
10 CS033513000180           0           1           0