「データサイエンス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、それ以外は0gender_cd_1:gender_cd == "1"のとき1、それ以外は0gender_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 0gender_cdが'0'のときは1NULLのときは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![Featured image of post [R & SQL] データサイエンス100本ノック+α - R-058 (解説)](/images/ds-drills/free/cover-drills-free_R-058.png)