一端の何かになれるか

一生懸命は眩しい

scikit-learnでクラスタリング分析を試す

とにかく試して見るシリーズ第3弾。
有効な分析か否かに関わらず、全試行過程を掲載します。
誰も見てないと思うんですけど、「ここちゃんと意識したほうがいいよ」、「そこわかってないね全然ダメだよ」、とかコメントついたらむちゃくちゃ嬉しいです。

なぜやるのか

職場でうんうん言いながらクラスタリングしてる人がいたので、やって見たくなった。
おじさんもQueryばっか書いてないで分析できるようにならないと。

クラスタリングとは

概要

  • 教師なしデータ分類の手法
  • データの集まりをデータ間の類似度に従って、いつかのグループに分ける
  • 階層的手法と非階層的手法に分類される

階層的手法

各データを1つのクラスタとし、クラスタ間の距離や類似度に基づいてクラスタを逐次的に併合していく手法

  • 最短距離法 (nearest neighbor method)
  • 最長距離法 (furthest neighbor method)
  • 群平均法 (group average method)
  • ウォード法 (Ward’s method)

非階層的手法

データの分割の良さを表す評価関数によって、最適解を探索する手法 階層的手法ではデータが多いと階層構造が複雑になってしまうため、非階層的手法のほうが実用的

  • k-means法(k平均法)

参考にしたサイト*1

参考にしたサイト*2

ビジネスでの活用例

市場細分化に基づくターゲット市場の選定

・顧客をセグメンテーションし、セグメントに適合するマーケティング施策を展開する
・顧客のどの属性をセグメンテーション変数として使うべきかは、分析の目的に合わせて選別する必要がある
コトラーによる顧客市場の主要なセグメンテーション変数には、以下4つがある

  1. 人口統計学的変数 (Demographic Variables)
  2. 地理的変数 (Geographic Variables)
  3. 心理的変数 (Psychographic Variables)
  4. 行動変数 (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))

f:id:select_from_where:20170924200316p:plain
Fresh_Channel

  • 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))

f:id:select_from_where:20170924200401p:plain
Fresh_Region

  • Regionごとでもそれほど分布は変わらない

Milkの分布

Channelごと

plt.hist([df_Channel1['Milk'],df_Channel2['Milk']],
         bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,40000))

f:id:select_from_where:20170924200504p:plain
Milk_Channel

  • 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))

f:id:select_from_where:20170924200543p:plain
Milk_Region

  • Regionごとではそれほど分布は変わらない

Groceryの分布

Channelごと

plt.hist([df_Channel1['Grocery'],df_Channel2['Grocery']],
        bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,40000))

f:id:select_from_where:20170924200622p:plain
Grocery_Channel

  • 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))

f:id:select_from_where:20170924200655p:plain
Grocery_Region

  • 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))

f:id:select_from_where:20170924200728p:plain
Frozen_Channel

  • 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))

f:id:select_from_where:20170924200811p:plain
Frozen_Region

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))

f:id:select_from_where:20170924200845p:plain
Paper_Channel

  • 洗剤、紙製品は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))

f:id:select_from_where:20170924200934p:plain
Paper_Region

  • Regionごとではそれほど分布は変わらない

Delicatessenの分布

Channelごと

plt.hist([df_Channel1['Delicassen'],df_Channel2['Delicassen']],
        bins=15, label=['Horeca','Retail'], color=['#FACC2E','#2E64FE'], rwidth=100, range=(0,20000))

f:id:select_from_where:20170924201008p:plain
Dericatessen_Channel

  • 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))

f:id:select_from_where:20170924201044p:plain
Dericatessen_Region

  • 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
  • 利益をあげている地域と販売チャネルは
    1. . Other RegionのHoreca
    2. . Other RegionのRetail
    3. . LisbonのHoreca
  • 品目単位で利益をあげているのは
    1. . Other Region,Horeca,Fresh
    2. . Other Region,Retail,Grocery
    3. . 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の方が、取引先あたりの卸売額が大きい
  • 取引先あたりの卸売額が多いのは
    1. Lisbon,Retail
    2. Other Region,Retail
    3. 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
  • 取引先の数が多い順に販促をかけるとすると下記をターゲットにするのが良いかもしれない
    1. Other Region,Horeca
    2. Other Region,Retail
    3. Lisbon,Horeca

感想

  • 分布を確認はして見たものの、特徴を掴むのにあまり有効な気がしなかった。やり方が悪い気がする
  • グループ化は、RegionとChannelを組み合わせたものだけでよかったかも
  • このぐらいのデータ量なら、クロス集計で一生懸命紐解いていったほうがわかりやすく有益な情報が得られそうな気がした
  • クラスタリング後の考察が甘いが、事業を深く知る手立てがあれば、いろんな切り口で分析するアイデアが湧いてきそうで楽しいなと思えた!

参考にしたサイト

*1:こちらのサイトを参考にしました

http://nlp.dse.ibaraki.ac.jp/~shinnou/zemi2008/Rclustering/r-tanaka-0415.pdf

*2:こちらのサイトを参考にしました

www.kamishima.net

*3:こちらのサイトを参考にしました

enterprisezine.jp