データサイエンス100本ノック 71~80

データサイエンス100本ノック 71~80#

import polars as pl
from helper.polars import load_100knocks_data
pl.Config.set_fmt_str_lengths(100)
df_customer, df_category, df_product, df_receipt, df_store, df_geocode = load_100knocks_data()

P-071#

レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、顧客データ(df_customer)の会員申込日(application_date)からの経過月数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1ヶ月未満は切り捨てること。

(
df_receipt
.select('customer_id', 'sales_ymd')
.unique(keep='first')
.join(df_customer, on='customer_id')
.select(
    pl.col.customer_id,
    pl.col.sales_ymd.cast(str).str.strptime(pl.Date, '%Y%m%d'),
    pl.col.application_date.cast(str).str.strptime(pl.Date, '%Y%m%d')
)
.with_columns(
    elapsed_months=
        (pl.col.sales_ymd.dt.year() - pl.col.application_date.dt.year()) * 12 +
        (pl.col.sales_ymd.dt.month() - pl.col.application_date.dt.month())
)
.head()
)
shape: (5, 4)
customer_idsales_ymdapplication_dateelapsed_months
strdatedatei32
"CS003215000091"2018-04-092016-02-2326
"CS007512000016"2018-09-212015-09-3036
"CS024515000234"2018-11-282015-06-1041
"CS038515000127"2019-02-072015-03-2547
"CS021515000153"2018-09-292015-03-0142

P-072#

レシート明細データ(df_receipt)の売上日(df_customer)に対し、顧客データ(df_customer)の会員申込日(application_date)からの経過年数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1年未満は切り捨てること。

(
df_receipt
.select('customer_id', 'sales_ymd')
.unique(keep='first')
.join(df_customer, on='customer_id')
.select(
    pl.col.customer_id,
    pl.col.sales_ymd.cast(str).str.strptime(pl.Date, '%Y%m%d'),
    pl.col.application_date.cast(str).str.strptime(pl.Date, '%Y%m%d')
)
.with_columns(
    elapsed_years=pl.col.sales_ymd.dt.year() - pl.col.application_date.dt.year()
)
.head()
)
shape: (5, 4)
customer_idsales_ymdapplication_dateelapsed_years
strdatedatei32
"CS040605000014"2017-02-212015-08-202
"CS034414000077"2019-09-292015-02-204
"CS013514000050"2017-11-102015-03-142
"CS024815000009"2018-09-232015-08-073
"CS010414000031"2018-01-082015-03-093

P-073#

レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、顧客データ(df_customer)の会員申込日(application_date)からのエポック秒による経過時間を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。なお、時間情報は保有していないため各日付は0時0分0秒を表すものとする。

(
df_receipt
.select('customer_id', 'sales_ymd')
.unique(keep='first')
.join(df_customer, on='customer_id')
.select(
    pl.col.customer_id,
    pl.col.sales_ymd.cast(str).str.strptime(pl.Date, '%Y%m%d'),
    pl.col.application_date.cast(str).str.strptime(pl.Date, '%Y%m%d')
)
.with_columns(
    elapsed_years=(pl.col.sales_ymd - pl.col.application_date).dt.total_seconds()
)
.head()
)
shape: (5, 4)
customer_idsales_ymdapplication_dateelapsed_years
strdatedatei64
"CS030214000008"2017-01-172015-05-2552099200
"CS027515000056"2017-09-232015-02-0882771200
"CS010415000253"2019-05-202015-01-22136425600
"CS040615000016"2019-08-092015-08-26124761600
"CS014415000151"2018-06-172015-07-1792102400

P-074#

レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、当該週の月曜日からの経過日数を計算し、売上日、直前の月曜日付とともに10件表示せよ(sales_ymdは数値でデータを保持している点に注意)。

(
df_receipt
.select('customer_id', 'sales_ymd')
.join(df_customer, on='customer_id')
.select(
    pl.col.sales_ymd.cast(str).str.strptime(pl.Date, '%Y%m%d'),
)
.with_columns(
    pl.col.sales_ymd.dt.truncate('1w').alias('monday')
)
.with_columns(
    (pl.col.sales_ymd - pl.col.monday).alias('elapsed_days').dt.total_days()
)
.head()
)
shape: (5, 3)
sales_ymdmondayelapsed_days
datedatei64
2018-11-032018-10-295
2018-11-182018-11-126
2017-07-122017-07-102
2018-08-212018-08-201
2019-06-052019-06-032

P-075#

顧客データ(df_customer)からランダムに1%のデータを抽出し、先頭から10件表示せよ。

df_customer.sample(fraction=0.01).head(5)
shape: (5, 11)
customer_idcustomer_namegender_cdgenderbirth_dayagepostal_cdaddressapplication_store_cdapplication_datestatus_cd
strstrstrstrstri64strstrstri64str
"CS010415000255""星 さやか""1""女性""1975-02-08"44"222-0011""神奈川県横浜市港北区菊名**********""S14010"20150408"D-20101026-D"
"CS031312000056""多岐川 未來""1""女性""1984-04-27"34"151-0063""東京都渋谷区富ヶ谷**********""S13031"20150410"0-00000000-0"
"CS023213000050""長野 倫子""1""女性""1995-12-01"23"212-0005""神奈川県川崎市幸区戸手**********""S14023"20150625"0-00000000-0"
"CS022313000123""中川 薫""1""女性""1984-02-20"35"249-0001""神奈川県逗子市久木**********""S14022"20160106"0-00000000-0"
"CS019714000037""浜口 季衣""1""女性""1944-06-04"74"173-0037""東京都板橋区小茂根**********""S13019"20180509"0-00000000-0"

P-076#

顧客データ(df_customer)から性別コード(gender_cd)の割合に基づきランダムに10%のデータを層化抽出し、性別コードごとに件数を集計せよ。

(
df_customer
.filter(
    (pl.int_range(pl.len()) < pl.len() * 0.1).shuffle().over('gender_cd') == 1
)
.group_by('gender_cd')
.agg(pl.len())
)
shape: (3, 2)
gender_cdlen
stru32
"0"299
"1"1792
"9"108

P-077#

レシート明細データ(df_receipt)の売上金額を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。なお、外れ値は売上金額合計を対数化したうえで平均と標準偏差を計算し、その平均から3σを超えて離れたものとする(自然対数と常用対数のどちらでも可)。結果は10件表示せよ。

(
df_receipt
.group_by('customer_id')
.agg(
    sum_amount=pl.col.amount.sum(),
    log_sum_amount=pl.col.amount.sum().log(),
)
.filter(
    (pl.col.log_sum_amount - pl.col.log_sum_amount.mean()) / pl.col.log_sum_amount.std() > 3
)
)
shape: (1, 3)
customer_idsum_amountlog_sum_amount
stri64f64
"ZZ000000000000"1239500316.332804

P-078#

レシート明細データ(df_receipt)の売上金額(amount)を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。なお、ここでは外れ値を第1四分位と第3四分位の差であるIQRを用いて、「第1四分位数-1.5×IQR」を下回るもの、または「第3四分位数+1.5×IQR」を超えるものとする。結果は10件表示せよ。

col = pl.col.sum_amount
q25 = col.quantile(0.25)
q75 = col.quantile(0.75)
iqr = q75 - q25
expr = (col > q75 + iqr * 1.5) | (col < q25 - iqr * 1.5)
(
df_receipt
.filter(
    pl.col.customer_id.str.starts_with('Z').not_()
)
.group_by('customer_id')
.agg(sum_amount=pl.col.amount.sum())
.filter(expr)
.head()
)
shape: (5, 2)
customer_idsum_amount
stri64
"CS014415000172"11329
"CS014415000077"14076
"CS017415000187"9948
"CS019415000015"13958
"CS023414000088"8798

P-079#

商品データ(df_product)の各項目に対し、欠損数を確認せよ。

df_product.select(
    pl.all().null_count()
)
shape: (1, 6)
product_cdcategory_major_cdcategory_medium_cdcategory_small_cdunit_priceunit_cost
u32u32u32u32u32u32
000077

P-080#

商品データ(df_product)のいずれかの項目に欠損が発生しているレコードを全て削除した新たな商品データを作成せよ。なお、削除前後の件数を表示させ、079で確認した件数だけ減少していることも確認すること。

len(df_product), len(df_product.drop_nulls())
(10030, 10023)