scikit-learnでクラスタリング分析を試す
とにかく試して見るシリーズ第3弾。
有効な分析か否かに関わらず、全試行過程を掲載します。
誰も見てないと思うんですけど、「ここちゃんと意識したほうがいいよ」、「そこわかってないね全然ダメだよ」、とかコメントついたらむちゃくちゃ嬉しいです。
- なぜやるのか
- クラスタリングとは
- 今回使用するデータ
- 試行過程と結果
- 感想
- 参考にしたサイト
なぜやるのか
職場でうんうん言いながらクラスタリングしてる人がいたので、やって見たくなった。
おじさんもQueryばっか書いてないで分析できるようにならないと。
クラスタリングとは
概要
- 教師なしデータ分類の手法
- データの集まりをデータ間の類似度に従って、いつかのグループに分ける
- 階層的手法と非階層的手法に分類される
階層的手法
- 最短距離法 (nearest neighbor method)
- 最長距離法 (furthest neighbor method)
- 群平均法 (group average method)
- ウォード法 (Ward’s method)
非階層的手法
データの分割の良さを表す評価関数によって、最適解を探索する手法 階層的手法ではデータが多いと階層構造が複雑になってしまうため、非階層的手法のほうが実用的
- k-means法(k平均法)
参考にしたサイト*1
参考にしたサイト*2
ビジネスでの活用例
市場細分化に基づくターゲット市場の選定
・顧客をセグメンテーションし、セグメントに適合するマーケティング施策を展開する
・顧客のどの属性をセグメンテーション変数として使うべきかは、分析の目的に合わせて選別する必要がある
・コトラーによる顧客市場の主要なセグメンテーション変数には、以下4つがある
- 人口統計学的変数 (Demographic Variables)
- 地理的変数 (Geographic Variables)
- 心理的変数 (Psychographic Variables)
- 行動変数 (Behavioral Variables)
製品ポジショニングによる差別化戦略の策定
・競合製品の属性でクラスタ分析を行うことで、競合製品に対する優位性に着目して製品のポジションを確立するなど差別化を図る
・競合が参入していない空白マーケットを発見し、新規市場を開拓する
テストマーケットにおけるマーケティング施策の評価
・マーケットのクラスタ分析を実施することで、テストマーケット※の有効性を保証する
※マーケティング施策を展開する前に、少数の顧客からなるテストマーケットでテストを行い、事前評価を行うのが一般的
クラスタ分析を行う際に注意すべきこと
・クラスタ分析は探索的な分析手法のため、複数回の分析結果を比較したり、異なる手法で分析結果を確認したりして、
地道な検証作業を繰り返すことで意思決定に有用な分析結果を導き出さなければならない
・欠損値の有無や標準化の必要性に関する確認が事前作業として不可欠 1. データから外れ値を除外すること 2. 分析データから分類理由を正しく説明できるかを常に考え、分析目的をサポートする属性を見極めること
参考にしたサイト*3
今回使用するデータ
Wholesale customers Data Set (卸売業者の顧客データ)
例によって、カリフォルニア大学アーバイン校(University of California, Irvine)のMachine Learning Repositoryから取得した
事例がいくつも転がっていて、のちにサンプルをいくつか見れて助かることになった
データセットの情報(Data Set Information)
ポルトガルの卸売業者の顧客のデータ(2011年(通年)、通貨単位の年間支出)。
大元のデータベースからサンプリングしたデータセット。
卸売の取引額なので、利益ではないことに注意したい。
以下、英訳のニュアンスが難しかったので、誤っている可能性あり。。
1)FRESH :生鮮品の年間支出(通貨単位) 2)MILK :乳製品の年間支出(通貨単位) 3)GROCERY:食料品の年間支出(通貨単位) 4)FROZEN :冷凍品の年間支出(通貨単位) 5)DETERGENTS_PAPER:洗剤、紙製品の年間支出(通貨単位) 6)DELICATESSEN:デリカテッセン(惣菜)の年間支出(通貨単位) 7)CHANNEL:販売チャネル - Horeca(ホテル/レストラン/カフェ)またはその他の販売チャネル 8)REGION :消費地域 - リスボン、ポルト、その他
統計量:
Product | Minimum | Maximum | Mean | Std Deviation |
---|---|---|---|---|
FRESH | 3 | 112151 | 12000.30 | 12647.329 |
MILK | 55 | 73498 | 5796.27 | 7380.377 |
GROCERY | 3 | 92780 | 7951.28 | 9503.163 |
FROZEN | 25 | 60869 | 3071.93 | 4854.673 |
DETERGENTS_PAPER | 3 | 40827 | 2881.49 | 4767.854 |
DELICATESSEN | 3 | 47943 | 1524.87 | 2820.106 |
REGION | Frequency |
---|---|
Lisbon | 77 |
Oporto | 47 |
Other Region | 316 |
Total | 440 |
CHANNEL | Frequency |
---|---|
Horeca | 298 |
Retail | 142 |
Total | 440 |
試行過程と結果
まずはデータを見てみる
データをダウンロードしてjupyter notebookにインポートする
rawデータを確認
import pandas as pd import_df = pd.read_csv('Wholesale customers data.csv') import_df.head()
Channel | Region | Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | |
---|---|---|---|---|---|---|---|---|
0 | 2 | 3 | 12669 | 9656 | 7561 | 214 | 2674 | 1338 |
1 | 2 | 3 | 7057 | 9810 | 9568 | 1762 | 3293 | 1776 |
2 | 2 | 3 | 6353 | 8808 | 7684 | 2405 | 3516 | 7844 |
3 | 1 | 3 | 13265 | 1196 | 4221 | 6404 | 507 | 1788 |
4 | 2 | 3 | 22615 | 5410 | 7198 | 3915 | 1777 | 5185 |
ChannelとRegionの値が何を示しているのか不明なので特定する
まずはChannel
import_df.groupby(['Channel']).count()
Region | Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | |
---|---|---|---|---|---|---|---|
Channel | |||||||
1 | 298 | 298 | 298 | 298 | 298 | 298 | 298 |
2 | 142 | 142 | 142 | 142 | 142 | 142 | 142 |
データセットの情報と突き合わせると
- 1:Hoterica
- 2:Retail
であることがわかる。
Regionはどうだろうか?
import_df.groupby(['Region']).count()
Channel | Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | |
---|---|---|---|---|---|---|---|
Region | |||||||
1 | 77 | 77 | 77 | 77 | 77 | 77 | 77 |
2 | 47 | 47 | 47 | 47 | 47 | 47 | 47 |
3 | 316 | 316 | 316 | 316 | 316 | 316 | 316 |
こちらもデータセットの情報と突き合わせて
- 1:Lisbon
- 2:Oporto
- 3:Other Region
であることがわかった。
データの特徴量
import_df.describe()
Channel | Region | Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | |
---|---|---|---|---|---|---|---|---|
count | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 |
mean | 1.322727 | 2.543182 | 12000.297727 | 5796.265909 | 7951.277273 | 3071.931818 | 2881.493182 | 1524.870455 |
std | 0.468052 | 0.774272 | 12647.328865 | 7380.377175 | 9503.162829 | 4854.673333 | 4767.854448 | 2820.105937 |
min | 1.000000 | 1.000000 | 3.000000 | 55.000000 | 3.000000 | 25.000000 | 3.000000 | 3.000000 |
25% | 1.000000 | 2.000000 | 3127.750000 | 1533.000000 | 2153.000000 | 742.250000 | 256.750000 | 408.250000 |
50% | 1.000000 | 3.000000 | 8504.000000 | 3627.000000 | 4755.500000 | 1526.000000 | 816.500000 | 965.500000 |
75% | 2.000000 | 3.000000 | 16933.750000 | 7190.250000 | 10655.750000 | 3554.250000 | 3922.000000 | 1820.250000 |
max | 2.000000 | 3.000000 | 112151.000000 | 73498.000000 | 92780.000000 | 60869.000000 | 40827.000000 | 47943.000000 |
- 欠損値はない
- 卸売額は、Fresh, Grocery, Milk, Frozen, Detergents_Paper, Delicassenの順で大きい
- 小口から大口まで、多様な顧客を持っている
データの分布を見てみる
とりかかる前に、Channel, Regionごとのデータフレームに分割しておく
Channelごとのデータフレーム
# Horeca df_Channel1 = import_df[import_df['Channel']==1].drop(['Channel'],axis=1) # Retail df_Channel2 = import_df[import_df['Channel']==2].drop(['Channel'],axis=1)
Regionごとのデータフレーム
# Lisbon df_Region1 = import_df[import_df['Region']==1].drop(['Region'],axis=1) # Oporto df_Region2 = import_df[import_df['Region']==2].drop(['Region'],axis=1) # Other Region df_Region3 = import_df[import_df['Region']==3].drop(['Region'],axis=1)
import matplotlib.pyplot as plt import seaborn as sns sns.set_style('whitegrid') %matplotlib inline
Freshの分布
まずはChannelごと
plt.hist([df_Channel1['Fresh'],df_Channel2['Fresh']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], range=(0,60000))
- Channelごとでそれほど分布は変わらない
次はRegionごと
plt.hist([df_Region1['Fresh'],df_Region2['Fresh'],df_Region3['Fresh']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,60000))
- Regionごとでもそれほど分布は変わらない
Milkの分布
Channelごと
plt.hist([df_Channel1['Milk'],df_Channel2['Milk']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,40000))
- MilkはRetailの方が、取引先あたりの卸売額が大きい傾向がある
Regionごと
plt.hist([df_Region1['Milk'],df_Region2['Milk'],df_Region3['Milk']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,40000))
- Regionごとではそれほど分布は変わらない
Groceryの分布
Channelごと
plt.hist([df_Channel1['Grocery'],df_Channel2['Grocery']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,40000))
- GroceryはRetailの方が、取引先あたりの卸売額が大きい傾向がある
Regionごと
plt.hist([df_Region1['Grocery'],df_Region2['Grocery'],df_Region3['Grocery']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,40000))
- RegionごとではOther Region方が、取引先あたりの卸売額が大きい傾向がある
Frozenの分布
Channelごと
plt.hist([df_Channel1['Frozen'],df_Channel2['Frozen']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,20000))
- FrozenはHorecaの方が、取引先あたりの卸売額が大きい傾向がある
Regionごと
plt.hist([df_Region1['Frozen'],df_Region2['Frozen'],df_Region3['Frozen']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,20000))
Detergents_Paperの分布
Channelごと
plt.hist([df_Channel1['Detergents_Paper'],df_Channel2['Detergents_Paper']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,25000))
- 洗剤、紙製品はRetailの方が、取引先あたりの卸売額が大きい傾向がある
Regionごと
plt.hist([df_Region1['Detergents_Paper'],df_Region2['Detergents_Paper'],df_Region3['Detergents_Paper']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,25000))
- Regionごとではそれほど分布は変わらない
Delicatessenの分布
Channelごと
plt.hist([df_Channel1['Delicassen'],df_Channel2['Delicassen']], bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,20000))
- Channelごとではそれほど分布は変わらない
Regionごと
plt.hist([df_Region1['Delicassen'],df_Region2['Delicassen'],df_Region3['Delicassen']], bins=15, label=['Lisbon','Oporto','Other Region'], color=['#2EFE2E','#FE2E2E','#F7FE2E'], rwidth=100, range=(0,20000))
- Regionごとではそれほど分布は変わらない
グループ化してクロス集計してみる
Regionでグループ化
データの情報にあったが、取引先の数を見てみる。合計行を追加しておく
import_df['Total'] = import_df[['Fresh','Milk','Grocery','Frozen','Detergents_Paper','Delicassen']].sum(axis=1) import_df[['Region','Total']].groupby(['Region']).count()
Total | |
---|---|
Region | |
1 | 77 |
2 | 47 |
3 | 316 |
- 取引先の数は、Other Region, Lisbon, Oportoの順で多い
取引額は、取引先の数に比例しそうだがどうだろうか
import_df.drop('Channel',axis=1).groupby(['Region']).sum()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | |
---|---|---|---|---|---|---|---|
Region | |||||||
1 | 854833 | 422454 | 570037 | 231026 | 204136 | 104327 | 2386813 |
2 | 464721 | 239144 | 433274 | 190132 | 173311 | 54506 | 1555088 |
3 | 3960577 | 1888759 | 2495251 | 930492 | 890410 | 512110 | 10677599 |
- 取引額も、Other Region, Lisbon, Oportoの順で多い
- 卸業者なので、ロジスティクスを考えると取引先との距離と関係が深いかもしれない
取引先あたりの卸売額を見てみる
import_df.drop('Channel',axis=1).groupby(['Region']).mean()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | |
---|---|---|---|---|---|---|---|
Region | |||||||
1 | 11101.727273 | 5486.415584 | 7403.077922 | 3000.337662 | 2651.116883 | 1354.896104 | 30997.571429 |
2 | 9887.680851 | 5088.170213 | 9218.595745 | 4045.361702 | 3687.468085 | 1159.702128 | 33086.978723 |
3 | 12533.471519 | 5977.085443 | 7896.363924 | 2944.594937 | 2817.753165 | 1620.601266 | 33789.870253 |
- Grocery, Frozen, Detergents_Paperは取引先あたりの卸売額順が、Regionごとの卸売額順と異なる
- OportoのGrocery, Frozen, Detergents_Paperは、Regionごとの卸売額の割に取引先あたりの卸売額が大きい
Channelでグループ化
取引先の数を見てみる
import_df[['Channel','Total']].groupby(['Channel']).count()
Total | |
---|---|
Channel | |
1 | 298 |
2 | 142 |
- 取引先の数は、Horecaが倍近く多い
取引額は、取引先の数に比例しそうだがどうだろうか。
import_df.drop('Region',axis=1).groupby(['Channel']).sum()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | |
---|---|---|---|---|---|---|---|
Channel | |||||||
1 | 4015717 | 1028614 | 1180717 | 1116979 | 235587 | 421955 | 7999569 |
2 | 1264414 | 1521743 | 2317845 | 234671 | 1032270 | 248988 | 6619931 |
- Horecaは、Retailに対して倍近い取引があるにもかかわらず、卸売額はそれほど大きな差がない
- ポルトガルの生活感がわからないので、データから解釈するには想像の域を出ないことが多い
- 両方を比べると、Horecaの卸売額がRetailを上回るのは、Fresh, Frozen, Dericatessen
日本の飲食店でバイトした経験から、Milkは専門の別卸売業者から、Groceryは加工食品のため少ないものと想像 その他の販売チャネルでFreshの額がHorecaと比べて小さいのは、Freshを専売している商店の集まった市場が多いのかも
Horeca
- Fresh, Grocery, Frozenの順で卸売額が多い
- Detergents_Paperは、特にHotelで専門の卸売業者から仕入れているか、もしくはRetailほど数は出ないということか
Delicatessenは、調理したものを客に出すため少ないのだろう
その他の販売チャネル(スーパーマーケット、商店等?)
- Grocery, Milk, Freshの順で卸売額が多い
取引先あたりの卸売額を見てみる
import_df.drop('Region',axis=1).groupby(['Channel']).mean()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | |
---|---|---|---|---|---|---|---|
Channel | |||||||
1 | 13475.560403 | 3451.724832 | 3962.137584 | 3748.251678 | 790.560403 | 1415.956376 | 26844.191275 |
2 | 8904.323944 | 10716.500000 | 16322.852113 | 1652.612676 | 7269.507042 | 1753.436620 | 46619.232394 |
- 取引先ごとの取引額でみると、Delicatessenは取引先あたりの卸売額順が、販売チャネルごとの卸売額順と異なる
RegionとChannelでグループ化してクロス集計
取引先の数を見てみる
import_df[['Region','Channel','Total']].groupby(['Region','Channel']).count()
Total | ||
---|---|---|
Region | Channel | |
1 | 1 | 59 |
2 | 18 | |
2 | 1 | 28 |
2 | 19 | |
3 | 1 | 211 |
2 | 105 |
- 全取引先に対する地域ごとのHorecaの比率はリスボンが高い
取引額はどうだろうか
import_df.groupby(['Region','Channel']).sum()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | ||
---|---|---|---|---|---|---|---|---|
Region | Channel | |||||||
1 | 1 | 761233 | 228342 | 237542 | 184512 | 56081 | 70632 | 1538342 |
2 | 93600 | 194112 | 332495 | 46514 | 148055 | 33695 | 848471 | |
2 | 1 | 326215 | 64519 | 123074 | 160861 | 13516 | 30965 | 719150 |
2 | 138506 | 174625 | 310200 | 29271 | 159795 | 23541 | 835938 | |
3 | 1 | 2928269 | 735753 | 820101 | 771606 | 165990 | 320358 | 5742077 |
2 | 1032308 | 1153006 | 1675150 | 158886 | 724420 | 191752 | 4935522 |
- 利益をあげている地域と販売チャネルは
- . Other RegionのHoreca
- . Other RegionのRetail
- . LisbonのHoreca
- 品目単位で利益をあげているのは
- . Other Region,Horeca,Fresh
- . Other Region,Retail,Grocery
- . Other Region,Retail,Milk
- ポルトのHotericaは全体の特徴と異なり、生鮮品、冷凍品、食料品の順で卸売額が多い
取引先あたりの卸売額はどうだろうか
import_df.groupby(['Region','Channel']).mean()
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicassen | Total | ||
---|---|---|---|---|---|---|---|---|
Region | Channel | |||||||
1 | 1 | 12902.254237 | 3870.203390 | 4026.135593 | 3127.322034 | 950.525424 | 1197.152542 | 26073.593220 |
2 | 5200.000000 | 10784.000000 | 18471.944444 | 2584.111111 | 8225.277778 | 1871.944444 | 47137.277778 | |
2 | 1 | 11650.535714 | 2304.250000 | 4395.500000 | 5745.035714 | 482.714286 | 1105.892857 | 25683.928571 |
2 | 7289.789474 | 9190.789474 | 16326.315789 | 1540.578947 | 8410.263158 | 1239.000000 | 43996.736842 | |
3 | 1 | 13878.052133 | 3486.981043 | 3886.734597 | 3656.900474 | 786.682464 | 1518.284360 | 27213.635071 |
2 | 9831.504762 | 10981.009524 | 15953.809524 | 1513.200000 | 6899.238095 | 1826.209524 | 47004.971429 |
- Retailの方が、取引先あたりの卸売額が大きい
- 取引先あたりの卸売額が多いのは
- Lisbon,Retail
- Other Region,Retail
- Oporto,Retail
分析の目的を定める
本音はただクラスタリングを試したいだけなのだが、分析が必要になった背景を仮定したい。
新たに現在取り扱っていない多品目を生産している生産者から生鮮食品を仕入れられることになった。
クラスタリングした結果から、新たな生鮮品の発注を取れそうな見込み顧客を割り出したいと仮定する。
クラスタリングを試す
クラスタリングに利用する項目を選定
MilkとDetergents_PaperはFreshと相関関係が薄いということにして除外しておく - Region - Channel - Fresh - Grocery - Frosen - Delicatessen
cluster_df = import_df.drop(['Milk','Detergents_Paper','Total'],axis=1) cluster_df.head()
Channel | Region | Fresh | Grocery | Frozen | Delicassen | |
---|---|---|---|---|---|---|
0 | 2 | 3 | 12669 | 7561 | 214 | 1338 |
1 | 2 | 3 | 7057 | 9568 | 1762 | 1776 |
2 | 2 | 3 | 6353 | 7684 | 2405 | 7844 |
3 | 1 | 3 | 13265 | 4221 | 6404 | 1788 |
4 | 2 | 3 | 22615 | 7198 | 3915 | 5185 |
行列 (Array) に変換して転置する
import numpy as np cluster_array = np.array([cluster_df['Channel'].tolist(), cluster_df['Region'].tolist(), cluster_df['Fresh'].tolist(), cluster_df['Grocery'].tolist(), cluster_df['Frozen'].tolist(), cluster_df['Delicassen'].tolist() ], np.int32) cluster_array = cluster_array.T cluster_array
array([[ 2, 3, 12669, 7561, 214, 1338],
[ 2, 3, 7057, 9568, 1762, 1776],
[ 2, 3, 6353, 7684, 2405, 7844],
...,
[ 2, 3, 14531, 30243, 437, 1867],
[ 1, 3, 10290, 2232, 1038, 2125],
[ 1, 3, 2787, 2510, 65, 52]], dtype=int32)
クラスタ分析を実行(本来はこれを何度も試行するが、今回は一回で)
from sklearn.cluster import KMeans cluster = KMeans(n_clusters=6).fit_predict(cluster_array) cluster
array([1, 3, 3, 1, 1, 3, 1, 3, 3, 2, 3, 1, 0, 1, 1, 3, 3, 3, 1, 3, 1, 3, 0,
0, 1, 1, 3, 1, 2, 0, 1, 3, 1, 0, 3, 3, 0, 1, 2, 0, 1, 1, 2, 2, 3, 2,
2, 5, 3, 2, 3, 3, 0, 3, 1, 3, 2, 3, 1, 3, 3, 5, 3, 2, 3, 2, 3, 1, 3,
3, 1, 2, 3, 1, 3, 1, 3, 2, 3, 3, 3, 2, 3, 1, 1, 5, 2, 0, 3, 1, 1, 1,
2, 1, 3, 3, 3, 3, 3, 3, 1, 2, 3, 0, 1, 1, 3, 2, 3, 2, 1, 2, 1, 1, 1,
3, 3, 3, 1, 3, 1, 3, 1, 3, 0, 4, 1, 1, 3, 0, 3, 3, 1, 3, 3, 3, 3, 3,
1, 3, 1, 0, 0, 3, 1, 2, 3, 3, 3, 0, 1, 3, 1, 3, 3, 2, 2, 1, 3, 2, 3,
1, 1, 2, 3, 2, 3, 3, 3, 3, 3, 2, 3, 2, 3, 3, 0, 1, 3, 3, 1, 4, 3, 0,
3, 3, 3, 3, 3, 3, 1, 1, 3, 2, 3, 1, 0, 3, 1, 3, 2, 2, 1, 3, 3, 2, 3,
3, 3, 2, 1, 2, 3, 3, 3, 2, 2, 1, 2, 3, 1, 3, 3, 3, 3, 1, 1, 3, 3, 3,
1, 3, 1, 3, 1, 3, 3, 1, 3, 0, 1, 1, 1, 3, 3, 2, 3, 1, 1, 3, 3, 2, 3,
0, 3, 0, 3, 3, 0, 0, 3, 3, 1, 3, 2, 3, 2, 1, 2, 1, 3, 3, 3, 0, 3, 3,
0, 3, 1, 1, 3, 1, 0, 1, 4, 0, 3, 1, 1, 0, 3, 3, 3, 3, 1, 3, 1, 3, 3,
3, 1, 2, 3, 2, 2, 3, 2, 1, 3, 3, 3, 0, 2, 3, 3, 3, 3, 3, 1, 2, 3, 3,
1, 1, 1, 0, 3, 3, 1, 3, 3, 2, 1, 5, 1, 1, 1, 3, 3, 3, 3, 3, 3, 2, 3,
3, 2, 1, 3, 2, 3, 2, 3, 2, 1, 3, 1, 2, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3,
1, 3, 0, 1, 3, 1, 3, 3, 3, 0, 3, 3, 1, 1, 0, 3, 2, 1, 3, 1, 3, 3, 3,
3, 3, 1, 1, 3, 3, 1, 1, 3, 3, 0, 1, 1, 1, 3, 1, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 2, 3, 3, 1, 1, 1, 1, 1, 1, 0, 3, 3, 2, 3, 1, 3, 1, 0, 0,
2, 3, 3], dtype=int32)
参考にしたサイト((こちらのサイトを参考にしました
[http://pythondatascience.plavox.info/scikit-learn/%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E5%88%86%E6%9E%90-k-means:title] ))
元のデータフレームにクラスタリングした番号を付与する
cluster_df['Cluster'] = cluster
cluster_df.head()
Channel | Region | Fresh | Grocery | Frozen | Delicassen | Cluster | |
---|---|---|---|---|---|---|---|
0 | 2 | 3 | 12669 | 7561 | 214 | 1338 | 1 |
1 | 2 | 3 | 7057 | 9568 | 1762 | 1776 | 3 |
2 | 2 | 3 | 6353 | 7684 | 2405 | 7844 | 3 |
3 | 1 | 3 | 13265 | 4221 | 6404 | 1788 | 1 |
4 | 2 | 3 | 22615 | 7198 | 3915 | 5185 | 1 |
各クラスタに分類されたサンプルの数
cluster_df[['Cluster','Fresh']].groupby(['Cluster']).count()
Fresh | |
---|---|
Cluster | |
0 | 37 |
1 | 120 |
2 | 60 |
3 | 216 |
4 | 3 |
5 | 4 |
各クラスタの卸売額合計
cluster_df.drop(['Region','Channel'],axis=1).groupby('Cluster').sum()
Fresh | Grocery | Frozen | Delicassen | |
---|---|---|---|---|
Cluster | ||||
0 | 1396192 | 239382 | 282851 | 142625 |
1 | 2129961 | 553601 | 426496 | 168718 |
2 | 330513 | 1329555 | 92192 | 118564 |
3 | 1061036 | 1062918 | 495943 | 216521 |
4 | 257339 | 37859 | 41975 | 11874 |
5 | 105090 | 275247 | 12193 | 12641 |
各クラスタの取引先あたりの卸売額に卸売額の合計、顧客数を付与
cluster_df1 = cluster_df.drop(['Channel','Region'],axis=1).groupby('Cluster').mean() count1 = np.array(cluster_df[['Cluster','Fresh']].groupby(['Cluster']).count()) Fresh_sum = np.array(cluster_df[['Cluster','Fresh']].groupby('Cluster').sum()) Grocery_sum = np.array(cluster_df[['Cluster','Grocery']].groupby('Cluster').sum()) Frozen_sum = np.array(cluster_df[['Cluster','Frozen']].groupby('Cluster').sum()) Delicassen_sum = np.array(cluster_df[['Cluster','Delicassen']].groupby('Cluster').sum()) cluster_df1['Count'] = count1 cluster_df1['Fresh_sum'] = Fresh_sum cluster_df1['Grocery_sum'] = Grocery_sum cluster_df1['Frozen_sum'] = Frozen_sum cluster_df1['Delicassen_sum'] = Delicassen_sum cluster_df1
Fresh | Grocery | Frozen | Delicassen | Count | Fresh_sum | Grocery_sum | Frozen_sum | Delicassen_sum | |
---|---|---|---|---|---|---|---|---|---|
Cluster | |||||||||
0 | 37734.918919 | 6469.783784 | 7644.621622 | 3854.729730 | 37 | 1396192 | 239382 | 282851 | 142625 |
1 | 17749.675000 | 4613.341667 | 3554.133333 | 1405.983333 | 120 | 2129961 | 553601 | 426496 | 168718 |
2 | 5508.550000 | 22159.250000 | 1536.533333 | 1976.066667 | 60 | 330513 | 1329555 | 92192 | 118564 |
3 | 4912.203704 | 4920.916667 | 2296.032407 | 1002.412037 | 216 | 1061036 | 1062918 | 495943 | 216521 |
4 | 85779.666667 | 12619.666667 | 13991.666667 | 3958.000000 | 3 | 257339 | 37859 | 41975 | 11874 |
5 | 26272.500000 | 68811.750000 | 3048.250000 | 3160.250000 | 4 | 105090 | 275247 | 12193 | 12641 |
- Freshの卸売額が最も多く、Groceryの卸売額が少ない顧客層であるCluster'0'は、新たな生鮮品に反応する可能性が高く、一番の狙い目かもしれない
そこで Cluster'0'に絞って、RegionとChannelから販促をかける対象をより絞ってみる
cluster_df2 = cluster_df[cluster_df['Cluster']==0] cluster_df2.head()
Channel | Region | Fresh | Grocery | Frozen | Delicassen | Cluster | |
---|---|---|---|---|---|---|---|
12 | 2 | 3 | 31714 | 11757 | 287 | 2931 | 0 |
22 | 1 | 3 | 31276 | 4469 | 9408 | 4334 | 0 |
23 | 2 | 3 | 26373 | 22019 | 5154 | 16523 | 0 |
29 | 1 | 3 | 43088 | 2609 | 1200 | 823 | 0 |
33 | 1 | 3 | 29729 | 7326 | 6130 | 1083 | 0 |
cluster_df2[['Region','Channel','Fresh']].groupby(['Region','Channel']).count()
Fresh | ||
---|---|---|
Region | Channel | |
1 | 1 | 6 |
2 | 1 | 2 |
3 | 1 | 25 |
2 | 4 |
cluster_df3 = cluster_df2.drop('Cluster',axis=1).groupby(['Region','Channel']).mean() cluster_df3 count2 = np.array(cluster_df2[['Region','Channel','Fresh']].groupby(['Region','Channel']).count()) Fresh_sum2 = np.array(cluster_df2[['Region','Channel','Fresh']].groupby(['Region','Channel']).sum()) Grocery_sum2 = np.array(cluster_df2[['Region','Channel','Grocery']].groupby(['Region','Channel']).sum()) Frozen_sum2 = np.array(cluster_df2[['Region','Channel','Frozen']].groupby(['Region','Channel']).sum()) Delicassen_sum2 = np.array(cluster_df2[['Region','Channel','Delicassen']].groupby(['Region','Channel']).sum()) cluster_df3['Count'] = count2 cluster_df3['Fresh_sum'] = Fresh_sum2 cluster_df3['Grocery_sum'] = Grocery_sum2 cluster_df3['Frozen_sum'] = Frozen_sum2 cluster_df3['Delicassen_sum'] = Delicassen_sum2 cluster_df3
Fresh | Grocery | Frozen | Delicassen | Count | Fresh_sum | Grocery_sum | Frozen_sum | Delicassen_sum | |
---|---|---|---|---|---|---|---|---|---|
Cluster | |||||||||
0 | 37734.918919 | 6469.783784 | 7644.621622 | 3854.729730 | 37 | 1396192 | 239382 | 282851 | 142625 |
1 | 17749.675000 | 4613.341667 | 3554.133333 | 1405.983333 | 120 | 2129961 | 553601 | 426496 | 168718 |
2 | 5508.550000 | 22159.250000 | 1536.533333 | 1976.066667 | 60 | 330513 | 1329555 | 92192 | 118564 |
3 | 4912.203704 | 4920.916667 | 2296.032407 | 1002.412037 | 216 | 1061036 | 1062918 | 495943 | 216521 |
4 | 85779.666667 | 12619.666667 | 13991.666667 | 3958.000000 | 3 | 257339 | 37859 | 41975 | 11874 |
5 | 26272.500000 | 68811.750000 | 3048.250000 | 3160.250000 | 4 | 105090 | 275247 | 12193 | 12641 |
- 取引先の数が多い順に販促をかけるとすると下記をターゲットにするのが良いかもしれない
- Other Region,Horeca
- Other Region,Retail
- Lisbon,Horeca
感想
- 分布を確認はして見たものの、特徴を掴むのにあまり有効な気がしなかった。やり方が悪い気がする
- グループ化は、RegionとChannelを組み合わせたものだけでよかったかも
- このぐらいのデータ量なら、クロス集計で一生懸命紐解いていったほうがわかりやすく有益な情報が得られそうな気がした
- クラスタリング後の考察が甘いが、事業を深く知る手立てがあれば、いろんな切り口で分析するアイデアが湧いてきそうで楽しいなと思えた!
参考にしたサイト
*1:こちらのサイトを参考にしました
http://nlp.dse.ibaraki.ac.jp/~shinnou/zemi2008/Rclustering/r-tanaka-0415.pdf
*2:こちらのサイトを参考にしました
*3:こちらのサイトを参考にしました