数据科学 100 道练习 41~50#
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-041#
对收据明细数据(df_receipt),按日期(sales_ymd)合计销售金额(amount),再计算相对上一次有销售的日期的销售金额增减额。显示 10 条结果。
(
df_receipt
.lazy()
.group_by('sales_ymd')
.agg(
pl.col.amount.sum()
)
.sort(
by='sales_ymd'
)
.with_columns(
pl.all().shift().name.prefix('lag_')
)
.with_columns(
diff_amount = pl.col.amount - pl.col.lag_amount
)
.head(10)
.collect()
)
| sales_ymd | amount | lag_sales_ymd | lag_amount | diff_amount |
|---|---|---|---|---|
| i64 | i64 | i64 | i64 | i64 |
| 20170101 | 33723 | null | null | null |
| 20170102 | 24165 | 20170101 | 33723 | -9558 |
| 20170103 | 27503 | 20170102 | 24165 | 3338 |
| 20170104 | 36165 | 20170103 | 27503 | 8662 |
| 20170105 | 37830 | 20170104 | 36165 | 1665 |
| 20170106 | 32387 | 20170105 | 37830 | -5443 |
| 20170107 | 23415 | 20170106 | 32387 | -8972 |
| 20170108 | 24737 | 20170107 | 23415 | 1322 |
| 20170109 | 26718 | 20170108 | 24737 | 1981 |
| 20170110 | 20143 | 20170109 | 26718 | -6575 |
P-042#
对收据明细数据(df_receipt),按日期(sales_ymd)合计销售金额(amount),并对每个日期的数据,连接上一次、上两次、上三次有销售的日期的数据。显示 10 条结果。
df_tmp = (
df_receipt
.group_by('sales_ymd')
.agg(pl.col.amount.sum())
.sort(by='sales_ymd')
.with_columns(
index=pl.int_range(0, pl.len(), dtype=pl.Int32)
)
)
df_rolling = (
df_tmp
.rolling('index', period='3i', offset='-4i')
.agg(
pl.col.sales_ymd,
pl.col.amount
)
)
(
df_tmp
.join(
df_rolling, on='index', suffix='_lag'
)
.explode(pl.col('^*._lag$'))
.drop('index')
.head(10)
)
| sales_ymd | amount | sales_ymd_lag | amount_lag |
|---|---|---|---|
| i64 | i64 | i64 | i64 |
| 20170102 | 24165 | 20170101 | 33723 |
| 20170103 | 27503 | 20170101 | 33723 |
| 20170103 | 27503 | 20170102 | 24165 |
| 20170104 | 36165 | 20170101 | 33723 |
| 20170104 | 36165 | 20170102 | 24165 |
| 20170104 | 36165 | 20170103 | 27503 |
| 20170105 | 37830 | 20170102 | 24165 |
| 20170105 | 37830 | 20170103 | 27503 |
| 20170105 | 37830 | 20170104 | 36165 |
| 20170106 | 32387 | 20170103 | 27503 |
P-043#
连接收据明细数据(df_receipt)与顾客数据(df_customer),按性别代码(gender_cd)和年代(由 age 计算)合计销售金额(amount),生成销售汇总数据。性别代码 0 表示男性、1 表示女性、9 表示不明。
项目结构为年代、女性销售金额、男性销售金额、性别不明销售金额这 4 项,纵向为年代、横向为性别的交叉汇总。年代按每 10 岁划分一档。
df_sales_summary = (
df_receipt
.join(
df_customer, on='customer_id', how='inner'
)
.group_by(
(pl.col.age // 10) * 10, 'gender_cd'
)
.agg(
pl.col.amount.sum()
)
.sort(
by=['age', 'gender_cd']
)
.with_columns(
pl.col.gender_cd.replace({'0':'male', '1':'female', '9':'unknown'})
)
.pivot(
index='age',
on='gender_cd',
values='amount'
)
)
df_sales_summary
| age | male | female | unknown |
|---|---|---|---|
| i64 | i64 | i64 | i64 |
| 10 | 1591 | 149836 | 4317 |
| 20 | 72940 | 1363724 | 44328 |
| 30 | 177322 | 693047 | 50441 |
| 40 | 19355 | 9320791 | 483512 |
| 50 | 54320 | 6685192 | 342923 |
| 60 | 272469 | 987741 | 71418 |
| 70 | 13435 | 29764 | 2427 |
| 80 | 46360 | 262923 | 5111 |
| 90 | null | 6260 | null |
P-044#
043 生成的销售汇总数据(df_sales_summary)把各性别的销售额横向摆放。将该数据中的性别改为纵向摆放,转换为年代、性别代码、销售金额这 3 个项目。其中性别代码男性为 "00"、女性为 "01"、不明为 "99"。
(
df_sales_summary
.unpivot(
index='age', variable_name='gender_cd', value_name='amount'
)
.with_columns(
pl.col.gender_cd.replace({'male':'00', 'female':'01', 'unknown':'99'})
)
.head()
)
| age | gender_cd | amount |
|---|---|---|
| i64 | str | i64 |
| 10 | "00" | 1591 |
| 20 | "00" | 72940 |
| 30 | "00" | 177322 |
| 40 | "00" | 19355 |
| 50 | "00" | 54320 |
P-045#
顾客数据(df_customer)的出生日期(birth_day)以日期型保存。将其转换为 YYYYMMDD 格式的字符串,与顾客 ID(customer_id)一起显示 10 条。
(
df_customer
.select(
pl.col.customer_id,
pl.col.birth_day.str.replace_all('-', '')
)
.head()
)
| customer_id | birth_day |
|---|---|
| str | str |
| "CS021313000114" | "19810429" |
| "CS037613000071" | "19520401" |
| "CS031415000172" | "19761004" |
| "CS028811000001" | "19330327" |
| "CS001215000145" | "19950329" |
P-046#
顾客数据(df_customer)的申请日期(application_date)以 YYYYMMDD 格式的字符串型保存。将其转换为日期型,与顾客 ID(customer_id)一起显示 10 条。
(
df_customer
.select(
pl.col.customer_id,
pl.col.application_date.cast(str).str.strptime(pl.Date, '%Y%m%d')
)
.head()
)
| customer_id | application_date |
|---|---|
| str | date |
| "CS021313000114" | 2015-09-05 |
| "CS037613000071" | 2015-04-14 |
| "CS031415000172" | 2015-05-29 |
| "CS028811000001" | 2016-01-15 |
| "CS001215000145" | 2017-06-05 |
P-047#
收据明细数据(df_receipt)的销售日期(sales_ymd)以 YYYYMMDD 格式的数值型保存。将其转换为日期型,与收据编号(receipt_no)、收据子编号(receipt_sub_no)一起显示 10 条。
# cast(str) 以外的方法
(
df_receipt
.select(
pl.col('receipt_no', 'receipt_sub_no'),
pl.date(
pl.col.sales_ymd // 10000,
pl.col.sales_ymd % 10000 // 100,
pl.col.sales_ymd % 100
)
.alias('sales_ymd')
)
.head()
)
| receipt_no | receipt_sub_no | sales_ymd |
|---|---|---|
| i64 | i64 | date |
| 112 | 1 | 2018-11-03 |
| 1132 | 2 | 2018-11-18 |
| 1102 | 1 | 2017-07-12 |
| 1132 | 1 | 2019-02-05 |
| 1102 | 2 | 2018-08-21 |
P-048#
收据明细数据(df_receipt)的销售纪元秒(sales_epoch)以数值型的 UNIX 秒保存。将其转换为日期型,与收据编号(receipt_no)、收据子编号(receipt_sub_no)一起显示 10 条。
(
df_receipt
.select(
pl.col('receipt_no', 'receipt_sub_no'),
pl.from_epoch(pl.col.sales_epoch).dt.date()
)
.head()
)
| receipt_no | receipt_sub_no | sales_epoch |
|---|---|---|
| i64 | i64 | date |
| 112 | 1 | 2018-11-03 |
| 1132 | 2 | 2018-11-18 |
| 1102 | 1 | 2017-07-12 |
| 1132 | 1 | 2019-02-05 |
| 1102 | 2 | 2018-08-21 |
P-049#
将收据明细数据(df_receipt)的销售纪元秒(sales_epoch)转换为日期型,只取出“年”,与收据编号(receipt_no)、收据子编号(receipt_sub_no)一起显示 10 条。
(
df_receipt
.select(
pl.col('receipt_no', 'receipt_sub_no'),
pl.from_epoch(pl.col.sales_epoch).dt.year()
)
.head()
)
| receipt_no | receipt_sub_no | sales_epoch |
|---|---|---|
| i64 | i64 | i32 |
| 112 | 1 | 2018 |
| 1132 | 2 | 2018 |
| 1102 | 1 | 2017 |
| 1132 | 1 | 2019 |
| 1102 | 2 | 2018 |
P-050#
将收据明细数据(df_receipt)的销售纪元秒(sales_epoch)转换为日期型,只取出“月”,与收据编号(receipt_no)、收据子编号(receipt_sub_no)一起显示 10 条。月份以补零的 2 位取出。
(
df_receipt
.select(
pl.col('receipt_no', 'receipt_sub_no'),
pl.from_epoch(pl.col.sales_epoch).dt.month().cast(str).str.pad_start(2, '0')
)
.head()
)
| receipt_no | receipt_sub_no | sales_epoch |
|---|---|---|
| i64 | i64 | str |
| 112 | 1 | "11" |
| 1132 | 2 | "11" |
| 1102 | 1 | "07" |
| 1132 | 1 | "02" |
| 1102 | 2 | "08" |