「データサイエンス100本ノック(構造化データ加工編)」の R と SQL 実装を丁寧に解説するシリーズです。 本編にはない、R によるデータベース操作の方法についても、あわせて紹介しています。
R のデータ加工は dplyr をベースとしており、データフレーム操作とデータベース操作の両面から学べる構成になっています。
RStudio や VSCode など、一般的な R の開発環境でそのまま実行できるようにしているので、実践的なスキルを身につけたい方におすすめです。
参考情報:
[R & SQL] データサイエンス100本ノック+α - 概要・導入
- 設問文は データサイエンス100本ノック(構造化データ加工編) より引用しています。
- すべてのコードはコピー&ペーストでそのまま実行できます。
演習問題
出力イメージ:
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
)
主要なカラムを抜き出してデータの一部を確認します。
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
...
解答例と実行結果
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_id
とgender_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
)
主要なカラムを抜き出してデータの一部を確認します。
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
...
解答例と実行結果
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_id
とgender_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
)
主要なカラムを抜き出してデータの一部を確認します。
"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()
で確認できます。
db_result %>% show_query()
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 を考慮した回答例は次のようになります。
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_1
とgender_cd_9
についても、それぞれの値に基づいて 1、0、またはNULL
を割り当てています。NULL の扱いについて
NULL
のまま残すことで、欠損データを除外・無視することなく、後続の集計・モデリング処理で明示的に扱うことができます。条件分岐で
NULL
を明示的にチェックしない場合、比較演算 (=
) は常にFALSE
と評価されてしまうため、gender_cd IS NULL THEN NULL
のような処理が必要です。
実行結果の確認
この SQLクエリの実行結果は、次のようにして確認できます。
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