1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

1000人以上のユーザーが、自社商品、A社商品、B社商品のどれを選んだのか、その人たちの傾向を決定木で分析しようと考え、人数が多いので、クラスタリング分類してグループ分けすることにしました。

Pythonで階層的クラスタリングをやってみたところ、トーナメント表みたいなグラフは出るんですが、誰がどのグループに入っているのかほとんど分からなかったので、結果をファイルに出力しました。

image.png

クラスタリング条件

① クラスタリング位置指定
・y軸 1500
・ファイル形式は、X軸の顧客ID(左から)の順番に作成
② クラスタリング数指定 
・8ケ
・ファイル形式は、サンプルデータと同じ顧客ID昇順に作成

データ(サンプル用に作成したもの)

顧客ID
年間購入金額(万円)
年齢
性別:1→男性 2→女性
年収(万円)
image.png

環境

python3.x
jupyter lab

フォルダ構成

1_flow : Pythonファイル
2_data : サンプルデータ
3_output : 出力ファイルの保存先
image.png

コード

import pandas as pd
import subprocess
import os

from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
import matplotlib.pyplot as plt
import numpy as np

#=============================================
# Inputファイル情報
#=============================================
INPUT_folder = "2_data"        
INPUT_DNAME = "サンプルデータ.csv"
#=============================================
# Outputファイル情報
#=============================================
OUTPUT_folder = "3_output"
#=============================================
# カレントパス
#=============================================
current_dpath = os.getcwd()
print("INFO:カレントパス:" + current_dpath)

#=============================================
# パレントパス
#=============================================
parent_dpath =os.path.sep.join(current_dpath.split(os.path.sep)[:-1])

print("INFO:パレントパス:" + parent_dpath)   

#=============================================
# Inputデータファイル Path
#=============================================
input_dpath =os.path.sep.join([parent_dpath + '\\' + INPUT_folder,INPUT_DNAME])
print("INFO:データファイルパス:" + input_dpath) 

#=============================================
# Outputデータファイル Path
#=============================================
output_dpath =parent_dpath + '\\' + OUTPUT_folder
print("INFO:出力先のフォルダパス:" + output_dpath)   

#=============================================
# サンプルデータ読み込む
#=============================================
df = pd.read_csv(input_dpath,encoding='shift-JIS')
df = pd.DataFrame(df)

#=============================================
# 分割の閾値設定
#=============================================
# ① y軸のクラスタ分割位置を指定 
threshold_distance = 1500

# ② クラスタ数指定  : 8ケ
criter = 8

・threshold_distance = 1500 → 距離による閾値指定
  デンドログラムのy軸にあたる距離の値で指定します。

・criter = 8 → クラスタ数を指定して分割します。

※デンドログラムの縦軸は、どれくらいデータ同士が似ているか(または違うか)を表す
 数値です。この縦軸の値を使って、グループを分けるための基準の高さを決めます。

#=============================================
# linkageの計算
#============================================
Z = linkage(df.iloc[:, 1:4], method="ward")

linkage → データの特徴を表す数字を使って、似ているもの同士を順番にグループに
まとめます。ここでは「Ward法」というやり方でグループを作っています。

どのグループがどんな順番でくっついたか」がわかるデータを作ります。
具体的には以下の流れで処理が進みます。
1. データの数字を使って「どれくらい似ているか」を計算する
2.その似ている度合いをもとに、だんだんとグループを作っていく
3. そして、グループのくっつき方の記録が結果として得られる


#=============================================
# 最大距離 適切な閾値の目安が得られます。
#=============================================
#print("最大距離:", np.max(Z[:, 2]))

#=============================================
# デンドログラムを描画
#=============================================
dendro = dendrogram(Z, labels=df["顧客ID"].values)

#=============================================
# 閾値に赤い破線のラインを入れる
#=============================================
plt.axhline(y=threshold_distance, color='red', linestyle='--')

#=============================================
# クラスタリング画 保存
#=============================================
plt.rcParams["font.size"] = 10
plt.savefig(output_dpath + "\\クラスタリング.png")
plt.show()

左の図(jupyter lab):閾値を示す赤い破線の位置が正しく表示されませんでしたが
デンドログラムのy軸スケールや描画範囲の設定により、赤線が正確な位置に表示されない
ことがあります。
今回の例でも完全に一致しませんでしたが、クラスタリングの計算自体には影響ありません。

右の図(ターミナル):正しく表示されます。

image.png

※ np.max(Z[:, 2])で最大距離を確認すると、適切な閾値の目安が得られます。

#=============================================
#  クラスタのラベルをつける
#=============================================
labels_distance = fcluster(Z, t=threshold_distance, criterion="distance")

fcluster → 先ほど作ったlinkageの結果Zをもとにクラスタ分け
t=threshold_distance → 縦軸のこの高さでグループを切り分ける
criterion="distance" → 距離を基準に何個のクラスタにするかを決める

#=============================================
# 元のデータにクラスタ番号をくっつける
#=============================================
df_distance = df.copy()
# cluster列を追加し、それぞれの行が属するクラスタ番号を書き込む
df_distance["cluster"] = labels_distance
#=============================================
# デンドログラムで並んでいた順に並べ替える 
#=============================================
ordered_df_distance = df_distance.iloc[dendro["leaves"]].reset_index(drop=True)

dendro["leaves"] → 描画時の左から右への並び順のインデックス」
これを使ってデータを並べ替えることで、デンドログラムと同じ順序になります

#=============================================
# CSV出力
#=============================================-
ordered_df_distance.to_csv(
    output_dpath + '\\クラスタリング結果_距離' + str(threshold_distance) + '_並べ替え.csv',
    index=False,
    encoding="utf-8-sig"
)
ordered_df_distance.head(3)  

image.png

顧客IDを見ると、デンドログラム(トーナメント表?)と同じ順序で作成されました。

<参考 クラスタ数を指定する方法>

クラスタ数を指定しますが、非階層のk-meansではありません

#=============================================-
#  クラスタのラベルをつける
#=============================================-
labels_maxclust = fcluster(Z, t=criter, criterion="maxclust")

fcluster → クラスタ番号をつける
t=criter → クラスタの数を指定
criterion="maxclust → 指定した数になるように、デンドログラムを切り分けてグループを作る

※ linkage()で作った階層的な計算結果を fcluster()で指定した個数に分けています。

#=============================================
# 元のデータにクラスタ番号をくっつける
#=============================================
df_maxclust = df.copy()
# cluster列にクラスタ番号追加
df_maxclust["cluster"] = labels_maxclust

#=============================================-
# CSV出力
#=============================================-
df_maxclust.to_csv(
    output_dpath + '\\クラスタリング結果_クラスタ数_' + str(criter) + '_.csv',
    index=False,
    encoding="utf-8-sig"
)
df_maxclust.head(3)

image.png

#=============================================
# 保存フォルダ開く
#=============================================
os.startfile(os.path.realpath(output_dpath) + "\\")

出力

image.png

①はクラスタリング図のx軸値(左からの顧客ID)の順番と一致しているか確認するため、この形式にしました。
(実際はクラスタリング図の顧客IDが小さすぎて読めずじまでしたが)

まとめ

クラスタリングによりグループを分けることができたため、このデータを用いて決定木を作成しますが、詳細については別途ご説明します。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?