6 minutes
データサイエンス100本ノックをPythonでやってみた [61-70]
データサイエンス 100 本ノックとは
The-Japan-DataScientist-Society/100knocks-preprocess: データサイエンス 100 本ノック(構造化データ加工編)
Google Colab で データサイエンス 100 本ノックをするにあたって
以下の ipynb ファイルを使用させていただきました。
実行ファイル
データサイエンス 100 本ノック by tomowarkar
061
P-061: レシート明細データフレーム(df_receipt)の売上金額(amount)を顧客 ID(customer_id)ごとに合計し、合計した売上金額を常用対数化(底=10)して顧客 ID、売上金額合計とともに表示せよ。ただし、顧客 ID が"Z"から始まるのものは非会員を表すため、除外して計算すること。結果は 10 件表示させれば良い。
対数化は 0 に注意ですが,今回は問題なさそうだったのでそのままです。
tmp = df_receipt[~df_receipt.customer_id.str.startswith("Z")].groupby("customer_id").amount.sum()
pd.concat(
[tmp,
pd.Series(np.log10(tmp), name="log10")],
axis=1
).head(10)
amount log10
customer_id
CS001113000004 1298 3.113275
CS001114000005 626 2.796574
CS001115000010 3044 3.483445
CS001205000004 1988 3.298416
CS001205000006 3337 3.523356
CS001211000025 456 2.658965
CS001212000027 448 2.651278
CS001212000031 296 2.471292
CS001212000046 228 2.357935
CS001212000070 456 2.658965
062
P-062: レシート明細データフレーム(df_receipt)の売上金額(amount)を顧客 ID(customer_id)ごとに合計し、合計した売上金額を自然対数化(底=e)して顧客 ID、売上金額合計とともに表示せよ。ただし、顧客 ID が"Z"から始まるのものは非会員を表すため、除外して計算すること。結果は 10 件表示させれば良い。
tmp = df_receipt[~df_receipt.customer_id.str.startswith("Z")].groupby("customer_id").amount.sum()
pd.concat(
[tmp,
pd.Series(np.log(tmp), name="log")],
axis=1
).head(10)
amount log
customer_id
CS001113000004 1298 7.168580
CS001114000005 626 6.439350
CS001115000010 3044 8.020928
CS001205000004 1988 7.594884
CS001205000006 3337 8.112827
CS001211000025 456 6.122493
CS001212000027 448 6.104793
CS001212000031 296 5.690359
CS001212000046 228 5.429346
CS001212000070 456 6.122493
063
P-063: 商品データフレーム(df_product)の単価(unit_price)と原価(unit_cost)から、各商品の利益額を算出せよ。結果は 10 件表示させれば良い。
pd.concat(
[df_product,
pd.Series(df_product.unit_price - df_product.unit_cost, name="unit_margin")],
axis=1
).head(10)
product_cd category_major_cd ... unit_cost unit_margin
0 P040101001 4 ... 149.0 49.0
1 P040101002 4 ... 164.0 54.0
2 P040101003 4 ... 173.0 57.0
3 P040101004 4 ... 186.0 62.0
4 P040101005 4 ... 201.0 67.0
5 P040101006 4 ... 224.0 74.0
6 P040101007 4 ... 254.0 84.0
7 P040101008 4 ... 315.0 105.0
8 P040101009 4 ... 374.0 124.0
9 P040101010 4 ... 435.0 145.0
[10 rows x 7 columns]
064
P-064: 商品データフレーム(df_product)の単価(unit_price)と原価(unit_cost)から、各商品の利益率の全体平均を算出せよ。 ただし、単価と原価には NULL が存在することに注意せよ。
pandas.Series.mean
を使います引数のskipnabool
がデフォルトでTrue
なので NULL に関しては何も考えなくていいと思います。
((df_product.unit_price - df_product.unit_cost)/df_product.unit_price).mean() #> 0.24911389885176904
065
P-065: 商品データフレーム(df_product)の各商品について、利益率が 30%となる新たな単価を求めよ。ただし、1 円未満は切り捨てること。そして結果を 10 件表示させ、利益率がおよそ 30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には NULL が存在することに注意せよ。
numpy.floor
を使います。
tmp = pd.concat(
[df_product,
pd.Series(np.floor(df_product.unit_cost/0.7), name="unit_price_30")],
axis=1
)
tmp.head(10)
product_cd category_major_cd ... unit_cost unit_price_30
0 P040101001 4 ... 149.0 212.0
1 P040101002 4 ... 164.0 234.0
2 P040101003 4 ... 173.0 247.0
3 P040101004 4 ... 186.0 265.0
4 P040101005 4 ... 201.0 287.0
5 P040101006 4 ... 224.0 320.0
6 P040101007 4 ... 254.0 362.0
7 P040101008 4 ... 315.0 450.0
8 P040101009 4 ... 374.0 534.0
9 P040101010 4 ... 435.0 621.0
[10 rows x 7 columns]
((tmp.unit_price_30 - tmp.unit_cost)/tmp.unit_price_30).mean() #>0.298678449725414
066
P-066: 商品データフレーム(df_product)の各商品について、利益率が 30%となる新たな単価を求めよ。今回は、1 円未満を四捨五入すること(0.5 については偶数方向の丸めで良い)。そして結果を 10 件表示させ、利益率がおよそ 30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には NULL が存在することに注意せよ。
numpy.round
を使います。
tmp = pd.concat(
[df_product,
pd.Series(np.round(df_product.unit_cost/0.7), name="unit_price_30")],
axis=1
)
tmp.head(10)
product_cd category_major_cd ... unit_cost unit_price_30
0 P040101001 4 ... 149.0 213.0
1 P040101002 4 ... 164.0 234.0
2 P040101003 4 ... 173.0 247.0
3 P040101004 4 ... 186.0 266.0
4 P040101005 4 ... 201.0 287.0
5 P040101006 4 ... 224.0 320.0
6 P040101007 4 ... 254.0 363.0
7 P040101008 4 ... 315.0 450.0
8 P040101009 4 ... 374.0 534.0
9 P040101010 4 ... 435.0 621.0
[10 rows x 7 columns]
((tmp.unit_price_30 - tmp.unit_cost)/tmp.unit_price_30).mean() #> 0.2999568445636658
067
P-067: 商品データフレーム(df_product)の各商品について、利益率が 30%となる新たな単価を求めよ。今回は、1 円未満を切り上げること。そして結果を 10 件表示させ、利益率がおよそ 30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には NULL が存在することに注意せよ。
numpy.ceil
を使います。
tmp = pd.concat(
[df_product,
pd.Series(np.ceil(df_product.unit_cost/0.7), name="unit_price_30")],
axis=1
)
tmp.head(10)
product_cd category_major_cd ... unit_cost unit_price_30
0 P040101001 4 ... 149.0 213.0
1 P040101002 4 ... 164.0 235.0
2 P040101003 4 ... 173.0 248.0
3 P040101004 4 ... 186.0 266.0
4 P040101005 4 ... 201.0 288.0
5 P040101006 4 ... 224.0 320.0
6 P040101007 4 ... 254.0 363.0
7 P040101008 4 ... 315.0 451.0
8 P040101009 4 ... 374.0 535.0
9 P040101010 4 ... 435.0 622.0
[10 rows x 7 columns]
((tmp.unit_price_30 - tmp.unit_cost)/tmp.unit_price_30).mean() #> 0.3014164030578494
068
P-068: 商品データフレーム(df_product)の各商品について、消費税率 10%の税込み金額を求めよ。 1 円未満の端数は切り捨てとし、結果は 10 件表示すれば良い。ただし、単価(unit_price)には NULL が存在することに注意せよ。
単価 NULL の場合税込み金額も NULL になるので特に気にしません。
pd.concat(
[df_product,
pd.Series(np.floor(df_product.unit_price*1.1), name="with_tax_10")],
axis=1
).head(10)
product_cd category_major_cd ... unit_cost with_tax_10
0 P040101001 4 ... 149.0 217.0
1 P040101002 4 ... 164.0 239.0
2 P040101003 4 ... 173.0 253.0
3 P040101004 4 ... 186.0 272.0
4 P040101005 4 ... 201.0 294.0
5 P040101006 4 ... 224.0 327.0
6 P040101007 4 ... 254.0 371.0
7 P040101008 4 ... 315.0 462.0
8 P040101009 4 ... 374.0 547.0
9 P040101010 4 ... 435.0 638.0
[10 rows x 7 columns]
069
P-069: レシート明細データフレーム(df_receipt)と商品データフレーム(df_product)を結合し、顧客毎に全商品の売上金額合計と、カテゴリ大区分(category_major_cd)が"07”(瓶詰缶詰)の売上金額合計を計算の上、両者の比率を求めよ。抽出対象はカテゴリ大区分"07”(瓶詰缶詰)の購入実績がある顧客のみとし、結果は 10 件表示させればよい。
# カテゴリ区分7を集計
tmp = pd.merge(
df_receipt,
df_product.query('category_major_cd == 7'),
how="inner",
on="product_cd"
).groupby('customer_id').agg({"amount":"sum"})
# カテゴリ区分7の購入実績のあるcustomer_idをもとにdf_receiptを抽出して集計
tmp2 = pd.merge(
df_receipt,
pd.Series(tmp.index),
how="inner",
on="customer_id"
).groupby('customer_id').agg({"amount":"sum"})
pd.DataFrame(
[tmp.amount, tmp2.amount, tmp.amount/tmp2.amount],
index=["cat07", "all", "ratio"]
).T.head(10)
cat07 all ratio
customer_id
CS001113000004 1298.0 1298.0 1.000000
CS001114000005 486.0 626.0 0.776358
CS001115000010 2694.0 3044.0 0.885020
CS001205000004 346.0 1988.0 0.174044
CS001205000006 2004.0 3337.0 0.600539
CS001212000027 200.0 448.0 0.446429
CS001212000031 296.0 296.0 1.000000
CS001212000046 108.0 228.0 0.473684
CS001212000070 308.0 456.0 0.675439
CS001213000018 145.0 243.0 0.596708
070
P-070: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、顧客データフレーム(df_customer)の会員申込日(application_date)からの経過日数を計算し、顧客 ID(customer_id)、売上日、会員申込日とともに表示せよ。結果は 10 件表示させれば良い(なお、sales_ymd は数値、application_date は文字列でデータを保持している点に注意)。
tmp = pd.merge(
df_receipt[["sales_ymd", "customer_id"]],
df_customer[["customer_id", "application_date"]],
how="left"
)
tmp["elapsed_days"] = pd.to_datetime(tmp.sales_ymd, format='%Y%m%d') - pd.to_datetime(tmp.application_date, format='%Y%m%d')
tmp.head(10)
sales_ymd customer_id application_date elapsed_days
0 20181103 CS006214000001 20150201.0 1371 days
1 20181118 CS008415000097 20150322.0 1337 days
2 20170712 CS028414000014 20150711.0 732 days
3 20190205 ZZ000000000000 NaN NaT
4 20180821 CS025415000050 20160131.0 933 days
5 20190605 CS003515000195 20150306.0 1552 days
6 20181205 CS024514000042 20151010.0 1152 days
7 20190922 CS040415000178 20150627.0 1548 days
8 20170504 ZZ000000000000 NaN NaT
9 20191010 CS027514000015 20151101.0 1439 days