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

081

P-081: 単価(unit_price)と原価(unit_cost)の欠損値について、それぞれの平均値で補完した新たな df_product_2 を作成せよ。なお、平均値について 1 円未満は四捨五入とし、0.5 については偶数寄せでかまわない。補完実施後、各項目について欠損が生じていないことも確認すること。

df_product_2 = df_product.fillna(
    {"unit_price": np.round(df_product.unit_price.mean()),
     "unit_cost": np.round(df_product.unit_cost.mean())})

df_product_2.isna().sum().any() #> False

動作検証は実行ファイル参照

082

P-082: 単価(unit_price)と原価(unit_cost)の欠損値について、それぞれの中央値で補完した新たな df_product_3 を作成せよ。なお、中央値について 1 円未満は四捨五入とし、0.5 については偶数寄せでかまわない。補完実施後、各項目について欠損が生じていないことも確認すること。

df_product_3 = df_product.fillna(
  {"unit_price": np.round(df_product.unit_price.median()),
   "unit_cost": np.round(df_product.unit_cost.median())})

df_product_3.isna().sum().any() #> False

動作検証は実行ファイル参照

083

P-083: 単価(unit_price)と原価(unit_cost)の欠損値について、各商品の小区分(category_small_cd)ごとに算出した中央値で補完した新たな df_product_4 を作成せよ。なお、中央値について 1 円未満は四捨五入とし、0.5 については偶数寄せでかまわない。補完実施後、各項目について欠損が生じていないことも確認すること。

mask = df_product.groupby("category_small_cd").agg(unit_price_median=("unit_price","median"), unit_cost_median=("unit_cost","median")).reset_index()
mask[["unit_price_median", "unit_cost_median"]] = mask[["unit_price_median", "unit_cost_median"]].applymap(lambda x: np.round(x))
mask = pd.merge(df_product, mask, how="left", on="category_small_cd")

mask.loc[mask.unit_price.isna(), "unit_price"] = mask.loc[mask.unit_price.isna(), "unit_price_median"]
mask.loc[mask.unit_cost.isna(), "unit_cost"] = mask.loc[mask.unit_cost.isna(), "unit_cost_median"]

df_product_4 = mask.drop(['unit_price_median', 'unit_cost_median'], axis=1)
df_product_4.isna().sum().any() #> False

動作検証は実行ファイル参照

084

P-084: 顧客データフレーム(df_customer)の全顧客に対し、全期間の売上金額に占める 2019 年売上金額の割合を計算せよ。ただし、販売実績のない場合は 0 として扱うこと。そして計算した割合が 0 超のものを抽出せよ。 結果は 10 件表示させれば良い。また、作成したデータに NA や NAN が存在しないことを確認せよ。

tmp = pd.merge(df_customer[["customer_id"]], df_receipt.query('20190101 <= sales_ymd <= 20191231').groupby("customer_id").agg(amount_2019=("amount","sum")).reset_index(), how="left")
tmp = pd.merge(tmp, df_receipt.groupby("customer_id").agg(amount_all=("amount","sum")).reset_index(), how="left")
tmp["ratio"] = tmp.amount_2019 / tmp.amount_all
tmp = tmp.fillna(0)
tmp.query('ratio > 0').head(10)
       customer_id  amount_2019  amount_all     ratio
2   CS031415000172       2971.0      5088.0  0.583923
6   CS015414000103        874.0      3122.0  0.279949
12  CS011215000048        248.0      3444.0  0.072009
15  CS029415000023       3767.0      5167.0  0.729050
21  CS035415000029       5823.0      7504.0  0.775986
23  CS023513000066        208.0       771.0  0.269780
24  CS035513000134        463.0      1565.0  0.295847
27  CS001515000263        216.0       216.0  1.000000
30  CS006415000279        229.0       229.0  1.000000
32  CS031415000106        215.0      7741.0  0.027774

085

P-085: 顧客データフレーム(df_customer)の全顧客に対し、郵便番号(postal_cd)を用いて経度緯度変換用データフレーム(df_geocode)を紐付け、新たな df_customer_1 を作成せよ。ただし、複数紐づく場合は経度(longitude)、緯度(latitude)それぞれ平均を算出すること。

df_customer_1 = pd.merge(
    df_customer,
    df_geocode.groupby("postal_cd").agg({"longitude":"mean", "latitude":"mean"}).reset_index(),
    on="postal_cd",
    how="left"
  )
df_customer_1.head(10)
      customer_id customer_name  gender_cd  ...     status_cd  longitude  latitude
0  CS021313000114        大野 あや子          1  ...  0-00000000-0  139.31779  35.41358
1  CS037613000071         六角 雅彦          9  ...  0-00000000-0  139.83502  35.67193
2  CS031415000172       宇多田 貴美子          1  ...  D-20100325-C  139.68965  35.67374
3  CS028811000001        堀井 かおり          1  ...  0-00000000-0  139.48360  35.39125
4  CS001215000145         田崎 美紀          1  ...  6-20090929-2  139.70775  35.54084
5  CS020401000016         宮下 達士          0  ...  0-00000000-0  139.67245  35.77073
6  CS015414000103         奥野 陽子          1  ...  B-20100609-B  139.83601  35.67818
7  CS029403000008          釈 人志          0  ...  0-00000000-0  139.90469  35.65422
8  CS015804000004         松谷 米蔵          0  ...  0-00000000-0  139.83601  35.67818
9  CS033513000180          安斎 遥          1  ...  6-20080506-5  139.51463  35.45013

[10 rows x 13 columns]

086

P-086: 前設問で作成した緯度経度つき顧客データフレーム(df_customer_1)に対し、申込み店舗コード(application_store_cd)をキーに店舗データフレーム(df_store)と結合せよ。そして申込み店舗の緯度(latitude)・経度情報(longitude)と顧客の緯度・経度を用いて距離(km)を求め、顧客 ID(customer_id)、顧客住所(address)、店舗住所(address)とともに表示せよ。計算式は簡易式で良いものとするが、その他精度の高い方式を利用したライブラリを利用してもかまわない。結果は 10 件表示すれば良い。

緯度(ラジアン):\phi \\
経度(ラジアン):\lambda \\
距離L = 6371 * arccos(sin \phi_1 * sin \phi_2
+ cos \phi_1 * cos \phi_2 * cos(\lambda_1 − \lambda_2))
tmp = pd.merge(
    df_customer_1[["customer_id", "application_store_cd", "longitude", "latitude"]],
    df_store[["store_cd", "longitude", "latitude"]],
    how="left",
    left_on="application_store_cd",
    right_on="store_cd"
  )
x1, y1, x2, y2 = map(np.radians, [tmp.longitude_x, tmp.latitude_x, tmp.longitude_y, tmp.latitude_y])

df_customer_1["distance"] = 6371*np.arccos(np.sin(y1) * np.sin(y2) + np.cos(y1) * np.cos(y2) * np.cos(x1-x2))
df_customer_1.head(10)
      customer_id customer_name  gender_cd  ...  longitude  latitude  distance
0  CS021313000114        大野 あや子          1  ...  139.31779  35.41358  1.394409
1  CS037613000071         六角 雅彦          9  ...  139.83502  35.67193  1.451182
2  CS031415000172       宇多田 貴美子          1  ...  139.68965  35.67374  0.411733
3  CS028811000001        堀井 かおり          1  ...  139.48360  35.39125  8.065196
4  CS001215000145         田崎 美紀          1  ...  139.70775  35.54084  1.268421
5  CS020401000016         宮下 達士          0  ...  139.67245  35.77073  4.185905
6  CS015414000103         奥野 陽子          1  ...  139.83601  35.67818  1.449673
7  CS029403000008          釈 人志          0  ...  139.90469  35.65422  0.804858
8  CS015804000004         松谷 米蔵          0  ...  139.83601  35.67818  1.449673
9  CS033513000180          安斎 遥          1  ...  139.51463  35.45013  1.956947

[10 rows x 14 columns]

087

P-087: 顧客データフレーム(df_customer)では、異なる店舗での申込みなどにより同一顧客が複数登録されている。名前(customer_name)と郵便番号(postal_cd)が同じ顧客は同一顧客とみなし、1 顧客 1 レコードとなるように名寄せした名寄顧客データフレーム(df_customer_u)を作成せよ。ただし、同一顧客に対しては売上金額合計が最も高いものを残すものとし、売上金額合計が同一もしくは売上実績の無い顧客については顧客 ID(customer_id)の番号が小さいものを残すこととする。

ソートしてdrop_duplicates

tmp = pd.merge(
    df_customer,
    df_receipt.groupby('customer_id').agg({"amount":"sum"}).reset_index(),
    how = "left",
    on ="customer_id"
).sort_values(["amount", "customer_id"], ascending=[False, True])


df_customer_u = tmp.drop_duplicates(subset=["customer_name", "postal_cd"], keep="first")
df_customer_u.head(10)
          customer_id customer_name  ...     status_cd   amount
16905  CS017415000097         福士 千夏  ...  F-20101006-F  23086.0
12692  CS015415000185        岩淵 はるみ  ...  F-20101014-F  20153.0
13550  CS031414000051        長澤 沙知絵  ...  F-20101009-F  19202.0
4808   CS028415000007         紺野 あい  ...  F-20100922-F  19127.0
14205  CS001605000009         安部 耕司  ...  F-20101019-E  18925.0
14760  CS010214000010         高嶋 芽以  ...  F-20100909-F  18585.0
15709  CS006515000023        竹村 はるみ  ...  F-20100831-F  18372.0
6353   CS016415000141         西谷 愛梨  ...  F-20100611-F  18372.0
21458  CS011414000106          紺野 窈  ...  F-20101028-F  18338.0
20336  CS038415000104        城戸 しほり  ...  F-20100922-F  17847.0

[10 rows x 12 columns]

088

P-088: 前設問で作成したデータを元に、顧客データフレームに統合名寄 ID を付与したデータフレーム(df_customer_n)を作成せよ。ただし、統合名寄 ID は以下の仕様で付与するものとする。

  • 重複していない顧客:顧客 ID(customer_id)を設定
  • 重複している顧客:前設問で抽出したレコードの顧客 ID を設定
df_customer_n = pd.merge(
    df_customer,
    df_customer_u[["customer_name", "postal_cd", "customer_id"]],
    how="left", on =["customer_name", "postal_cd"]
).rename(columns={"customer_id_x":"customer_id", "customer_id_y":"new_customer_id"})


df_customer_n.head(10)
      customer_id customer_name  ...     status_cd new_customer_id
0  CS021313000114        大野 あや子  ...  0-00000000-0  CS021313000114
1  CS037613000071         六角 雅彦  ...  0-00000000-0  CS037613000071
2  CS031415000172       宇多田 貴美子  ...  D-20100325-C  CS031415000172
3  CS028811000001        堀井 かおり  ...  0-00000000-0  CS028811000001
4  CS001215000145         田崎 美紀  ...  6-20090929-2  CS001215000145
5  CS020401000016         宮下 達士  ...  0-00000000-0  CS020401000016
6  CS015414000103         奥野 陽子  ...  B-20100609-B  CS015414000103
7  CS029403000008          釈 人志  ...  0-00000000-0  CS029403000008
8  CS015804000004         松谷 米蔵  ...  0-00000000-0  CS015804000004
9  CS033513000180          安斎 遥  ...  6-20080506-5  CS033513000180

[10 rows x 12 columns]

089

P-089: 売上実績のある顧客に対し、予測モデル構築のため学習用データとテスト用データに分割したい。それぞれ 8:2 の割合でランダムにデータを分割せよ。

from sklearn.model_selection import train_test_split

df_train, df_test = train_test_split(df_customer_u.query("amount>0"), test_size=0.2)

090

P-090: レシート明細データフレーム(df_receipt)は 2017 年 1 月 1 日〜2019 年 10 月 31 日までのデータを有している。売上金額(amount)を月次で集計し、学習用に 12 ヶ月、テスト用に 6 ヶ月のモデル構築用データを 3 セット作成せよ。

pandas.Grouperを使う

tmp = df_receipt.copy()
tmp.sales_ymd = pd.to_datetime(tmp.sales_ymd, format='%Y%m%d')
group = tmp.groupby(pd.Grouper(key='sales_ymd', freq='1m')).agg({"amount":"sum"}).reset_index()
df_train1, df_test1 = group.loc[:11], group.loc[:5]
df_train2, df_test2 = group.loc[12:23], group.loc[12:17]
df_train3, df_test3 = group.loc[24:], group.loc[24:30]

動作検証は実行ファイル参照