数据科学 100 道练习 51~60

数据科学 100 道练习 51~60#

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-051#

将收据明细数据(df_receipt)的销售纪元秒转换为日期型,只取出“日”,与收据编号(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.day().cast(str).str.pad_start(2, '0')
)
.head()
)
shape: (5, 3)
receipt_noreceipt_sub_nosales_epoch
i64i64str
1121"03"
11322"18"
11021"12"
11321"05"
11022"21"

P-052#

对收据明细数据(df_receipt)的销售金额(amount),按顾客 ID(customer_id)合计,再对合计金额做二值化:2,000 日元以下为 0,大于 2,000 日元为 1,与顾客 ID、销售金额合计一起显示 10 条。顾客 ID 以 "Z" 开头的是非会员,计算时排除在外。

(
df_receipt
.filter(
    pl.col.customer_id.str.starts_with('Z').not_()
)
.group_by(
    'customer_id', maintain_order=True
)
.agg(
    sum_amount=pl.col.amount.sum()
)
.with_columns(
    sales_flg=pl.when(pl.col.sum_amount >= 2000).then(1).otherwise(0)
)
.head()
)
shape: (5, 3)
customer_idsum_amountsales_flg
stri64i32
"CS006214000001"73641
"CS008415000097"18950
"CS028414000014"62221
"CS025415000050"57361
"CS003515000195"54121

P-053#

对顾客数据(df_customer)的邮编(postal_cd)做二值化:东京(前 3 位为 100〜209)为 1,其他为 0。再与收据明细数据(df_receipt)连接,按生成的二值统计全期间有销售实绩的顾客数。

(
df_receipt
.join(
    df_customer
        .select(
            'customer_id',
            postal_flg=pl.col.postal_cd
                .str.slice(0, 3)
                .cast(pl.UInt16)
                .is_between(100, 209)
                .cast(pl.UInt8)
        ),
    on='customer_id',
    how='inner'
)
.group_by(
    'postal_flg'
)
.agg(customer_cnt=pl.col.customer_id.n_unique())
)
shape: (2, 2)
postal_flgcustomer_cnt
u8u32
03906
14400

P-054#

顾客数据(df_customer)的地址(address)是埼玉县、千叶县、东京都、神奈川县之一。按都道府县生成代码值,与顾客 ID、地址一起显示 10 条。取值为埼玉县 11、千叶县 12、东京都 13、神奈川县 14。

names = ['埼玉県', '千葉県', '東京都', '神奈川県']
codes = [11, 12, 13, 14]
(
df_customer
.select(
    'customer_id', 'address',
    prefecture_cd=pl.col.address
        .str.extract('^(.*?[都道府県])')
        .replace(names, codes)
)
.head()
)
shape: (5, 3)
customer_idaddressprefecture_cd
strstrstr
"CS021313000114""神奈川県伊勢原市粟窪**********""14"
"CS037613000071""東京都江東区南砂**********""13"
"CS031415000172""東京都渋谷区代々木**********""13"
"CS028811000001""神奈川県横浜市泉区和泉町**********""14"
"CS001215000145""東京都大田区仲六郷**********""13"

P-055#

对收据明细数据(df_receipt)的销售金额(amount),按顾客 ID(customer_id)合计,求出合计金额的四分位点。然后按以下标准为每位顾客的销售金额合计生成类别值,与顾客 ID、销售金额合计一起显示 10 条。类别值依次为 1〜4。

  • 不小于最小值且小于第 1 四分位 … 赋予 1

  • 不小于第 1 四分位且小于第 2 四分位 … 赋予 2

  • 不小于第 2 四分位且小于第 3 四分位 … 赋予 3

  • 不小于第 3 四分位 … 赋予 4

(
    df_receipt
    .group_by("customer_id", maintain_order=True)
    .agg(sum_amount=pl.col.amount.sum())
    .with_columns(
        # 2.0 中用 bin_quantiles 代替 qcut(默认为左闭区间)
        pct_group=pl.col.sum_amount.bin_quantiles(
            [0.25, 0.5, 0.75], labels=["1", "2", "3", "4"]
        )
    )
    .head(10)
)
shape: (10, 3)
customer_idsum_amountpct_group
stri64enum
"CS006214000001"7364"4"
"CS008415000097"1895"3"
"CS028414000014"6222"4"
"ZZ000000000000"12395003"4"
"CS025415000050"5736"4"
"CS003515000195"5412"4"
"CS024514000042"533"1"
"CS040415000178"6149"4"
"CS027514000015"2788"3"
"CS025415000134"4902"4"

P-056#

根据顾客数据(df_customer)的年龄(age)按每 10 岁计算年代,与顾客 ID(customer_id)、出生日期(birth_day)一起显示 10 条。60 岁以上全部归入 60 岁档。表示年代的类别名可自定。

(
df_customer
.select(
    'customer_id', 'birth_day',
    (pl.col.age // 10 * 10).clip(upper_bound=60)
)
.head(10)
)
shape: (10, 3)
customer_idbirth_dayage
strstri64
"CS021313000114""1981-04-29"30
"CS037613000071""1952-04-01"60
"CS031415000172""1976-10-04"40
"CS028811000001""1933-03-27"60
"CS001215000145""1995-03-29"20
"CS020401000016""1974-09-15"40
"CS015414000103""1977-08-09"40
"CS029403000008""1973-08-17"40
"CS015804000004""1931-05-02"60
"CS033513000180""1962-07-11"50

P-057#

根据 056 的抽取结果和性别代码(gender_cd),新建表示性别×年代组合的类别数据,显示 10 条。组合类别的取值可自定。

(
df_customer
.select(
    'customer_id', 'birth_day',
    gender_era=
        pl.col.gender_cd + 
        ((pl.col.age // 10 * 10).clip(upper_bound=60) + 100)
            .cast(str)
            .str.slice(1, 2)
)
.head(10)
)
shape: (10, 3)
customer_idbirth_daygender_era
strstrstr
"CS021313000114""1981-04-29""130"
"CS037613000071""1952-04-01""960"
"CS031415000172""1976-10-04""140"
"CS028811000001""1933-03-27""160"
"CS001215000145""1995-03-29""120"
"CS020401000016""1974-09-15""040"
"CS015414000103""1977-08-09""140"
"CS029403000008""1973-08-17""040"
"CS015804000004""1931-05-02""060"
"CS033513000180""1962-07-11""150"

P-058#

将顾客数据(df_customer)的性别代码(gender_cd)做成哑变量,与顾客 ID(customer_id)一起显示 10 条。

(
pl.concat([
    df_customer.select('customer_id'), 
    df_customer.get_column('gender_cd').to_dummies()
], how='horizontal')
.head()
)
shape: (5, 4)
customer_idgender_cd_0gender_cd_1gender_cd_9
stru8u8u8
"CS021313000114"010
"CS037613000071"001
"CS031415000172"010
"CS028811000001"010
"CS001215000145"010

P-059#

对收据明细数据(df_receipt)的销售金额(amount),按顾客 ID(customer_id)合计,将合计金额标准化为均值 0、标准差 1,与顾客 ID、销售金额合计一起显示 10 条。标准化所用的标准差可以是方差的平方根,也可以是无偏方差的平方根。顾客 ID 以 "Z" 开头的是非会员,计算时排除在外。

TIPS:

  • query() 的参数 engine 可选 'python' 或 'numexpr',默认在已安装时使用 numexpr,未安装则使用 python。另外,字符串方法只有在 engine='python' 时才能在 query() 内使用。

(
df_receipt
.filter(
    pl.col.customer_id.str.starts_with('Z').not_()    
)
.group_by('customer_id')
.agg(
    sum_amount=pl.col.amount.sum()
)
.with_columns(
    std_amount=(pl.col.sum_amount - pl.col.sum_amount.mean()) / pl.col.sum_amount.std()
)
.head(10)
)
shape: (10, 3)
customer_idsum_amountstd_amount
stri64f64
"CS017412000064"47550.811291
"CS031512000114"202-0.862192
"CS003414000181"46260.763876
"CS001415000480"66351.502296
"CS013513000128"216-0.857046
"CS009514000024"1446-0.404952
"CS003415000452"42370.620897
"CS040613000056"27640.079487
"CS008615000064"346-0.809264
"CS027612000052"280-0.833522

P-060#

对收据明细数据(df_receipt)的销售金额(amount),按顾客 ID(customer_id)合计,将合计金额归一化到最小值 0、最大值 1,与顾客 ID、销售金额合计一起显示 10 条。顾客 ID 以 "Z" 开头的是非会员,计算时排除在外。

(
df_receipt
.filter(
    pl.col.customer_id.str.starts_with('Z').not_()    
)
.group_by('customer_id')
.agg(
    sum_amount=pl.col.amount.sum()
)
.with_columns(
    std_amount=(pl.col.sum_amount - pl.col.sum_amount.min()) / (pl.col.sum_amount.max() - pl.col.sum_amount.min())
)
.head(10)
)
shape: (10, 3)
customer_idsum_amountstd_amount
stri64f64
"CS035513000171"22510.09476
"CS028512000131"5620.021376
"CS029515000204"11410.046533
"CS040212000007"4860.018074
"CS002415000576"27860.118005
"CS001114000005"6260.024157
"CS016415000253"19780.082899
"CS015415000069"98520.425009
"CS023414000072"68980.296663
"CS034515000164"41360.17666