SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Python in Excel実務入門:Excelデータ分析の新パラダイム

scikit-learnで始めるExcel版機械学習。予測分析と評価

Python in Excel実務入門:Excelデータ分析の新パラダイム 第4回

WINGSプロジェクト 西 潤史郎[著] / 山田 祥寛[監修]

分類とクラスタリングで顧客データの理解を深める

 ここでは、売上の見積もりとは別の分析として、分類とクラスタリングを確認します。

 分類は教師あり学習、クラスタリングは教師なし学習の代表例です。教師ありとは、正解ラベル(今回は解約列)を使って学ぶ方法、教師なしとは、正解ラベルを使わずにデータの構造を見つける方法です。同じ30行のデータを、ラベルあり・なしで使い分けます。

 配布ファイルの定期購入顧客データ30行を、月次表と同じシートの右側(H1:N31)に置いてください。

定期購入顧客データを見出しを含めてH1:N31に置いたシート
定期購入顧客データを見出しを含めてH1:N31に置いたシート

 平均購入額の単位は千円です。解約列は0が継続、1が解約で、30件中10件が解約です。分類では解約列を正解として使い、クラスタリングでは外します。

分類

 ここでは、定期購入顧客データをcustという変数に読み込み、ロジスティック回帰のモデルをclf(classifierの略)という変数に格納して使います。

 ロジスティック回帰は、Yes/Noといった二値を、0~1の確率として見積もる手法です。3列の説明変数から解約リスクを見積もり、predict_probaで確率を出して、優先リストにします。

python
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

cust = xl("H1:N31", headers=True)
feats = ["継続月数", "問い合わせ回数",
         "最終購入からの経過日数"]
Xc = cust[feats]
yc = cust["解約"]
Xc_train, Xc_test, yc_train, yc_test = train_test_split(
    Xc, yc, test_size=0.2, random_state=0,
    stratify=yc)  # (1)
clf = LogisticRegression()
clf.fit(Xc_train, yc_train)
int((clf.predict(Xc_test) == yc_test).sum()), len(yc_test)
テストデータの正解件数と総件数を確認した結果
テストデータの正解件数と総件数を確認した結果

 学習24件・テスト6件で、テストの6件中5件が一致しました(正解率は約0.833。テスト6件での値です)。stratify=yc(1)は、解約の比率を学習とテストでそろえる指定です。

 係数の符号を見ると、継続月数が負、問い合わせ回数と経過日数が正になっています。これは、継続月数が長いほど解約確率は低く、問い合わせ回数と経過日数が大きいほど解約確率は高く見積もられることを意味します。ただし符号が示すのはあくまで関連の向きであり、原因を表すものではありません。

 0か1かだけでなく、確率で順位を付けると、限られた工数をどこから使うかの材料になります。全30件に解約確率を付け、降順に並べてシートへ返します。なお、確率を付けた30件のうち24件は学習に使った行です。

python
ranked = cust.copy()
ranked["解約確率"] = clf.predict_proba(Xc)[:, 1].round(3)
ranked = ranked.sort_values(
    "解約確率", ascending=False)
ranked

 解約確率の高い順に30件が並びます。

解約確率の降順で並べた優先アプローチリスト
解約確率の降順で並べた優先アプローチリスト

 上位のC024・C029・C022は、いずれも実際に解約した顧客です。確率の分布を見ると、0.8以上が6件、0.2~0.8が7件、0.2以下が17件で、0か1にはっきり振り切れているわけではありません。閾値を0.5に置くと解約と判定されるのは9件になりますが、実際には継続していた空振りのC014(0.754)や、取りこぼしとなったC023(0.343)・C027(0.095)も出るため、線引きは現場の判断に委ねられます。

 シートへ戻した列は、フィルタや条件付き書式と組み合わせられます。

クラスタリング

 K-Meansとは、距離の近いデータを、指定した個数のグループにまとめる手法です。前項と同じ顧客データから解約列を外し、正解ラベルなしで購買傾向のセグメントを出します。クラスタ数kは「施策を何通り出し分けるか」という業務判断で決めます。

 距離でグループにまとめるK-Meansは、列の単位の大きさに引きずられるという性質があります。今回のデータでは、継続月数(4~34)と最終購入からの経過日数(12~141)とで値の幅が大きく違うため、StandardScalerで標準化し、尺度をそろえてから渡します。

 標準化とは、各列を平均0・標準偏差1に置き換え、列ごとの単位の差をなくす処理です。分類(標準化なし・3列)と手順が非対称になるのは、この違いのためです。

python
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

clu_feats = ["継続月数", "累計購入回数",
             "平均購入額", "問い合わせ回数",
             "最終購入からの経過日数"]
Xs = StandardScaler().fit_transform(
    cust[clu_feats])
km = KMeans(n_clusters=3, random_state=0)
km.fit(Xs)
seg = cust.copy()
seg["クラスタ"] = km.labels_
seg

 右端にクラスタ列が付いた30件が表示されます。

クラスタ列を追加した顧客表をシートへ展開した結果
クラスタ列を追加した顧客表をシートへ展開した結果

 今回の設定では、30人が13人・8人・9人の3グループに分かれました。割り振られた「0・1・2」という番号自体に順位や意味はありません。

 K-Meansは最初の中心をランダムに選ぶため、random_stateを変えると番号の割り当てが入れ替わるだけでなく、分け方そのものが変わることもあります(今回のデータでも、値によっては18人・5人・7人に分かれます)。ここで乱数を固定しているのはこのためです。各グループが「どんな顧客層なのか」は、次のコードで項目の平均値を集計して読み解きます。

python
prof = (seg.groupby("クラスタ")[clu_feats]
        .mean().round(1))
prof["人数"] = seg.groupby("クラスタ").size()
prof

 クラスタごとの平均値と人数が3行で並びます。

クラスタごとの平均値と人数のプロファイル
クラスタごとの平均値と人数のプロファイル

 クラスタ1(8人)は継続月数が長く購入回数が多いグループ、クラスタ2(9人)は経過日数が長く問い合わせ回数が多いグループ、クラスタ0(13人)はその中間です。平均値から読めるのはここまでで、「優良」「離脱予備軍」と名前を付けるかどうかは業務側の判断です。

 解約列は学習に使っていません。あとから照合すると、クラスタ2の解約率は0.89、クラスタ1は0.00、クラスタ0は0.154でした。正解ラベルを使わずに分けたグループが、結果として解約の有無と対応していたわけです。ただし確かめられたのは今回の30件での対応関係までで、クラスタが解約を説明しているとはいえません。結果をシートへ戻せば、クラスタごとに案内文やクーポンを出し分ける材料になります。

 このように、同じ顧客表を、ラベルありの確率リストとラベルなしのセグメントに使い分けられます。予測もグループ分けも、最後は列としてシートへ戻すところが、Excelで使う意味です。

まとめ

 本稿では、scikit-learnの共通手順(fitで学習し、predictで見積もる)を軸に、回帰・分類・クラスタリングをExcel上で実行し、特に学習/テスト分割と結果をシートへ戻す流れを確認しました。

  • 学習/テスト分割:見たことのないデータで通用するかを確かめる
  • 書き戻し:予測列をDataFrameに足し、Pythonセルの出力としてシートへ展開する

 機械学習のための専用基盤がなくても、手元の表のまま予測の材料を足せます。実務に向けた次の一歩は、自社の表を同じ型に載せ、分割と指標で確かめてからシートへ戻すことです。説明変数を足す、閾値を業務で決める、セグメントごとに施策を出し分ける、といった運用を試してみてください。

 予測をシートへ戻せるようになったあとでも、モデルは正解を出す機械ではありません。外れた行を見て表を直し、現場の判断に材料を足す。そうやって育てていく使い方が、Excelで機械学習を扱うメリットです。

連載を振り返って

 本連載では、次の順で確認してきました。

  • 第1回:Python in Excelの位置づけとVBAとの使い分け、=PYとxl()の基本操作
  • 第2回:pandasによるクレンジング・欠損値処理と、groupby/mergeでの集計・結合
  • 第3回:matplotlib/seabornによる散布図・ヒートマップ・箱ひげ図の使い分け
  • 第4回:scikit-learnによる回帰・分類・クラスタリングと、予測値のシートへの書き戻し

 「データを整える」「見る」「予測する」という分析の流れに沿って進めてきました。どの工程もExcelブックの中で完結し、処理の手順がコードとしてセルに残ります。誰が実行しても同じ結果になる手順として残せる点が、Python in Excelを使う大きな利点です。

 一方で、Python in Excelはクラウド上で実行されるため、ファイルの操作や他のアプリケーションとの連携は、これまでどおりVBAの役割です。「どちらが上か」ではなく「どの仕事にどちらが向くか」という観点は、機械学習まで進んだ今も変わりません。手元の表をxl()で読み、describe()で眺めるところからはじめてみてください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Python in Excel実務入門:Excelデータ分析の新パラダイム連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 西 潤史郎(ニシ ジュンシロウ)

< WINGSプロジェクト について>有限会社 WINGSプロジェクト が運営する、テクニカル執筆コミュニティ(代表 山田...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー