数据科学 100 道练习 41~50

数据科学 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()
)
shape: (10, 5)
sales_ymdamountlag_sales_ymdlag_amountdiff_amount
i64i64i64i64i64
2017010133723nullnullnull
20170102241652017010133723-9558
201701032750320170102241653338
201701043616520170103275038662
201701053783020170104361651665
20170106323872017010537830-5443
20170107234152017010632387-8972
201701082473720170107234151322
201701092671820170108247371981
20170110201432017010926718-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)
)
shape: (10, 4)
sales_ymdamountsales_ymd_lagamount_lag
i64i64i64i64
20170102241652017010133723
20170103275032017010133723
20170103275032017010224165
20170104361652017010133723
20170104361652017010224165
20170104361652017010327503
20170105378302017010224165
20170105378302017010327503
20170105378302017010436165
20170106323872017010327503

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
shape: (9, 4)
agemalefemaleunknown
i64i64i64i64
1015911498364317
2072940136372444328
3017732269304750441
40193559320791483512
50543206685192342923
6027246998774171418
7013435297642427
80463602629235111
90null6260null

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()
)
shape: (5, 3)
agegender_cdamount
i64stri64
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()
)
shape: (5, 2)
customer_idbirth_day
strstr
"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()
)
shape: (5, 2)
customer_idapplication_date
strdate
"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()
)
shape: (5, 3)
receipt_noreceipt_sub_nosales_ymd
i64i64date
11212018-11-03
113222018-11-18
110212017-07-12
113212019-02-05
110222018-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()
)
shape: (5, 3)
receipt_noreceipt_sub_nosales_epoch
i64i64date
11212018-11-03
113222018-11-18
110212017-07-12
113212019-02-05
110222018-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()
)
shape: (5, 3)
receipt_noreceipt_sub_nosales_epoch
i64i64i32
11212018
113222018
110212017
113212019
110222018

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()
)
shape: (5, 3)
receipt_noreceipt_sub_nosales_epoch
i64i64str
1121"11"
11322"11"
11021"07"
11321"02"
11022"08"