データサイエンス 100 本ノックとは

The-Japan-DataScientist-Society/100knocks-preprocess: データサイエンス 100 本ノック(構造化データ加工編)

Google Colab で データサイエンス 100 本ノックをするにあたって

以下の ipynb ファイルを使用させていただきました。

noguhiro2002/100knocks-preprocess_ForColab-AzureNotebook: データサイエンス 100 本ノック(構造化データ加工編)For Azure_Notebook/Google_Colabo

実行ファイル

データサイエンス 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