『Python実践データ分析100本ノック』ノック36〜45
今回は、『Python実践データ分析100本ノック』で学んだことをアウトプットします。
indexの初期化
機械学習を行う場合は、欠損値の対応を行う必要があります。今回は欠損値を除去し、対象顧客を6ヶ月以上滞在している顧客に絞ります。
predict_data = predict_data.dropna()
predict_data.head()
欠損値の除去は、dropna()で行います。
欠損値を含む行が除去されたので、インデックスが飛び飛びになっているのが分かります。
なので、reset_index()で、インデックスを初期化します。
引数dropをTrueとすると、元のindexは削除され残らなくなります。
predict_data = predict_data.dropna()
predict_data = predict_data.reset_index(drop=True)
predict_data.head()
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()
どうやら、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()
本書では、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()
コードブロックの1行目でデータのシャッフルを行い、2行目でcustomer_idが重複しているデータは最初のデータのみを取得します。件数は2842件まで減りました。
今回の学びのまとめ
○reset_index()で、インデックスを初期化する。引数dropをTrueとすると、元のindexは削除され残らなくなる。
○ノック42で直面したエラーと解決策
○欠損値を除外(削除)するdropna()で、特定の行・列を基準に削除したい場合は、引数subsetに対象としたい行ラベル・列ラベルをリストで指定。
○数%しか片方のデータがない場合は、サンプル数を調整するアンダーサンプリングをおこなう。
○sample()メソッドを活用すると、行をランダムに並び替える(シャッフルする)ことができる。引数frac=1とすると、すべての行数分のランダムサンプリングをすることになり、全体をランダムに並び替えることになる。
○重複した行を削除するには、drop_duplicates()を使用する。引数にsubsetを指定すると、重複判定する列を指定できる。