【解説】データサイエンス100本ノック【問91回答】

【解説】データサイエンス100本ノック【問91回答】

当ページのリンクには広告が含まれています。


目次


データサイエンス100本ノックの始め方は、以下の記事を参考にしていただければと思います。


>>データサイエンス100本ノックの始め方を確認する


第91問目:不均衡データ(アンダーサンプリング)

P-091: 顧客データフレーム(df_customer)の各顧客に対し、売上実績のある顧客数と売上実績のない顧客数が1:1となるようにアンダーサンプリングで抽出せよ。

アンダーサンプリングとは、偏りがあるデータを均一にするアプローチです。

例えば全体のデータの数として100個あった時に、野菜のデータが80個、果物のデータが20個となっている場合が該当します。

この場合、果物のデータが20個あるので、野菜のデータも20個にしてあげることで、機械学習で分類モデルを作成する際のデータの偏りを解決します。

アンダーサンプリングを行う場合、imblearn.under_sampling.RandomUnderSamplerクラスを使用します。

https://imbalanced-learn.org/stable/references/generated/imblearn.under_sampling.RandomUnderSampler.html

RandomUnderSamplerクラスのインスタンスを生成し、fit_sample(X, y)のようにすることで、yの値が均等になるようにアンダーサンプリングがXに対して行われます。

戻り値あ、アンダーサンプリングされたXとyになります。

簡単なサンプルコードで使い方を確認しましょう。

サンプル
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
from imblearn.under_sampling import RandomUnderSampler

columns = ['product', 'price']
data = [['pen', 200], ['book', 500], ['pen', 120], ['book', 500], ['book', 1200]]
df = pd.DataFrame(data, columns=columns)

target =[0, 0, 0, 1, 1]
rs = RandomUnderSampler(random_state=71)

df_sample, _ = rs.fit_resample(df, target)

print(df_sample)
出力
1
2
3
4
5
  product  price
0 pen 200
1 pen 120
2 book 500
3 book 1200

問題に取り掛かります。まずはdf_customerの構造を確認します。

1
df_customer.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	customer_name	gender_cd	gender	birth_day	age	postal_cd	address	application_store_cd	application_date	status_cd
0 CS021313000114 大野 あや子 1 女性 1981-04-29 37 259-1113 神奈川県伊勢原市粟窪********** S14021 20150905 0-00000000-0
1 CS037613000071 六角 雅彦 9 不明 1952-04-01 66 136-0076 東京都江東区南砂********** S13037 20150414 0-00000000-0
2 CS031415000172 宇多田 貴美子 1 女性 1976-10-04 42 151-0053 東京都渋谷区代々木********** S13031 20150529 D-20100325-C
3 CS028811000001 堀井 かおり 1 女性 1933-03-27 86 245-0016 神奈川県横浜市泉区和泉町********** S14028 20160115 0-00000000-0
4 CS001215000145 田崎 美紀 1 女性 1995-03-29 24 144-0055 東京都大田区仲六郷********** S13001 20170605 6-20090929-2
5 CS020401000016 宮下 達士 0 男性 1974-09-15 44 174-0065 東京都板橋区若木********** S13020 20150225 0-00000000-0
6 CS015414000103 奥野 陽子 1 女性 1977-08-09 41 136-0073 東京都江東区北砂********** S13015 20150722 B-20100609-B
7 CS029403000008 釈 人志 0 男性 1973-08-17 45 279-0003 千葉県浦安市海楽********** S12029 20150515 0-00000000-0
8 CS015804000004 松谷 米蔵 0 男性 1931-05-02 87 136-0073 東京都江東区北砂********** S13015 20150607 0-00000000-0
9 CS033513000180 安斎 遥 1 女性 1962-07-11 56 241-0823 神奈川県横浜市旭区善部町********** S14033 20150728 6-20080506-5

売上情報としてdf_receiptの構造も確認します。

1
df_receipt.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	sales_ymd	sales_epoch	store_cd	receipt_no	receipt_sub_no	customer_id	product_cd	quantity	amount
0 20181103 1541203200 S14006 112 1 CS006214000001 P070305012 1 158
1 20181118 1542499200 S13008 1132 2 CS008415000097 P070701017 1 81
2 20170712 1499817600 S14028 1102 1 CS028414000014 P060101005 1 170
3 20190205 1549324800 S14042 1132 1 ZZ000000000000 P050301001 1 25
4 20180821 1534809600 S14025 1102 2 CS025415000050 P060102007 1 90
5 20190605 1559692800 S13003 1112 1 CS003515000195 P050102002 1 138
6 20181205 1543968000 S14024 1102 2 CS024514000042 P080101005 1 30
7 20190922 1569110400 S14040 1102 1 CS040415000178 P070501004 1 128
8 20170504 1493856000 S13020 1112 2 ZZ000000000000 P071302010 1 770
9 20191010 1570665600 S14027 1102 1 CS027514000015 P071101003 1 680

全体の流れとしては以下のような流れで処理を進めます。

  1. 顧客ごとの売上金額の合計を算出する
  2. 顧客情報に売上金額の合計を追加する
  3. 売上金額合計がない(NaN)のものは0とし、あるものは1とする。このデータは”buy_flg”カラムに結果を代入する
  4. アンダーサンプリングを実施。

まずは顧客ごとの売上金額の合計を算出します。
顧客ごとの売上金額の合計は、groupbyメソッドを使えばよかったですね。

1
2
df_tmp = df_receipt.groupby('customer_id').agg({'amount':'sum'}).reset_index()
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	amount
0 CS001113000004 1298
1 CS001114000005 626
2 CS001115000010 3044
3 CS001205000004 1988
4 CS001205000006 3337
5 CS001211000025 456
6 CS001212000027 448
7 CS001212000031 296
8 CS001212000046 228
9 CS001212000070 456

顧客情報に売上金額の合計を追加していきます。 売上金額の合計を追加するには、mergeメソッドで左外部結合を実施します。

1
2
df_tmp = pd.merge(df_customer, df_tmp, how='left', on='customer_id')
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
12

customer_id customer_name gender_cd gender birth_day age postal_cd address application_store_cd application_date status_cd amount
0 CS021313000114 大野 あや子 1 女性 1981-04-29 37 259-1113 神奈川県伊勢原市粟窪********** S14021 20150905 0-00000000-0 NaN
1 CS037613000071 六角 雅彦 9 不明 1952-04-01 66 136-0076 東京都江東区南砂********** S13037 20150414 0-00000000-0 NaN
2 CS031415000172 宇多田 貴美子 1 女性 1976-10-04 42 151-0053 東京都渋谷区代々木********** S13031 20150529 D-20100325-C 5088.0
3 CS028811000001 堀井 かおり 1 女性 1933-03-27 86 245-0016 神奈川県横浜市泉区和泉町********** S14028 20160115 0-00000000-0 NaN
4 CS001215000145 田崎 美紀 1 女性 1995-03-29 24 144-0055 東京都大田区仲六郷********** S13001 20170605 6-20090929-2 875.0
5 CS020401000016 宮下 達士 0 男性 1974-09-15 44 174-0065 東京都板橋区若木********** S13020 20150225 0-00000000-0 NaN
6 CS015414000103 奥野 陽子 1 女性 1977-08-09 41 136-0073 東京都江東区北砂********** S13015 20150722 B-20100609-B 3122.0
7 CS029403000008 釈 人志 0 男性 1973-08-17 45 279-0003 千葉県浦安市海楽********** S12029 20150515 0-00000000-0 NaN
8 CS015804000004 松谷 米蔵 0 男性 1931-05-02 87 136-0073 東京都江東区北砂********** S13015 20150607 0-00000000-0 NaN
9 CS033513000180 安斎 遥 1 女性 1962-07-11 56 241-0823 神奈川県横浜市旭区善部町********** S14033 20150728 6-20080506-5 868.0

売上金額の合計が無い(NaN)のものは0とし、あるものは1にします。

1
2
df_tmp['buy_flg'] = df_tmp['amount'].apply(lambda x : 0 if np.isnan(x) else 1)
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	customer_name	gender_cd	gender	birth_day	age	postal_cd	address	application_store_cd	application_date	status_cd	amount	buy_flg
0 CS021313000114 大野 あや子 1 女性 1981-04-29 37 259-1113 神奈川県伊勢原市粟窪********** S14021 20150905 0-00000000-0 NaN 0
1 CS037613000071 六角 雅彦 9 不明 1952-04-01 66 136-0076 東京都江東区南砂********** S13037 20150414 0-00000000-0 NaN 0
2 CS031415000172 宇多田 貴美子 1 女性 1976-10-04 42 151-0053 東京都渋谷区代々木********** S13031 20150529 D-20100325-C 5088.0 1
3 CS028811000001 堀井 かおり 1 女性 1933-03-27 86 245-0016 神奈川県横浜市泉区和泉町********** S14028 20160115 0-00000000-0 NaN 0
4 CS001215000145 田崎 美紀 1 女性 1995-03-29 24 144-0055 東京都大田区仲六郷********** S13001 20170605 6-20090929-2 875.0 1
5 CS020401000016 宮下 達士 0 男性 1974-09-15 44 174-0065 東京都板橋区若木********** S13020 20150225 0-00000000-0 NaN 0
6 CS015414000103 奥野 陽子 1 女性 1977-08-09 41 136-0073 東京都江東区北砂********** S13015 20150722 B-20100609-B 3122.0 1
7 CS029403000008 釈 人志 0 男性 1973-08-17 45 279-0003 千葉県浦安市海楽********** S12029 20150515 0-00000000-0 NaN 0
8 CS015804000004 松谷 米蔵 0 男性 1931-05-02 87 136-0073 東京都江東区北砂********** S13015 20150607 0-00000000-0 NaN 0
9 CS033513000180 安斎 遥 1 女性 1962-07-11 56 241-0823 神奈川県横浜市旭区善部町********** S14033 20150728 6-20080506-5 868.0 1

アンダーサンプリングを実施していきます。

1
2
3
4
5
6
7
8
print('アンダーサンプリング前')
print('0の件数', len(df_tmp.query('buy_flg == 0')))
print('1の件数', len(df_tmp.query('buy_flg == 1')))
rs = RandomUnderSampler(random_state=1)
df_sample, _ = rs.fit_resample(df_tmp, df_tmp.buy_flg)
print('アンダーサンプリング後')
print('0の件数', len(df_sample.query('buy_flg == 0')))
print('1の件数', len(df_sample.query('buy_flg == 1')))
出力
1
2
3
4
5
6
アンダーサンプリング前
0の件数 13665
1の件数 8306
アンダーサンプリング後
0の件数 8306
1の件数 8306

まとめ:アンダーサンプリングを学びました。

本記事で紹介した方法を元にデータサイエンティストとしての知見を深めていただければと思います。

データサイエンティストに必要な知識は、TechAcademyのデータサイエンスコースでの学習がおすすめです。

無料体験可能なのでご確認ください。

コメント