【初心者が挑戦中】 Python実践データ分析100本ノックをやってみた

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

Python実践データ分析100本ノックが良書という口コミはよく見るけど、初心者が Python実践データ分析100本ノックをやってもわかるのか!?

その疑問を解決するべく、データサイエンス初心者の筆者が挑戦中です!
使っている本は、名著と有名の Python実践データ分析100本ノックです。
非常にわかりやすいです。レビューでも実用的と高評価ですね。

データ分析の前半部分解説、実践していきますので、一緒に頑張りましょう。

Python実践データ分析100本ノック を持ちながら一緒にやって行くスタイルが良いと思います。

目次

第1部 基礎編:データ加工
第1章-1 ウェブからの注文数を分析する10本ノック

ノック1:データを読み込んでみよう

import pandas as pd
customer_master = pd.read_csv('customer_master.csv')

pandas を pd という名前でインポートし、”customer_master.csv’というファイルを読み込み、customer_master という名前に代入する感じですね。

ノック2:データを縦方向へ結合(ユニオン)してみよう

transaction = pd.concat([transaction_1, transaction_2], ignore_index=True)

concatenate :連結する
ignore_index=True: indexを振り直して連番にする。って意味です。

ノック3:売上データ同士を結合(ジョイン)してみよう

join_data =
pd.merge(transaction_detail, transaction[["transaction_id", "payment_date", "customer_id"]], on="transaction_id", how="left")

transaction_detailを左側に置いて、tansaction[[“transaction_id”, “payment_date”, “customer_id”]]をマージさせるイメージ。

ノック4:マスターデータを結合(ジョイン)してみよう

join_data = pd.merge(join_data, customer_master, on="customer_id", how="left")
join_data = pd.merge(join_data, item_master, on="item_id", how="left")
join_data.head()

さっきと同じ要領でcustomer_masterとitem_masterをジョインする。

ノック5:必要なデータ列を作ろう

quntity(量)とitem_price(単価)を掛け算して、売り上げの列を作りましょう

join_data["price"] = join_data["quantity"] * join_data["item_price"]

ここまでで、ノック1〜5は終わりです。
今は訳も分からず作業しているだけな感じもしますが、ここからが面白くなってくるので、しばらくの辛抱ですね。

第1章-2 ウェブからの注文数を分析する10本ノック後半

ノック6:データ検算をしよう

join_data["price"].sum() == transaction["price"].sum()

ノック5で作成した price の列の合計とノック2で作ったtransaction の合計が同じことを確認しましょう。 True となれば正解です!

ノック7:各種統計量を把握しよう

まず、大事なこととして、データに欠損値があるか確認しましょう。

join_data.isnull().sum()

次に、最小値、最大値、標準偏差、データ件数などの一覧表示をしましょう

join_data.describe()

ノック8:月別でデータを集計してみよう

まず、データの型を確認しましょう

join_data.dtypes

次にpayment_date の型をdatetime 型に変えましょう。
2行目は、dt.strftime(“%Y%m”) により、年月の部分を切り取って表示します。

join_data["payment_date"] = pd.to_datetime(join_data["payment_date"])
join_data["payment_month"] = join_data["payment_date"].dt.strftime("%Y%m")
join_data[["payment_date", "payment_month"]].head()

groupby を使って、payment_month の列毎に、売り上げの合計を表示します

join_data.groupby("payment_month").sum()["price"]

ノック9:月別、商品別でデータを集計してみよう

まずはさっきと同じようにgroupby を使って、payment_monthとitem_name の列毎に、売り上げの合計と売り上げ数を表示してみます

join_data.groupby(["payment_month","item_name"]).sum()[["price", "quantity"]]

いまいちわかりにくかったので、今度はピボットテーブルで作ってみましょう。
indexは行、columnは列を示します。

pd.pivot_table(join_data, index='item_name', columns='payment_month', values=['price', 'quantity'], aggfunc='sum')

こちらの方がまとまってますが、グラフにしてより可視化したいですよね。次で総仕上げしましょう。

ノック10:商品別の売上推移を可視化してみよう

最後に、ピボットテーブルを作成した後、 matplotlibを用いて、グラフを作ってみましょう!

graph_data = pd.pivot_table(join_data, index='payment_month', columns='item_name', values='price', aggfunc='sum')
graph_data.head()
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(list(graph_data.index), graph_data["PC-A"], label='PC-A')
plt.plot(list(graph_data.index), graph_data["PC-B"], label='PC-B')
plt.plot(list(graph_data.index), graph_data["PC-C"], label='PC-C')
plt.plot(list(graph_data.index), graph_data["PC-D"], label='PC-D')
plt.plot(list(graph_data.index), graph_data["PC-E"], label='PC-E')
plt.legend()

お疲れ様でした。これで1章は終了です。次回は2章に進んでいきたいと思います。

第2章-1  小売店のデータでデータ加工を行う10本 ノック前半

ノック11:データを読み込んでみよう

import pandas as pd
uriage_data = pd.read_csv(“uriage.csv”)
uriage_data.head()

まず、今まで通りの方法で、uriageのcsvファイルを読み込みましょう。

ノック12:データの揺れを見てみよう

データの揺れとは、同じ内容で異なる表記がされていることや入力ミスなどで異なり表現がされていることです。

「山田太郎」を「山田 太郎」「山田たろう」「山田さん」←これが揺れです!

uriage_data[“item_name”].head()

売り上げデータの、[“item_name”]を抽出すると、

商品A,商品a,商品 A などが混在しているのがわかります。

uriage_data[“item_price”].head()

売り上げデータの、[“item_price”]を抽出すると、

欠損値NaNが多数あるのがわかります。

ノック13:データに揺れがあるまま集計しよう

一旦そのまま今までの方法で集計してみましょう。

uriage_data[“purchase_date”] = pd.to_datetime(uriage_data[“purchase_date”])

pd.to_datetime はノック8で出てきた、日付を年月に直す内容です。

復習してみてください。

uriage_data[“purchase_month”] = uriage_data[“purchase_date”].dt.strftime(“%Y%m”)
res = uriage_data.pivot_table(index=”purchase_month”, columns=”item_name”, aggfunc=”size”, fill_value=0)
res

pivot_table を作りましょう、 fill_value=0 はNaN を0で埋めるという意味です。

res = uriage_data.pivot_table(index=”purchase_month”, columns=”item_name”, values=”item_price”, aggfunc=”sum”, fill_value=0)
res

商品名がずらっと並んでいますね。

データの揺れが生じている事がわかります。

ノック14:商品名の揺れを補正しよう

まずはlen でデータ件数を調べます。 pd.unique は重複を除外した件数です。

print(len(pd.unique(uriage_data[“item_name”])))

A-Z で26種のはずが、99種になってます。

本来同じ商品が別々にカウントされている事がわかります。

下三段は揺れの解消です

1: upper で全て大文字に

2,3 : replace で全、半角スペースを無くしてます

uriage_data["item_name"] = uriage_data["item_name"].str.upper()
uriage_data[“item_name”] = uriage_data[“item_name”].str.replace(“ ”, “”)
uriage_data[“item_name”] = uriage_data[“item_name”].str.replace(” “, “”)
uriage_data.sort_values(by=[“item_name”], ascending=True)
print(pd.unique(uriage_data["item_name"]))
print(len(pd.unique(uriage_data[“item_name”])))

ユニークなデータ件数を調べたところ26件になりました。

これでOKです。

ノック15:金額欠損値の補完をしよう

まず、データの欠損値を確認しましょう。

isnull() 関数を使います。 (axis=0) は縦方向でという意味です。

uriage_data.isnull().any(axis=0)

flg_is_null = uriage_data[“item_price”].isnull()

for trg in list(uriage_data.loc[flg_is_null, “item_name”].unique()):

item_name の flg_is_null に条件付けして、list() によってリスト化する文です。

price = uriage_data.loc[(~flg_is_null) & (uriage_data[“item_name”] == trg), “item_price”].max()

(~flg_is_null) は flg_is_null==False の意味です。

uriage_data[“item_price”].loc[(flg_is_null) & (uriage_data[“item_name”]==trg)] = price
uriage_data.head()

uriage_data.isnull().any(axis=0)

item_price False になっていることから、

item_price の欠損値をなくすことに成功しました。

for trg in list(uriage_data[“item_name”].sort_values().unique()):
print(trg + “の最大額:” + str(uriage_data.loc[uriage_data[“item_name”]==trg][“item_price”].max()) + “の最小額:” + str(uriage_data.loc[uriage_data[“item_name”]==trg][“item_price”].min(skipna=False)))

全ての商品に対してループ処理を実施して、

結果が画面上に表示する事ができました。

ここまでで、商品名とその価格の揺れを直す事ができました。次は顧客名の揺れを補正したいと思います。

第2章-1  小売店のデータでデータ加工を行う10本 ノック前半

ノック16:顧客名の揺れを補正しよう

kokyaku_data[“顧客名”].head()
uriage_data[“customer_name”].head()

まず、そのまま表示させてみましょう。

顧客台帳はスペースが含まれるが、売り上げ履歴の顧客名には

スペースが含まれていません。

kokyaku_data[“顧客名”] = kokyaku_data[“顧客名”].str.replace(“ ”, “”)
kokyaku_data[“顧客名”] = kokyaku_data[“顧客名”].str.replace(” “, “”)
kokyaku_data[“顧客名”].head()

ノック14 でやったように、半角、全角スペースを除去します。

ノック17:日付の揺れを補正しよう

まず、日付の形式と数値の形式が混在しているので、数値のものを抽出します。

str.isdigit(): 全ての文字が数字なら真、そうでなければ偽。

2行目:flg.is_serial.sum() は数値として書いてある行数を数えています。

flg_is_serial = kokyaku_data[“登録日”].astype(“str”).str.isdigit()
flg_is_serial.sum()

次に数値やスラッシュ区切りで取り込まれている数値を日付表記に変えます。

fromSerial = pd.to_timedelta(kokyaku_data.loc[flg_is_serial, “登録日”].astype(“float”), unit=”D”) + pd.to_datetime(“1900/01/01”)
fromSerial

fromString = pd.to_datetime(kokyaku_data.loc[~flg_is_serial, “登録日”])
fromString

concat で結合してデータを見てましょう。

kokyaku_data["登録日"] = pd.concat([fromSerial, fromString])
kokyaku_data

いい感じですね。

次は、登録年月と顧客数をgroupby で並べて集計しましょう。

そして、データ数も len でカウントします。

kokyaku_data[“登録年月”] = kokyaku_data[“登録日”].dt.strftime(“%Y%m”)
rslt = kokyaku_data.groupby(“登録年月”).count()[“顧客名”]
print(rslt)
print(len(kokyaku_data))

登録日に数値データが残っていないか確認しましょう。

flg_is_serial.sum() が 0になっていればOKです。

flg_is_serial = kokyaku_data[“登録日”].astype(“str”).str.isdigit()
flg_is_serial.sum()

ノック18:顧客名をキーに2つのデータを結合(ジョイン)しよう

ノック3でやった内容を思い出して、マージさせてみましょう。

customer_name は消しましょう。

join_data = pd.merge(uriage_data, kokyaku_data, left_on=”customer_name”, right_on=”顧客名”, how=”left”)
join_data = join_data.drop(“customer_name”, axis=1)
join_data

ノック19:クレンジングしたデータをダンプ(ファイル出力)しよう

ファイル出力するまえにデータ列を再配置しましょう。

dump_data = join_data[[“purchase_date”, “purchase_month”, “item_name”, “item_price”, “顧客名”, “かな”, “地域”, “メールアドレス”, “登録日”]]
dump_data

最後にcsvファイルに出力しましょう。

dump_data.to_csv(“dump_data.csv”, index=False)

ノック20:データを集計しよう

まず、ファイルを読み込みます。

import_data = pd.read_csv(“dump_data.csv”) import_data

購入年月ー商品

購入年月ー売り上げ金額

購入年月ー各顧客への販売数

購入年月ー地域での販売数

byItem = import_data.pivot_table(index=”purchase_month”, columns=”item_name”, aggfunc=”size”, fill_value=0) byItem

byPrice = import_data.pivot_table(index=”purchase_month”, columns=”item_name”, values=”item_price”, aggfunc=”sum”, fill_value=0) byPrice
byCustomer = import_data.pivot_table(index=”purchase_month”, columns=”顧客名”, aggfunc=”size”, fill_value=0) byCustomer
byRegion = import_data.pivot_table(index=”purchase_month”, columns=”地域”, aggfunc=”size”, fill_value=0) byRegion

最後に集計期間で購入してないユーザーのチェックをします。

away_data = pd.merge(uriage_data, kokyaku_data, left_on=”customer_name”, right_on=”顧客名”, how=”right”)
away_data[away_data[“purchase_date”].isnull()][[“顧客名”, “メールアドレス”, “登録日”]]
お疲れ様でした。これで2章は終了です。

第2部 実践編①:機械学習
第3章-1 顧客の全体像を把握する10本 ノック前半

ノック21:データを読み込んで把握しよう

今までの方法で、

1.ジムの履歴データ

2.会員データ

3.会員区分データ

4.キャンペーン区分データ

を読み込んでみましょう

import pandas as pd
uselog = pd.read_csv(‘use_log.csv’)
print(len(uselog))
uselog.head()
customer = pd.read_csv(‘customer_master.csv’)
print(len(customer))
customer.head()
class_master = pd.read_csv(‘class_master.csv’)
print(len(class_master))
class_master.head()
campaign_master = pd.read_csv(‘campaign_master.csv’)
print(len(campaign_master))
campaign_master.head()

ノック22:顧客データを整形しよう

customer と class_master を [class] 列をキーとして結合して、

customer_join というデータを作ります。その後、campaign_master も結合します。

customer_join = pd.merge(customer, class_master, on=”class”, how=”left”)
customer_join = pd.merge(customer_join, campaign_master, on=”campaign_id”, how=”left”)
customer_join.head()

欠損値を確認しましょう。

end_date 以外は欠損値がない事がわかります。

customer_join.isnull().sum()

ノック23:顧客データの基礎集計をしよう

ここからは、何を解析するかを決める為、

会員区分、男女数、辞めたかどうかなどの基礎データを集積します。

customer_join.groupby(“class_name”).count()[“customer_id”]
customer_join.groupby(“campaign_name”).count()[“customer_id”]
customer_join.groupby(“gender”).count()[“customer_id”]
customer_join.groupby(“is_deleted”).count()[“customer_id”]

20180401 より後に入会した人を集計しましょう。

customer_join[“start_date”] = pd.to_datetime(customer_join[“start_date”])
customer_start = customer_join.loc[customer_join[“start_date”]>pd.to_datetime(“20180401”)]
print(len(customer_start))

ノック24:最新顧客データの基礎集計をしよう

まずは、最新月の顧客データを確認しましょう。

集計条件は20190331 以降に辞めた、または(|)、退会日データが空欄(isna())

である事です。

customer_join[“end_date”] = pd.to_datetime(customer_join[“end_date”])
customer_newer = customer_join.loc[(customer_join[“end_date”]>=pd.to_datetime(“20190331”))|(customer_join[“end_date”].isna())]
print(len(customer_newer))
customer_newer[“end_date”].unique()

ノック23でやったように、最新月バージョンで

顧客データを集計しましょう。

customer_newer.groupby(“class_name”).count()[“customer_id”]
customer_newer.groupby(“campaign_name”).count()[“customer_id”]
customer_newer.groupby(“gender”).count()[“customer_id”]

ノック25:利用履歴データを集計しよう

まず、顧客ID と月別利用回数を並べて表示してみましょう。

uselog[“usedate”] = pd.to_datetime(uselog[“usedate”])
uselog[“年月”] = uselog[“usedate”].dt.strftime(“%Y%m”)
uselog_months = uselog.groupby([“年月”,”customer_id”],as_index=False).count()
uselog_months.rename(columns={“log_id”:”count”}, inplace=True)
del uselog_months[“usedate”]
uselog_months.head()
uselog_customer = uselog_months.groupby(“customer_id”).agg([“mean”, “median”, “max”, “min” ])[“count”]
uselog_customer = uselog_customer.reset_index(drop=False)
uselog_customer.head()
ここまでで、顧客ごとの月内利用回数をまとめる事ができました。
次は定期的にジムを利用している場合のフラグ(目印)をつけていきましょう。

第3章-1 顧客の全体像を把握する10本 ノック後半

 

今回の目的は、スポーツジムの顧客履歴などを元に顧客の定着を

データ分析する事です。

ノック26:利用履歴データから定期利用フラグを作成しよう

曜日による利用履歴の違いを確認する為、

.dt.weekday を使って月曜日0~日曜日6まで変換します。

log_id を count という名前に変えて、表示します。

uselog["weekday"] = uselog["usedate"].dt.weekday
uselog_weekday = uselog.groupby(["customer_id","年月","weekday"], as_index=False).count()[["customer_id","年月", "weekday","log_id"]]
uselog_weekday.rename(columns={"log_id":"count"}, inplace=True)
uselog_weekday.head()

データを見ると、weekday=5 つまり、土曜日に集中的にきている事がわかります

uselog_weekday = uselog_weekday.groupby(“customer_id”,as_index=False).max()[[“customer_id”, “count”]]
uselog_weekday[“routine_flg”] = 0
uselog_weekday[“routine_flg”] = uselog_weekday[“routine_flg”].where(uselog_weekday[“count”]<4, 1)
uselog_weekday.head()

.where(uselog_weekday[“count”]<4, 1) というのは、

uselog_weekday[“count”]<4 であればそのまま、それ以外は1 の値を入れるという意味です。

ノック27:顧客データと利用履歴データを結合しよう

今までにやってきた方法で、customer_id をキーとして、顧客データと利用履歴データをマージしましょう。

customer_join = pd.merge(customer_join, uselog_customer, on=”customer_id”, how=”left”)
customer_join = pd.merge(customer_join, uselog_weekday[[“customer_id”, “routine_flg”]], on=”customer_id”, how=”left”)
customer_join.head()

最後に、欠損値の数を確認しましょう。

customer_join.isnull().sum()

ノック28:会員期間を計算しよう

次は会員期間の列を追加して、会員期間をいれてみましょう。

from dateutil.relativedelta import relativedelta
customer_join[“calc_date”] = customer_join[“end_date”]
customer_join[“calc_date”] = customer_join[“calc_date”].fillna(pd.to_datetime(“20190430”))
customer_join[“membership_period”] = 0
for i in range(len(customer_join)):
delta = relativedelta(customer_join[“calc_date”].iloc[i], customer_join[“start_date”].iloc[i])
customer_join[“membership_period”].iloc[i] = delta.years*12 + delta.months
customer_join.head()

ノック29:顧客行動の各種統計量を把握しよう

まず、会員の月別利用回数の各種統計量を確認しましょう。

月別の平均の平均、平均の最大などややこしいですが、しっかり理解してみましょう。

customer_join[[“mean”, “median”, “max”, “min”]].describe()

次は、routine_flg をID 毎にカウントします。

その結果、週に4回以上来店しているフラグ会員と、その他を分けられます。

customer_join.groupby(“routine_flg”).count()[“customer_id”]

最後に、会員期間を横軸、会員数を縦軸にしてヒストグラムを作成してみましょう。

import matplotlib.pyplot as plt
%matplotlib inline
plt.hist(customer_join[“membership_period”])

ノック30:退会ユーザーと継続ユーザーの違いを把握しよう

もう一息です。is_deleted=1 の退会会員(1行目)と is_deleted=0 の継続会員(3行目) の特徴を見てみましょう。

customer_end = customer_join.loc[customer_join[“is_deleted”]==1]customer_end.describe()
customer_stay = customer_join.loc[customer_join[“is_deleted”]==0]customer_stay.describe()

最後にcsv ファイルに落としてみましょう。

customer_join.to_csv(“customer_join.csv”, index=False)

お疲れ様でした。これで3章は終了です。次回は4章に進んでいきたいと思います。

この章の目的は、顧客をクラス分けして、顧客毎の利用回数を予測する事です。

いよいよ本格的な機械学習の章に入っていくようです。

第4章-1 顧客の行動を予測する10本 ノック前半

ノック31:データを読み込んで確認しよう

import pandas as pd
uselog = pd.read_csv(‘use_log.csv’)
uselog.isnull().sum()

まず、今まで通りの方法で、use_log のcsvファイルを読み込みましょう。

次にcustomer_join も同様に読み込んだあと、欠損値の確認をしましょう

customer = pd.read_csv(‘customer_join.csv’)customer.isnull().sum()

ノック32:クラスタリングで顧客をグループ化しよう

クラスター化とは、ある特徴量に基づいていくつかの集団にクラス分けする事です。

customer_clustering = customer[[“mean”, “median”,”max”, “min”, “membership_period”]]
customer_clustering.head()

まず、機械学習ライブラリである sikit-learn をインポートした後、K-means法を用いてグルーピングしていきます。k-means法は変数間の距離を基準にグループ化する為、各変数のスケールを合わせる標準化が必要になります。

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
customer_clustering_sc = sc.fit_transform(customer_clustering)
kmeans = KMeans(n_clusters=4, random_state=0)
clusters = kmeans.fit(customer_clustering_sc)
customer_clustering[“cluster”] = clusters.labels_
print(customer_clustering[“cluster”].unique())
customer_clustering.head()

print(customer_clustering[“cluster”].unique()) を見ると、

1,2,3,0 と出ます。これはクラスター0~3 の4つにグループ化出来ているという事です。

ノック33:クラスタリング結果を分析しよう

まず、分析結果を見やすくする為、1行目で列の名前を変えていきます。

2行目で、クラスター毎にデータ件数の集計を行います。

customer_clustering.columns = [“月内平均値”,”月内中央値”, “月内最大値”, “月内最小値”,”会員期間”, “cluster”]
customer_clustering.groupby(“cluster”).count()

データ件数はcluster3が一番多いようです。

次は会員期間の平均を表に表示しましょう。

customer_clustering.groupby(“cluster”).mean()

ノック34:クラスタリング結果を可視化してみよう

次はPCA(主成分分析)を使って次元を減らし、2次元平面に可視化しましょう。

n_components=2 とは2次元に圧縮するという意味です。

from sklearn.decomposition import PCA
X = customer_clustering_sc
pca = PCA(n_components=2)
pca.fit(X)
x_pca = pca.transform(X)
pca_df = pd.DataFrame(x_pca)
pca_df[“cluster”] = customer_clustering[“cluster”]

次に matplotlib で可視化しましょう。

for 文で、クラスター毎に繰り返して、描写します。

import matplotlib.pyplot as plt
%matplotlib inline
for i in customer_clustering[“cluster”].unique():
tmp = pca_df.loc[pca_df[“cluster”]==i]
plt.scatter(tmp[0], tmp[1])

かなり難しいですが、写経でも良いので続けましょう。

ノック35:クラスタリング結果をもとに退会顧客の傾向を把握しよう

まず、データの結合(concat) を行います。その後、クラスター毎に is_deleted

つまり、在籍or退会会員数を並べてみます。

customer_clustering = pd.concat([customer_clustering, customer], axis=1)
customer_clustering.groupby([“cluster”,”is_deleted”],as_index=False).count()[[“cluster”,”is_deleted”,”customer_id”]] customer_clustering.groupby([“cluster”,”routine_flg”],as_index=False).count()[[“cluster”,”routine_flg”,”customer_id”]]

最後に、クラスター毎にroutine-flag つまり、週4回以上来ている人の数を並べて表示しましょう。グループ1,2 は flagあり、つまり継続会員が多い事がわかります。

ここまでで、3章の前半は終わりです。次は3章の後半ノック36-40 へいってみましょう。
このシリーズも中盤から終盤にさしかかってきています。最後まで付いてきてください!

第4章-1 顧客の行動を予測する10本 ノック後半

ノック36:翌月の利用回数予測を行うためのデータ準備をしよう

翌日の利用回数を予測する為、直近の6ヶ月の利用回数をデータとして

読み込ませます。まずは、顧客毎、年月毎にデータを整理します。

uselog["usedate"] = pd.to_datetime(uselog["usedate"])

uselog[“年月”] = uselog[“usedate”].dt.strftime(“%Y%m”)

uselog_months = uselog.groupby([“年月”,”customer_id”],as_index=False).count()

uselog_months.rename(columns={“log_id”:”count”}, inplace=True)

del uselog_months[“usedate”]

uselog_months.head()

for文を使って、過去6ヶ月分の利用データを取得して列に追加していきましょう。難しいですけど写経してもOKと思います。

year_months = list(uselog_months[“年月”].unique())

predict_data = pd.DataFrame()

for i in range(6, len(year_months)):

tmp = uselog_months.loc[uselog_months[“年月”]==year_months[i]]

tmp.rename(columns={“count”:”count_pred”}, inplace=True)

for j inrange(1, 7):

tmp_before = uselog_months.loc[uselog_months[“年月”]==year_months[i-j]]

del tmp_before[“年月”]

tmp_before.rename(columns={“count”:”count_{}”.format(j-1)}, inplace=True)

tmp = pd.merge(tmp, tmp_before, on=”customer_id”, how=”left”)

predict_data = pd.concat([predict_data, tmp], ignore_index=True)

predict_data.head()

欠損値を消すのが1行目

新たにindexを振り直すのが2行目の処理です。

predict_data = predict_data.dropna()

predict_data = predict_data.reset_index(drop=True)

predict_data.head()

ノック37:特徴となる変数を付与しよう

まず、start_dateれ列を追加します。

predict_data = pd.merge(predict_data, customer[[“customer_id”,”start_date”]], on=”customer_id”, how=”left”)

predict_data.head()

次にnow_date 列を作り、start_date との差から、会員期間を作成しましょう。

predict_data[“now_date”] = pd.to_datetime(predict_data[“年月”], format=”%Y%m”)
predict_data[“start_date”] = pd.to_datetime(predict_data[“start_date”])
from dateutil.relativedelta import relativedelta
predict_data[“period”] = None
for i in range(len(predict_data)):
delta = relativedelta(predict_data[“now_date”][i], predict_data[“start_date”][i])
predict_data[“period”][i] = delta.years*12 + delta.months
predict_data.head()

いい感じですね。

次は、いよいよ予測モデルを作ります!

ノック38:来月の利用回数予測モデルを作成しよう

sklearn の線形モデル( linear_model)で解析します。

predict_data = predict_data.loc[predict_data[“start_date”]>=pd.to_datetime(“20180401”)]

from sklearn import linear_model
import sklearn.model_selection
model = linear_model.LinearRegression()
X = predict_data[[“count_0″,”count_1″,”count_2″,”count_3″,”count_4″,”count_5″,”period”]]
y = predict_data[“count_pred”]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)
model.fit(X_train, y_train)

最後に予測精度を計測します。

print(model.score(X_train, y_train))
print(model.score(X_test, y_test))

ノック39:モデルに寄与している変数を確認しよう

ここまで、count0-count5 と会員期間を変数としましたが、どのパラメータが

いちばん大きいか確認しましょう。

coef = pd.DataFrame({“feature_names”:X.columns, “coefficient”:model.coef_})
coef

結果、count0 つまり直近月の回数が最も関連する事がわかります。

ノック40:来月の利用回数を予測しよう

このモデルは、新たにデータを入れるとすぐに予測結果が表示されます。

顧客の利用データを入れると結果がでます。

x1 = [3, 4, 4, 6, 8, 7, 8]
x2 = [2, 2, 3, 3, 4, 6, 8]
x_pred = [x1, x2]model.predict(x_pred)

最後に出力します。

uselog_months.to_csv(“use_log_months.csv”,index=False)

お疲れ様でした。これで4章は終了です。次回はいよいよ【第9回】5章:ノック41ー45に進んでいきたいと思います。

第5章-1 顧客の退会を予測する10本ノック前半


ノック41:データを読み込んで利用データを整形しよう

import pandas as pd
customer = pd.read_csv(‘customer_join.csv’)
uselog_months = pd.read_csv(‘use_log_months.csv’)

for 文で当月と1ヶ月前の利用回数を集計します。

year_months = list(uselog_months[“年月”].unique())
uselog = pd.DataFrame()
for i in range(1, len(year_months)):
tmp = uselog_months.loc[uselog_months[“年月”]==year_months[i]]
tmp.rename(columns={“count”:”count_0″}, inplace=True)
tmp_before = uselog_months.loc[uselog_months[“年月”]==year_months[i-1]]
del tmp_before[“年月”]
tmp_before.rename(columns={“count”:”count_1″}, inplace=True)
tmp = pd.merge(tmp, tmp_before, on=”customer_id”, how=”left”)
uselog = pd.concat([uselog, tmp], ignore_index=True)
uselog.head()

ノック42:退会前月の退会顧客データを作成しよう

退会者は退会月の前月に退会届を出しますので、その前月のデータを解析する事で退会申請を防ぐ目的があります。

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_uselog = exit_uselog.dropna(subset=[“name”])
print(len(exit_uselog))
print(len(exit_uselog[“customer_id”].unique()))
exit_uselog.head()

ノック43:継続顧客のデータを作成しよう

次に継続顧客のデータを作成します。

Name列が欠損しているデータを除去します。

次にcustomer_id が重複しているデータを除去します。

conti_customer = customer.loc[customer[“is_deleted”]==0]
conti_uselog = pd.merge(uselog, conti_customer, on=[“customer_id”], how=”left”)
print(len(conti_uselog))
conti_uselog = conti_uselog.dropna(subset=[“name”])
print(len(conti_uselog))
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()

最後に退会顧客と継続顧客を結合しましょう。

継続と大会の両方が混ざったデータベースを作成しましょう。

predict_data = pd.concat([conti_uselog, exit_uselog],ignore_index=True)
print(len(predict_data))
predict_data.head()

ノック44:予測する月の在籍期間を作成しよう

在籍期間の作成をして、機械学習に備えましょう。

period列が作成されているのをを確認しましょう。

predict_data[“period”] = 0
predict_data[“now_date”] = pd.to_datetime(predict_data[“年月”], format=”%Y%m”)
predict_data[“start_date”] = pd.to_datetime(predict_data[“start_date”])
for i in range(len(predict_data)):
delta = relativedelta(predict_data[“now_date”][i], predict_data[“start_date”][i])
predict_data[“period”][i] = int(delta.years*12 + delta.months)
predict_data.head()

ノック45:欠損値を除去しよう

まず、欠損値の数を把握しましょう。

predict_data.isna().sum()

end_date, exit_data, count_1に欠損値がありますが、

count_1 に欠損値があるデータのみ除外します。

predict_data = predict_data.dropna(subset=[“count_1”])
predict_data.isna().sum()

お疲れ様でした。次回はいよいよ最終回です。ノック46−50をやっていきましょう。

今回の目的は、決定木という方法を用いて退会予測モデルを作る事です。

第5章-1 顧客の退会を予測する10本ノック後半

ノック46:文字列型の変数を処理できるように整形しよう

文字列のままでは、機械学習モデルに入れられないため、男=1,女=0 というようにダミー変数化します。

target_col = ["campaign_name", "class_name", "gender", 
"count_1", "routine_flg", "period", "is_deleted"]

predict_data = predict_data[target_col]<
predict_data.head()

get_dummies 関数で一括でダミー変数化します

predict_data = pd.get_dummies(predict_data)
predict_data.head()

del predict_data["campaign_name_通常"]
del predict_data["class_name_ナイト"]
del predict_data["gender_M"]
predict_data.head()

2者択一のものは男、女でそれぞれダミー変数化しなくても良いので、

それぞれのものは消しておきましょう

ノック47:決定木を用いて退会予測モデルを作成してみよう

いよいよ決定木モデルを作成しましょう。

from sklearn.tree import DecisionTreeClassifier を使用します。

exit と conti の数を合わせて比率を50:50にします。

その後、データを結合して isdeleted をy として、その予測モデルを作成します。

from sklearn.tree import DecisionTreeClassifier
import sklearn.model_selection
exit = predict_data.loc[predict_data["is_deleted"]==1]
conti = predict_data.loc[predict_data["is_deleted"]==0].sample(len(exit))
X = pd.concat([exit, conti], ignore_index=True)
y = X["is_deleted"]
del X["is_deleted"]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)
model = DecisionTreeClassifier(random_state=0)
model.fit(X_train, y_train)
y_test_pred = model.predict(X_test)
print(y_test_pred)

実際の正解との比較を行うためのデータフレームを作成しましょう

results_test = pd.DataFrame({"y_test":y_test ,"y_pred":y_test_pred })
results_test.head()

ノック48:予測モデルの評価を行ない、モデルのチューニングをしてみよう

y_pred と y_test を比較して、正解率を算出しましょう。

correct = len(results_test.loc[results_test["y_test"]==results_test["y_pred"]])
data_count = len(results_test)
score_test = correct / data_count
print(score_test)

正解率は0.87なので87% が正解率ということになります。

print(model.score(X_test, y_test))
print(model.score(X_train, y_train))

正解率の評価は、model.score 一発で出ます。

今回は test データに対し train データの方が正解率が高いため、

過学習がおきている事がわかります。

次は、モデルパラメータをいじってモデルを簡易化しましょう。

X = pd.concat([exit, conti], ignore_index=True)
y = X["is_deleted"]
del X["is_deleted"]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)
model = DecisionTreeClassifier(random_state=0, max_depth=5)
model.fit(X_train, y_train)
print(model.score(X_test, y_test))
print(model.score(X_train, y_train))

max_depth=5 にする事で、学習を5段階でやめているようなイメージです。

すると、正解率が 92% に上昇しています。

ノック49:モデルに寄与している変数を確認しよう

model.feature_importances で退会するかどうか判定するのに重要な因数をチェックできます。今回の場合は、count_1 つまり1ヶ月前の利用回数が最も重要であるという事ですね。

importance = pd.DataFrame({"feature_names":X.columns, "coefficient":model.feature_importances_})
importance

ノック50:顧客の退会を予測しよう

いよいよ仕上げです。ノック40でやったように、モデル人物を作ってみましょう。

count_1 = 3
routing_flg = 1
period = 10
campaign_name = "入会費無料"
class_name = "オールタイム"
gender = "M"

次にデータ加工を行いましょう。

if campaign_name == "入会費半額":
campaign_name_list = [1, 0]
elif campaign_name == "入会費無料":
campaign_name_list = [0, 1]
elif campaign_name == "通常":
campaign_name_list = [0, 0]
if class_name == "オールタイム":
class_name_list = [1, 0]
elif campaign_name == "デイタイム":
campaign_name_list = [0, 1]
elif campaign_name == "ナイト":
campaign_name_list = [0, 0]
if gender == "F":
gender_list = [1]<
elif gender == "M":
gender_list = [0]
input_data = [count_1, routing_flg, period]
input_data.extend(campaign_name_list)
input_data.extend(class_name_list)
input_data.extend(gender_list)

最後に予想してみましょう。predict で退会予測をします。

print(model.predict([input_data]))
print(model.predict_proba([input_data]))

このモデルだと、98%の割合で退会しているという結果が出ました。

お疲れ様でした。これで5章の機械学習モデル作成部分は終了です。生データを加工して、学習モデルを当てはめるという流れを学習できたのはかなり貴重だったと思います。
Python実戦データ分析100本ノックの学習メモは今回で最終回です。

一度では理解できなかったことも多かったので、また復習してみたいと思います。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

元薬局薬剤師。今は一般会社で働き、副業ライター(月5-6桁)しつつ自由に暮らす30代男性。英語学習(TOEIC920点)やライティング、マーケティング、メンタル術など、社会で必要とされつつ、個人で稼げるために必要なスキルを磨いて、その様子を発信しています。
詳しい自己紹介は(https://iroirotantan.com/ryoutaro/)で

コメント

コメントする

目次