LoginSignup
0
1

More than 1 year has passed since last update.

前処理、後処理を含めたDBSCAN関数化

Posted at

はじめに

 DBSCANは、密度ベースのクラスタリング手法の1つです。sklearnライブラリを用いることで簡単に実装できます。...できますが、前処理(正規化)や後処理(クラスタリング結果とデータの紐づけ、クラスタごとにデータの分割)を毎回行うのが面倒だなー、と思ってます。
 ですので、今回の記事は前処理・後処理を含めて2次元配列を対象としたDBSCANを関数化を行いました。

 今回の記事では、関数化にフォーカスを当てて記事を作成するため、理論的な部分については最低限となります。

DBSCAN

 クラスタリングといえば距離ベースのk-meansが有名ですが、外れ値との相性がよくない・クラスタ数をしてする必要があり、結果への影響が大きい などのデメリットがあります。(理解しやすく実装しやすい・大規模なデータセットをうまく扱える などのメリットもあるため、全然使えないと手法ではありません。)
 DBSCANは、密度ベースのクラスタリング手法で、データ点の密集領域を1つのクラスターとし、逆に低密度の領域はノイズとみなすという手法です。
 外れ値を簡単に特定できノイズの多いデータセットに向いている・クラスタ数は自動で決定されるといったメリットがあります。
 疎なデータセットではあまりうまく動作しない・epsやminPtsパラメータの影響を受けやすいというデメリットがあります。(eps、minPtsについては、後述します。)

パラメーター

  • eps
    最大でどの程度離れた2点を同じクラスターに属するとみなすかを決めるパラメーター
  • minPts
    クラスターとみなすための最小限の点の数。minPts未満の点しかない場合は、ノイズとして扱われます。

アルゴリズム

 各点について、他のすべての点からの距離を計算し、もし距離がeps以下ならば、その点xに隣接する点としてマークする。その点がminPts以上の隣接点がある場合には、クラスターとしてマークします。
 この手順を繰り返すことでクラスタリングを行っていきます。
 ざっくり説明ですので、詳細を知りたい方は、以下をご覧ください。

コード

前処理(正規化)

 epsを0-1に収めるため、Min-Max normalizationにて正規化を行います。
 列ごとに指標が異なる場合は、引数axis=1を使用して、列ごとに正規化を行ってください。

DBSCAN.py
    #正規化
    normalize_dataset = preprocessing.minmax_scale(dataset)

クラスタリング

 DBSCANを行います。eps,min_sample, metricにはそれぞれ以下の値を指定してください。

  • eps:0-1。大きくすると離れた点をクラスタに取り込みやすくなります。
  • min_sample:2以上の整数。クラスタを形成する最低の数を指定して下さい。
DBSCAN.py
    #クラスタリング
    dbscan_dataset = cluster.DBSCAN(eps = eps, min_samples = min_sample).fit_predict(normalize_dataset)

後処理(各クラスタを要素とするリスト作成)

 DBSCANを実行して出力された各点のクラスタを基準に、各クラスタを要素とするリストを作成します。

DBSCAN.py
    #クラスタ数抽出
    data_class = np.unique(dbscan_dataset)
    #各クラスタのデータ数を取得
    class_list = [np.count_nonzero(dbscan_dataset == dc) for dc in data_class]
    #データセットにクラスタを紐づけ
    cluster_dataset = np.insert(dataset, col, dbscan_dataset, axis = 1)
    #クラスタ順にソート
    sorted_dataset = cluster_dataset[np.argsort(cluster_dataset[:, col])]

    #データセットをクラスタごとにlistに格納
    start, end = 0, 0
    cluster_list = []
    for cl in class_list:
        end += cl
        cluster_list.append(sorted_dataset[start:end, :col])
        start = end

全体

データ作成から、出力までの全コードを以下に記載します。
データは0-30の整数を50行2列でランダム作成し、ランダムシードは11としています。

今回は、eps=0.2, min_sample=3としています。

DBSCAN.py
import numpy as np
from sklearn import cluster, preprocessing


def DBSCAN_(dataset, eps = 0.5, min_sample = 4):
    col = dataset.shape[1]
    #正規化
    normalize_dataset = preprocessing.minmax_scale(dataset)
    #クラスタリング
    dbscan_dataset = cluster.DBSCAN(eps = eps, min_samples = min_sample).fit_predict(normalize_dataset)
    #クラスタ数抽出
    data_class = np.unique(dbscan_dataset)
    #各クラスタのデータ数を取得
    class_list = [np.count_nonzero(dbscan_dataset == dc) for dc in data_class]
    #データセットにクラスタを紐づけ
    cluster_dataset = np.insert(dataset, col, dbscan_dataset, axis = 1)
    #クラスタ順にソート
    sorted_dataset = cluster_dataset[np.argsort(cluster_dataset[:, col])]

    #データセットをクラスタごとにlistに格納
    start, end = 0, 0
    cluster_list = []
    for cl in class_list:
        end += cl
        cluster_list.append(sorted_dataset[start:end, :col])
        start = end
    return data_class, cluster_list

if __name__ == "__main__":
    #データ作成
    np.random.seed(seed = 11)
    data = np.random.randint(0, 30, (50, 2))
    #DBSCANにてクララスタリング
    data_class, cluster_list = DBSCAN_(data, eps = 0.2, min_sample = 3)

実行結果

 plotする部分のコードは本題から逸れるため、割愛させていただきます。
 xは外れ値、その他は各色ごとに1クラスタとなっております。
Figure_1.png

最後に

 今回は、前処理・後処理を含めたDBSCANの関数化を行いました。今後も、面倒だなーと感じている部分を関数・クラス化して、楽をできるようにしていこうと思います。

参考

S-Analysis
生命医学をハックする

0
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
0
1