6 minutes
データサイエンス100本ノックをPythonでやってみた [81-90]
データサイエンス 100 本ノックとは
The-Japan-DataScientist-Society/100knocks-preprocess: データサイエンス 100 本ノック(構造化データ加工編)
Google Colab で データサイエンス 100 本ノックをするにあたって
以下の ipynb ファイルを使用させていただきました。
実行ファイル
データサイエンス 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]