見出し画像

『Python実践データ分析100本ノック』ノック36〜45

今回は、『Python実践データ分析100本ノック』で学んだことをアウトプットします。

indexの初期化

機械学習を行う場合は、欠損値の対応を行う必要があります。今回は欠損値を除去し、対象顧客を6ヶ月以上滞在している顧客に絞ります。

predict_data = predict_data.dropna()
predict_data.head()

画像1

欠損値の除去は、dropna()で行います。
欠損値を含む行が除去されたので、インデックスが飛び飛びになっているのが分かります。

なので、reset_index()で、インデックスを初期化します。
引数dropをTrueとすると、元のindexは削除され残らなくなります。

predict_data = predict_data.dropna()
predict_data = predict_data.reset_index(drop=True)
predict_data.head()

画像2

indexが初期化されていることが確認できました。

ノック42で直面したエラーと解決策

ノック42において、まず退会した顧客に絞り込んで、end_date列の1ヶ月前の年月を取得し、ノック41で整形したuselogとcustomer_id、年月をキーにして結合する本書のコードを入力して、実行してみました。

from dateutil.relativedelta import relativedelta
exit_customer = customer.loc[customer['is_deleted'] == 1]
exit_customer['exit_date'] = None
exit_customer['end_date'] = pd.to_datetime(exit_customer['end_date'])
for i in range(len(exit_customer)):
   exit_customer['exit_date'].iloc[i] = exit_customer['end_date'].iloc[i] - relativedelta(months=1)
exit_customer['年月'] = exit_customer['exit_date'].dt.strftime('%Y%m')
uselog['年月'] = uselog['年月'].astype(str)
exit_uselog = pd.merge(uselog, exit_customer, on=['customer_id', '年月'], how='left')
print(len(uselog))
exit_uselog.head()

画像3

どうやら、exit_customer['exit_date']列の型がdatetimeになっていないのが原因かなと予想。

ググってみると、同じようなエラーに直面した方がいて、teratailにて質問していたので、解決方法を参考させて頂きました。

やはり、exit_customer['exit_date']列の型(dtype)をdatetimeに変換する必要があるようです。

from dateutil.relativedelta import relativedelta
exit_customer = customer.loc[customer['is_deleted'] == 1]
exit_customer['exit_date'] = None
exit_customer['end_date'] = pd.to_datetime(exit_customer['end_date'])
for i in range(len(exit_customer)):
   exit_customer['exit_date'].iloc[i] = exit_customer['end_date'].iloc[i] - relativedelta(months=1)
exit_customer['exit_date'] = pd.to_datetime(exit_customer['exit_date'])
exit_customer['年月'] = exit_customer['exit_date'].dt.strftime('%Y%m')
uselog['年月'] = uselog['年月'].astype(str)
exit_uselog = pd.merge(uselog, exit_customer, on=['customer_id', '年月'], how='left')
print(len(uselog))
exit_uselog.head()

特定の行・列に欠損値がある列・行を削除するsubset

参考にしたのは、こちらのサイト

欠損値を除外(削除)するdropna()で、特定の行・列を基準に削除したい場合は、引数subsetに対象としたい行ラベル・列ラベルをリストで指定します。

リストである必要があるので、対象が一つでもsubset=['name']のように指定する必要があります。デフォルトではsubsetで指定した列に欠損値がある行を削除します。

exit_uselog = exit_uselog.dropna(subset=['name'])
print(len(exit_uselog))
print(len(exit_uselog['customer_id'].unique()))
exit_uselog.head()

画像4

本書では、name列の欠損値がある行を削除していることが分かります。

アンダーサンプリング

先述の退会データは1104件でした。それに対して継続顧客のデータは27422件あり、その全てを継続顧客のデータにする場合、不均衡なデータとなってしまいます。

このように、数%しか片方のデータがない場合は、サンプル数を調整していくことになります。

そこで、まずは、簡単に、継続顧客も、顧客あたり1件になるようにアンダーサンプリングをしていきます。つまり、2018年5月のAさんか2018年12月のAさんのどちらかを選ぶということです。

そのために、データをシャッフルして、重複を除外する方法を取ります。

sample()で、pandasの行をシャッフル

参考にしたのは、こちらのサイト

sample()メソッドを活用すると、pandas.DataFrame, pandas.Seriesの行をランダムに並び替える(シャッフルする)ことができます。

引数frac=1とすると、すべての行数分のランダムサンプリングをすることになり、全体をランダムに並び替える(シャッフルする)ことに等しくなります。

drop_duplicates()で重複した行を削除

重複した行を削除するには、drop_duplicates()を使用します。
引数に先述のsubsetを指定すると、重複判定する列を指定できます。

conti_uselog = conti_uselog.sample(frac=1).reset_index(drop=True)
conti_uselog = conti_uselog.drop_duplicates(subset='customer_id')
print(len(conti_uselog))
conti_uselog.head()

画像5

コードブロックの1行目でデータのシャッフルを行い、2行目でcustomer_idが重複しているデータは最初のデータのみを取得します。件数は2842件まで減りました。

今回の学びのまとめ

○reset_index()で、インデックスを初期化する。引数dropをTrueとすると、元のindexは削除され残らなくなる。

○ノック42で直面したエラーと解決策

○欠損値を除外(削除)するdropna()で、特定の行・列を基準に削除したい場合は、引数subsetに対象としたい行ラベル・列ラベルをリストで指定。

○数%しか片方のデータがない場合は、サンプル数を調整するアンダーサンプリングをおこなう。

○sample()メソッドを活用すると、行をランダムに並び替える(シャッフルする)ことができる。引数frac=1とすると、すべての行数分のランダムサンプリングをすることになり、全体をランダムに並び替えることになる。

○重複した行を削除するには、drop_duplicates()を使用する。引数にsubsetを指定すると、重複判定する列を指定できる。

サポート、本当にありがとうございます。サポートしていただいた金額は、知的サイドハッスルとして取り組んでいる、個人研究の費用に充てさせていただきますね♪