数据科学 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
"CS002515000372"2019-08-312015-12-1244
"CS032515000101"2017-12-072015-09-1427
"CS007615000026"2017-12-282015-10-2726
"CS020414000031"2019-01-052015-03-2946
"CS037415000134"2017-01-042015-01-2824

P-072#

对收据明细数据(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_years=pl.col.sales_ymd.dt.year() - pl.col.application_date.dt.year()
)
.head()
)
shape: (5, 4)
customer_idsales_ymdapplication_dateelapsed_years
strdatedatei32
"CS004515000278"2017-12-122016-07-161
"CS015515000227"2018-08-232015-05-053
"CS011515000108"2018-02-172015-03-153
"CS006414000037"2019-06-082015-06-124
"CS019214000015"2017-01-092015-09-122

P-073#

对收据明细数据(df_receipt)的销售日期(sales_ymd),计算距顾客数据(df_customer)中会员申请日(application_date)的经过时间(以 epoch 秒表示),并连同顾客 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
"CS030415000043"2017-10-172015-07-2570416000
"CS033415000154"2019-10-082015-04-19141091200
"CS013515000038"2019-08-042015-06-21130032000
"CS037515000118"2018-11-222015-04-22113184000
"CS042311000001"2019-05-212016-08-3085881600

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
"CS002711000049""菊地 真希""1""女性""1940-11-27"78"185-0035""東京都国分寺市西町**********""S13002"20170607"0-00000000-0"
"CS004412000064""北条 朝香""1""女性""1972-03-15"47"176-0002""東京都練馬区桜台**********""S13004"20150816"0-00000000-0"
"CS004303000001""松川 ノブヒコ""0""男性""1979-05-18"39"166-0001""東京都杉並区阿佐谷北**********""S13004"20150517"0-00000000-0"
"CS012415000156""本田 あさみ""1""女性""1970-08-20"48"231-0806""神奈川県横浜市中区本牧町**********""S14012"20150930"D-20100820-D"
"CS003712000085""中井 みあ""1""女性""1944-03-11"75"201-0015""東京都狛江市猪方**********""S13003"20160228"0-00000000-0"

P-076#

按性别代码(gender_cd)的比例从顾客数据(df_customer)中随机分层抽取 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" 开头的记录代表非会员,计算时须排除。这里用第一四分位数与第三四分位数之差 IQR 判定离群值,低于“第一四分位数 - 1.5×IQR”或高于“第三四分位数 + 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
"CS023515000195"10688
"CS026515000043"9303
"CS021515000211"10148
"CS026414000062"9466
"CS022515000113"9347

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)