📘

データサイエンス100本ノック(SQL)81~90

2021/12/02に公開

追記
最近になってGCPのBigQueryでも回答を作ってみたので合わせてそのコードも記載する
基本的にPostgreSQLの下にGCPのコードを記していく


S-081:単価(unit_price)と原価(unit_cost)の欠損値について、
     それぞれの平均値で補完した新たなproduct_2を作成せよ。
    なお、平均値について1円未満は四捨五入とする。
    補完実施後、各項目について欠損が生じていないことも確認すること。

PostgreSQL
 drop table if exists product_2;
 create table if not exists product_2 as
                                         with unit_avg as (
                                                           select round(avg(unit_price)) as u_p_avg,
                                                                  round(avg(unit_cost)) as u_c_avg
                                                           from product
                                                          )
                                         select product_cd,
                                                category_major_cd,
                                                category_medium_cd,
                                                category_small_cd,
                                                coalesce(unit_price, u_p_avg) as unit_price, 
                                                coalesce(unit_cost, u_c_avg) as unit_cost
                                         from product
                                         cross join unit_avg;

 #平均値算出 --u_p_avg:403、u_c_avg:302
 select (round(avg(unit_price))) as u_p_avg,
        (round(avg(unit_cost))) as u_c_avg
 from product;

 #欠損値確認
 select * from product_2 where product_cd = 'P040802007';

BigQuery
 drop table if exists [データセット名].product_2;
 create table if not exists [データセット名].product_2 as
                                                         with unit_avg as (
                                                                           select round(avg(unit_price)) as u_p_avg,
                                                                                  round(avg(unit_cost)) as u_c_avg
                                                                           from [データセット名].product
                                                                          )
                                                         select product_cd,
                                                                category_major_cd,
                                                                category_medium_cd,
                                                                category_small_cd,
                                                                coalesce(unit_price, u_p_avg) as unit_price, 
                                                                coalesce(unit_cost, u_c_avg) as unit_cost
                                                         from [データセット名].product
                                                         cross join unit_avg; 

 select * 
 from [データセット名].product_2
 where unit_price is null or
       unit_cost is null;

--create table if not exists【テーブル名】as:その【テーブル名】が存在しない場合に新たに作成


S-082:単価(unit_price)と原価(unit_cost)の欠損値について、
     それぞれの中央値で補完した新たなproduct_3を作成せよ。
    なお、中央値について1円未満は四捨五入とする。
    補完実施後、各項目について欠損が生じていないことも確認すること。

PostgreSQL
 drop table if exists product_3;
 create table if not exists product_3 as
                                         with avg_ as (
                                                       select round(percentile_cont(0.5) within group (order by unit_price)) as med_u_p, 
                                                              round(percentile_cont(0.5) within group (order by unit_cost)) as med_u_c
                                                       from product
                                                       )
                                         select product_cd,
                                                category_major_cd,
                                                category_medium_cd,
                                                category_small_cd, 
                                                coalesce(unit_price, med_u_p) as unit_price, 
                                                coalesce(unit_cost, med_u_c) as unit_cost
                                         from product
                                         cross join avg_;

 --u_p_med:252、u_c_med:189
 select round(percentile_cont(0.5) within group (order by unit_price)) as med_u_p, 
	round(percentile_cont(0.5) within group (order by unit_cost)) as med_u_c 
 from product;

 select * from product_3 where product_cd = 'P040802007';

BigQuery
 drop table if exists [データセット名].product_3;
 create table if not exists [データセット名].product_3 as
                                                         with avg_ as (
                                                                       select round(percentile_cont(unit_price, 0.5) over()) as med_u_p, 
                                                                              round(percentile_cont(unit_cost, 0.5) over()) as med_u_c
                                                                       from [データセット名].product
                                                                      )
                                                         select product_cd,
                                                                category_major_cd,
                                                                category_medium_cd,
                                                                category_small_cd, 
                                                                coalesce(unit_price, med_u_p) as unit_price, 
                                                                coalesce(unit_cost, med_u_c) as unit_cost
                                                         from [データセット名].product
                                                         cross join avg_;

S-083:単価(unit_price)と原価(unit_cost)の欠損値について、
     各商品の小区分(category_small_cd)ごとに算出した中央値で
     補完した新たなproduct_4を作成せよ。
    なお、中央値について1円未満は四捨五入とする。
    補完実施後、各項目について欠損が生じていないことも確認すること。

PostgreSQL
 drop table if exists product_4;
 create table if not exists product_4 as 
                                         with csc_med as
                                                         (
                                                          select category_small_cd,
                                                                 round(percentile_cont(0.5) within group (order by unit_price)) as csc_up_med,
                                                                 round(percentile_cont(0.5) within group (order by unit_cost)) as csc_uc_med
                                                          from product
                                                          group by category_small_cd
                                                         )
                                         select product_cd,
                                                category_major_cd,
                                                category_medium_cd,
                                                pr.category_small_cd, 
                                                coalesce(unit_price, csc_up_med) as unit_price, 
                                                coalesce(unit_cost, csc_uc_med) as unit_cost 
                                         from product as pr
                                         inner join csc_med as c_m
                                         on pr.category_small_cd = c_m.category_small_cd;

BigQuery
 drop table if exists [データセット名].product_4;
 create table if not exists [データセット名].product_4 as 
                                                         with csc_med as
                                                                         (
                                                                          select distinct category_small_cd,
                                                                                          round(percentile_cont(unit_price, 0.5) over(partition by category_small_cd)) as csc_up_med,
                                                                                          round(percentile_cont(unit_cost, 0.5) over(partition by category_small_cd)) as csc_uc_med
                                                                          from [データセット名].product
                                                                         )
                                                         select product_cd,
                                                                category_major_cd,
                                                                category_medium_cd,
                                                                pr.category_small_cd, 
                                                                coalesce(unit_price, csc_up_med) as unit_price, 
                                                                coalesce(unit_cost, csc_uc_med) as unit_cost 
                                                         from [データセット名].product as pr
                                                         inner join csc_med as c_m
                                                         on pr.category_small_cd = c_m.category_small_cd
                                                         order by product_cd asc;
 select category_small_cd,
       round(percentile_cont(0.5) within group (order by unit_price)) as csc_up_med,
       round(percentile_cont(0.5) within group (order by unit_cost)) as csc_uc_med
 from product
 group by category_small_cd;

/*
'P040802007'  040802: csc_up_med:313、csc_uc_med:235
'P050103021'  050103: csc_up_med:132、csc_uc_med:100
'P050405009'  050405: csc_up_med:178、csc_uc_med:134
'P060802026'  060802: csc_up_med:270、csc_uc_med:200
'P070202092'  070202: csc_up_med:238、csc_uc_med:179
'P080504027'  080504: csc_up_med:258、csc_uc_med:196
'P090204185'  090204: csc_up_med:694、csc_uc_med:521
*/
 select * from product_4 where product_cd = 'P040802007';

S-084:顧客テーブル(customer)の全顧客に対し、
     全期間の売上金額に占める2019年売上金額の割合を計算せよ。
   ただし、販売実績のない場合は0として扱うこと。
   そして計算した割合が0超のものを抽出せよ。 結果は10件表示させれば良い。

PostgreSQL
 with sales_amount_all as (
                           select cu.customer_id,
                                  sum(r.amount)
                           from customer as cu
                           inner join receipt as r
                           on cu.customer_id = r.customer_id
                           group by cu.customer_id
                          ),
      sales_amount_2019 as (
                            select cu.customer_id,
                                   sum(r.amount)
                            from customer as cu
                            inner join receipt as r
                            on cu.customer_id = r.customer_id
                            where 20190000 < r.sales_ymd and r.sales_ymd < 20191231
                            group by cu.customer_id
                           )
 select sales_all.customer_id,
        sales_2019.sum as sales_amount_2019,
        sales_all.sum as sales_amount,
        sales_2019.sum * 1.0 / sales_all.sum * 1.0 as percentage 
 from sales_amount_all as sales_all
 inner join sales_amount_2019 as sales_2019
 on sales_all.customer_id = sales_2019.customer_id
 limit 10;

BigQuery
 with sales_amount_all as (
                           select cu.customer_id,
                                  sum(r.amount) as sum
                           from [データセット名].customer as cu
                           inner join [データセット名].receipt as r
                           on cu.customer_id = r.customer_id
                           group by cu.customer_id
                          ),
      sales_amount_2019 as (
                            select cu.customer_id,
                                   sum(r.amount) as sum
                            from [データセット名].customer as cu
                            inner join [データセット名].receipt as r
                            on cu.customer_id = r.customer_id
                            where 20190000 < r.sales_ymd and r.sales_ymd < 20191231
                            group by cu.customer_id
                           )
 select sales_all.customer_id,
        sales_2019.sum as sales_amount_2019,
        sales_all.sum as sales_amount,
        sales_2019.sum * 1.0 / sales_all.sum * 1.0 as percentage 
 from sales_amount_all as sales_all
 inner join sales_amount_2019 as sales_2019
 on sales_all.customer_id = sales_2019.customer_id
 limit 10;

S-085:顧客テーブル(customer)の全顧客に対し、郵便番号(postal_cd)を用いて
     経度緯度変換用テーブル(geocode)を紐付け、新たなcustomer_1を作成せよ。
    ただし、複数紐づく場合は
     経度(longitude)、緯度(latitude)それぞれ平均を算出すること。

PostgreSQL
 drop table if exists customer_1;
 create table if not exists customer_1 as
                                          with avg_ge as (
                                                          select postal_cd,
                                                                 avg(longitude) as m_longitude,
                                                                 avg(latitude) as m_latitude
                                                          from geocode
                                                          group by postal_cd
                                                          ) 
                                          select cu.*,
                                                 a_ge.m_longitude,
                                                 a_ge.m_latitude
                                          from customer as cu
                                          inner join avg_ge as a_ge
                                          on cu.postal_cd = a_ge.postal_cd;

BigQuery
 drop table if exists [データセット名].customer_1;
 create table if not exists [データセット名].customer_1 as
                                                          with avg_ge as (
                                                                          select postal_cd,
                                                                                 avg(longitude) as m_longitude,
                                                                                 avg(latitude) as m_latitude
                                                                          from [データセット名].geocode
                                                                          group by postal_cd
                                                                         )
                                                          select cu.*,
                                                                 a_ge.m_longitude,
                                                                 a_ge.m_latitude
                                                          from [データセット名].customer as cu
                                                          inner join avg_ge as a_ge
                                                          on cu.postal_cd = a_ge.postal_cd;

S-086:前設問で作成した緯度経度つき顧客テーブル(customer_1)に対し、
    申込み店舗コード(application_store_cd)をキーに店舗テーブル(store)と結合せよ。
   そして申込み店舗の緯度(latitude)・経度情報(longitude)と
    顧客の緯度・経度を用いて距離(km)を求め、
    顧客ID(customer_id)、顧客住所(address)、店舗住所(address)とともに表示せよ。
   計算式は簡易式で良いものとするが、
    その他精度の高い方式を利用したライブラリを利用してもかまわない。
   結果は10件表示すれば良い。
    --緯度(ラジアン):𝜙、経度(ラジアン):𝜆、
    --距離𝐿 = 6371 ∗ 𝑎𝑟𝑐𝑐𝑜𝑠( 𝑠𝑖𝑛𝜙1 ∗ 𝑠𝑖𝑛𝜙2 + 𝑐𝑜𝑠𝜙1 ∗ 𝑐𝑜𝑠𝜙2 ∗ 𝑐𝑜𝑠(𝜆1−𝜆2) )

PostgreSQL
 select c1.customer_id,
        c1.address as c_ad,
        s.address as s_ad,
        6371 * acos(sin(radians(c1.m_latitude)) * sin(radians(s.latitude)) +
          cos(radians(c1.m_latitude)) * cos(radians(s.latitude)) * cos(radians(c1.m_longitude - s.longitude)))
          as distance
 from customer_1 as c1
 inner join store as s
 on c1.application_store_cd = s.store_cd
 limit 10;

BigQuery
BigQueryにラジアンを計算できる関数が無いため行わない

--S-087,S-088,S-089は解答を見ても良く分からなかったので割愛する。
S-087: 顧客テーブル(customer)では、異なる店舗での申込みなどにより同一顧客が複数登録されている。名前(customer_name)と郵便番号(postal_cd)が同じ顧客は同一顧客とみなし、1顧客1レコードとなるように名寄せした名寄顧客テーブル(customer_u)を作成せよ。ただし、同一顧客に対しては売上金額合計が最も高いものを残すものとし、売上金額合計が同一もしくは売上実績の無い顧客については顧客ID(customer_id)の番号が小さいものを残すこととする。

select customer_name from customer; #21971行
select distinct customer_name from customer; #18689行
select distinct postal_cd from customer; #1239行

S-088: 前設問で作成したデータを元に、顧客テーブルに統合名寄IDを付与したテーブル(customer_n)を作成せよ。ただし、統合名寄IDは以下の仕様で付与するものとする。

重複していない顧客:顧客ID(customer_id)を設定
重複している顧客:前設問で抽出したレコードの顧客IDを設定

S-089: 売上実績のある顧客に対し、予測モデル構築のため学習用データとテスト用データに分割したい。それぞれ8:2の割合でランダムに分割し、テーブルを作成せよ。


--S-090はSQL向きではないため割愛
S-090: レシート明細テーブル(receipt)は2017年1月1日〜2019年10月31日までのデータを有している。売上金額(amount)を月次で集計し、学習用に12ヶ月、テスト用に6ヶ月のモデル構築用データを3テーブルとしてセット作成せよ。データの持ち方は自由とする。

Discussion