LoginSignup
6
5

More than 5 years have passed since last update.

JupyterでDBSCANのメリットを確認してみた

Last updated at Posted at 2019-01-25

DBSCANのメリットを可視化して確認してみました。

DBSCANとは

最も一般的なクラスタリングアルゴリズムの一つで、データ点の密度を基に分類します。
パラメータはeps(ε)、minPtsの2つ。
各データ点は下記のロジックでコア点・(密度)到達可能点・外れ値に分類されます。

  • 点 p がもし、p を含め、距離 ε(ε は p からの近傍の最大半径)以内に少なくとも minPts の点があれば、その点はコア点である。
  • 点 q がもし、各 pi+1 が pi から直接到達可能(パス上のすべての点は、 q の可能な例外を持つ、コア点で無ければならない。)な、p1 = p かつ pn = q であるパス p1, ..., pn があれば、q は p から到達可能である。
  • 他のどの点からも到達可能でないすべての点は外れ値である。

DBSCAN - Wikipedia

密度ベースのため
・球(超球)の形状を前提としない
・ノイズを分離できる
といったメリットがあり、例えばk-meansでは難しいこんなデータでも上手く分類可能(なはず)です。
output_6_1.png

また、k-meansのようにクラスタ数を指定する必要はありません。

(詳しい英語記事)

それではjupyter labで実際にクラスタリングしてみます。

ライブラリの読み込みとグラフの表示設定。scikit-learnを使います。

In[1]:

import pandas as pd
import numpy as np
import matplotlib as mpl
import seaborn as sns
from sklearn.cluster import DBSCAN
%matplotlib inline
sns.set()

datasetの読み込み。

In[2]

file = 'Dataset_Compound.csv'
df = pd.read_csv(file)

DBSCANの実施。
* eps(ε) = 0.1~5
* minPts = 5~30
の範囲で探索し、結果をDataFrameに入れてみます。

In[3]

columns=['eps','minPts','cluster_index','cluster_size']
df_dbscan = pd.DataFrame(columns=columns)

for eps in range(1,51):
    eps /= 10
    for minPts in range(5,31,5):
        dbscan = DBSCAN(eps=eps,min_samples=minPts).fit(df)
        labels = dbscan.labels_
        clusters_index = np.unique(labels) #index of clusters, '-1' indicates noize points
        clusters_size = np.bincount(labels+1) #size of each cluster
        result = [eps, minPts, list(clusters_index), list(clusters_size)]
        df_dbscan.loc[len(df_dbscan)] = result

クラスタ数6以上、ノイズ100未満の結果に絞って表示。

In[4]

filt = df_dbscan[columns[2]].map(lambda x: len(x)>5)
filt = filt & df_dbscan[columns[3]].map(lambda x: x[0]<100)
df_dbscan[filt]

Out[4]

eps minPts cluster_index cluster_size
54 1.0 5 [-1, 0, 1, 2, 3, 4] [97, 92, 15, 21, 158, 16]
60 1.1 5 [-1, 0, 1, 2, 3, 4] [91, 92, 18, 24, 158, 16]
66 1.2 5 [-1, 0, 1, 2, 3, 4] [85, 92, 22, 26, 158, 16]
72 1.3 5 [-1, 0, 1, 2, 3, 4] [74, 92, 27, 32, 158, 16]
73 1.3 10 [-1, 0, 1, 2, 3, 4] [90, 92, 20, 23, 158, 16]
78 1.4 5 [-1, 0, 1, 2, 3, 4] [64, 92, 28, 41, 158, 16]
79 1.4 10 [-1, 0, 1, 2, 3, 4] [87, 92, 21, 25, 158, 16]
84 1.5 5 [-1, 0, 1, 2, 3, 4] [59, 93, 31, 42, 158, 16]
85 1.5 10 [-1, 0, 1, 2, 3, 4] [83, 93, 22, 27, 158, 16]
86 1.5 15 [-1, 0, 1, 2, 3, 4] [99, 92, 15, 21, 156, 16]

上手く分類できていそう。
78番目をグラフで確認してみます。

In[5]

index = 78
eps = df_dbscan[columns[0]][index]
minPts = df_dbscan[columns[1]][index]
dbscan = DBSCAN(eps=eps,min_samples=minPts).fit(df)
labels = dbscan.labels_
X = df.copy()
X['labels'] = labels
X['color'] = X.labels.map(lambda x: 'noize' if x<0 else 'r'+str(x)) #色分けのためにcategorical dataを作成
X.sort_values('color')
sns.scatterplot(x='x', y='y', data=X, hue='color')

Out[5]
output_9_1.png

右側:ノイズを上手く切り分けられています。
左下:ドーナツ型(紫)とその内部(茶)を上手く切り分けられています。
左上:他のクラスタと密度に違いがあり、周囲がノイズと判定されています。

まとめ

DBSCANの下記のメリットが確認できました。

  • 球(超球)の形状を前提としない
  • ノイズを分離できる
  • クラスタ数の指定が必要ない

反対に、デメリットには下記が挙げられるようです。

  • クラスタの密度に差が大きいと上手くクラスタ分けできない
    eps, minPtsのパラメータで全クラスタの密度を決めるからですね。
    階層クラスタリングによる解決がありえます。
  • 計算コストが高い
    全データ点を対象とした計算を反復して行うため。
    今回程度の計算量では気になりませんが、次元の増加に弱い、リアルタイムのアプリケーションには実装しにくいなど言われています。

Dataset References:
Compound: C.T. Zahn, Graph-theoretical methods for detecting and describing gestalt clusters. IEEE Transactions on Computers, 1971. 100(1): p. 68-86.

6
5
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
6
5