k-meansでクラスタリング

クラスタリングとは分類そのものを作成する手法

クラスタリングは複数の項目から対象のクラスターを分類する手法。小売りの現場での主な用途としては

  1. 顧客の購買履歴を使用した分類
  2. 店舗の立地条件などを使用した分類
  3. 商品の売れ行きなどを使用した分類

などがある。指標が売上金額など単純な場合は経験的に分類してもいいが、指標が多岐にわたる場合はなかなか適切な判断が難しい。

分類とクラスタリングが異なるのは、クラスタリングが「教師なし学習」である点。こちらが用意した分類に落とし込むのではなく、与えられたデータをもとに分類自体を行う点が大きく異なる。

k-means法はクラスタリングの中でも速度に優れる。ランダムに設定したクラスタの重心を設定し、各点と重心との距離が最短になるよう繰り返していく。

グラフィカルに説明してくれるページ

まずはざっくり

検索したら手軽に試せそうなコードがあったので活用させてもらう。使用するライブラリはscikit-learnになる。

import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
 
# データセットを読み込み
cust_df = pd.read_csv("http://pythondatascience.plavox.info/wp-content/uploads/2016/05/Wholesale_customers_data.csv")
cust_df

------------

 	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
...	...	...	...	...	...	...	...	...
435	1	3	29703	12051	16027	13135	182	2204
436	1	3	39228	1431	764	4510	93	2346
437	2	3	14531	15488	30243	437	14841	1867
438	1	3	10290	1981	2232	1038	168	2125
439	1	3	2787	1698	2510	65	477	52
440 rows × 8 columns

8列。欠損・重複なし。

ChannelとRegionは分類項目なので除去する。

del(cust_df['Channel'])
del(cust_df['Region'])
cust_df

--------------

	Fresh	Milk	Grocery	Frozen	Detergents_Paper	Delicassen
0	12669	9656	7561	214	2674	1338
1	7057	9810	9568	1762	3293	1776
2	6353	8808	7684	2405	3516	7844
3	13265	1196	4221	6404	507	1788
4	22615	5410	7198	3915	1777	5185
...	...	...	...	...	...	...
435	29703	12051	16027	13135	182	2204
436	39228	1431	764	4510	93	2346
437	14531	15488	30243	437	14841	1867
438	10290	1981	2232	1038	168	2125
439	2787	1698	2510	65	477	52
440 rows × 6 columns

del関数めっちゃ便利や…。dropにaxis=1してた。

つづいて、各列をデータフレームから行列に変換して分析できる形にする。

cust_array = np.array([cust_df['Fresh'].tolist(),
                       cust_df['Milk'].tolist(),
                       cust_df['Grocery'].tolist(),
                       cust_df['Frozen'].tolist(),
                       cust_df['Milk'].tolist(),
                       cust_df['Detergents_Paper'].tolist(),
                       cust_df['Delicassen'].tolist()
                       ], np.int32)
cust_array = cust_array.T # 転置
cust_array

-------------

array([[12669,  9656,  7561, ...,  9656,  2674,  1338],
       [ 7057,  9810,  9568, ...,  9810,  3293,  1776],
       [ 6353,  8808,  7684, ...,  8808,  3516,  7844],
       ...,
       [14531, 15488, 30243, ..., 15488, 14841,  1867],
       [10290,  1981,  2232, ...,  1981,   168,  2125],
       [ 2787,  1698,  2510, ...,  1698,   477,    52]], dtype=int32)

いざクラスタリング

pred = KMeans(n_clusters=4).fit_predict(cust_array)
pred

-----------

array([0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 1, 1, 0, 3, 0, 0, 0, 0, 0,
       1, 2, 1, 0, 0, 0, 3, 1, 0, 0, 1, 1, 0, 0, 1, 3, 3, 1, 1, 0, 3, 3,
       0, 3, 3, 2, 0, 3, 0, 0, 1, 3, 1, 0, 3, 3, 0, 0, 0, 2, 0, 3, 0, 3,
       0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 3, 0, 0, 0, 3, 0, 1, 0, 2, 2, 1,
       0, 1, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 3, 3, 0, 1, 0, 0, 0, 3, 0, 3,
       0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0,
       0, 3, 3, 0, 0, 3, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, 3, 0, 3, 0, 3,
       1, 0, 0, 0, 0, 2, 3, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 1, 0,
       0, 0, 3, 3, 1, 0, 0, 3, 0, 0, 0, 3, 0, 3, 0, 0, 0, 3, 3, 0, 3, 0,
       0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
       0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 0, 1, 3, 1, 0, 0, 1, 1, 0, 0, 1, 0,
       3, 3, 3, 1, 3, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1,
       0, 0, 0, 1, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 3, 3, 0,
       0, 3, 0, 1, 3, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
       0, 3, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 1, 0, 3, 0, 3,
       0, 3, 0, 0, 1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
       0, 0, 3, 1, 0, 0, 1, 0, 1, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
       3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
       3, 0, 0, 0, 1, 0, 0, 0, 3, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 3, 0, 0],
      dtype=int32)

クラスタリングした結果をもとのDFに記入

cust_df['cluster_id']=pred
cust_df

--------------

	Fresh	Milk	Grocery	Frozen	Detergents_Paper	Delicassen	cluster_id
0	12669	9656	7561	214	2674	1338	0
1	7057	9810	9568	1762	3293	1776	0
2	6353	8808	7684	2405	3516	7844	0
3	13265	1196	4221	6404	507	1788	0
4	22615	5410	7198	3915	1777	5185	1
...	...	...	...	...	...	...	...
435	29703	12051	16027	13135	182	2204	1
436	39228	1431	764	4510	93	2346	1
437	14531	15488	30243	437	14841	1867	3
438	10290	1981	2232	1038	168	2125	0
439	2787	1698	2510	65	477	52	0
440 rows × 7 columns

おお…。各グループの数を確認。

cust_df['cluster_id'].value_counts()

---------------

0    279
3     78
1     76
2      7
Name: cluster_id, dtype: int64

最初に設定したクラスタがランダムなので、参考にしたサイトとは微妙に数値が異なる。

積み上げグラフで可視化する。

import matplotlib.pyplot as plt
 
clusterinfo = pd.DataFrame()
for i in range(4):
    clusterinfo['cluster' + str(i)] = cust_df[cust_df['cluster_id'] == i].mean()
clusterinfo = clusterinfo.drop('cluster_id')
 
my_plot = clusterinfo.T.plot(kind='bar', stacked=True, title="Mean Value of 4 Clusters")
my_plot.set_xticklabels(my_plot.xaxis.get_majorticklabels(), rotation=0)

[Text(0, 0, 'cluster0'),
 Text(0, 0, 'cluster1'),
 Text(0, 0, 'cluster2'),
 Text(0, 0, 'cluster3')]

クラスタ0は消費自体が少ない。クラスタ1は生鮮食品多め。2はかなり消費高め。3は雑貨と飲料がメイン。

なんとなく7つのクラスターにしてみる。

なんとなく分類はできたが、判断には実務知識要りそうな結果になった。あんまり細かくしすぎると逆に分かりにくくなることもありそう。

けっこうボリューム大きかったので、応用はまた次回。

参考文献

このサイトの主
投稿を作成しました 98

関連投稿

検索語を上に入力し、 Enter キーを押して検索します。キャンセルするには ESC を押してください。

トップに戻る