数据科学 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()
)
| customer_id | sales_ymd | application_date | elapsed_months |
|---|---|---|---|
| str | date | date | i32 |
| "CS002515000372" | 2019-08-31 | 2015-12-12 | 44 |
| "CS032515000101" | 2017-12-07 | 2015-09-14 | 27 |
| "CS007615000026" | 2017-12-28 | 2015-10-27 | 26 |
| "CS020414000031" | 2019-01-05 | 2015-03-29 | 46 |
| "CS037415000134" | 2017-01-04 | 2015-01-28 | 24 |
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()
)
| customer_id | sales_ymd | application_date | elapsed_years |
|---|---|---|---|
| str | date | date | i32 |
| "CS004515000278" | 2017-12-12 | 2016-07-16 | 1 |
| "CS015515000227" | 2018-08-23 | 2015-05-05 | 3 |
| "CS011515000108" | 2018-02-17 | 2015-03-15 | 3 |
| "CS006414000037" | 2019-06-08 | 2015-06-12 | 4 |
| "CS019214000015" | 2017-01-09 | 2015-09-12 | 2 |
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()
)
| customer_id | sales_ymd | application_date | elapsed_years |
|---|---|---|---|
| str | date | date | i64 |
| "CS030415000043" | 2017-10-17 | 2015-07-25 | 70416000 |
| "CS033415000154" | 2019-10-08 | 2015-04-19 | 141091200 |
| "CS013515000038" | 2019-08-04 | 2015-06-21 | 130032000 |
| "CS037515000118" | 2018-11-22 | 2015-04-22 | 113184000 |
| "CS042311000001" | 2019-05-21 | 2016-08-30 | 85881600 |
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()
)
| sales_ymd | monday | elapsed_days |
|---|---|---|
| date | date | i64 |
| 2018-11-03 | 2018-10-29 | 5 |
| 2018-11-18 | 2018-11-12 | 6 |
| 2017-07-12 | 2017-07-10 | 2 |
| 2018-08-21 | 2018-08-20 | 1 |
| 2019-06-05 | 2019-06-03 | 2 |
P-075#
从顾客数据(df_customer)中随机抽取 1% 的记录,显示前 10 条。
df_customer.sample(fraction=0.01).head(5)
| customer_id | customer_name | gender_cd | gender | birth_day | age | postal_cd | address | application_store_cd | application_date | status_cd |
|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | str | str | i64 | str | str | str | i64 | str |
| "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())
)
| gender_cd | len |
|---|---|
| str | u32 |
| "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
)
)
| customer_id | sum_amount | log_sum_amount |
|---|---|---|
| str | i64 | f64 |
| "ZZ000000000000" | 12395003 | 16.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()
)
| customer_id | sum_amount |
|---|---|
| str | i64 |
| "CS023515000195" | 10688 |
| "CS026515000043" | 9303 |
| "CS021515000211" | 10148 |
| "CS026414000062" | 9466 |
| "CS022515000113" | 9347 |
P-079#
确认商品数据(df_product)各项目的缺失值数量。
df_product.select(
pl.all().null_count()
)
| product_cd | category_major_cd | category_medium_cd | category_small_cd | unit_price | unit_cost |
|---|---|---|---|---|---|
| u32 | u32 | u32 | u32 | u32 | u32 |
| 0 | 0 | 0 | 0 | 7 | 7 |
P-080#
删除商品数据(df_product)中存在缺失值的全部记录,创建新的商品数据。同时显示删除前后的记录数,并确认减少的数量与 079 中确认的缺失数一致。
len(df_product), len(df_product.drop_nulls())
(10030, 10023)