はじめに
前回は、
目標
本連載では、
Java開発環境のセットアップ
手元にJavaの開発環境がなく、
NetBeansのインストール時にはJDK
ソースコードのダウンロード
今回作成するプログラムのソースコードは、
階層的クラスタリングの概要
今回は、
階層的クラスタリングとは、
階層的クラスタリングのアルゴリズムの手順を示すと、
- データ集合の中から、
互いの距離が最も近くなるデータ項目の対を探す。 - その項目対を、
1つのクラスタに統合する。 - そのクラスタと残りのデータ集合の中から、
互いの距離が最も近くなる要素対を探して統合する。 - 上記の処理を、
データ全体が1つのクラスタに統合されるまで繰り返す。

以上の手順によって、
各クラスタのノードが常に2つの子ノードを持つことから、

本連載では、
ベクトルクラスの作成
Javaの標準ライブラリには、
MultiVectorクラスは、
public class MultiVector {
private double[] data;
// 指定された次元数でゼロベクトルを作成
public MultiVector(int dimension) {
data = new double[dimension];
}
// 次元数チェック
private void checkDimension(MultiVector v) {
if (dimension() != v.dimension()) {
throw new IllegalArgumentException("Dimension mismatch.");
}
}
// 指定されたベクトルを加算
public void add(MultiVector v) {
checkDimension(v);
for (int i = 0; i < dimension(); i++) {
data[i] += v.data[i];
}
}
...
}
MultiVectorには、
- void add(MultiVector v)
- ベクトルにvを加算します。
- void subtract(MultiVector v)
- ベクトルからvを減算します。
- void multiply(double d)
- ベクトル成分をd倍します。
- void divide(double d)
- ベクトル成分をdで除算します。
- double norm()
- ベクトルのノルム
(長さ) を計算します。 - void normalize()
- ベクトルを正規化します
(ノルムが1になるように調整します)。 - double distanceSq(MultiVector v)
- このベクトルとvの距離の二乗を計算します。
ノードクラスの作成
次に、
まず、
public interface Node {
List<MultiVector> getVectors();
}
次に、
public class Item implements Node {
private String name; // 結果出力用のプロパティ
private MultiVector vector;
public Item(String name, MultiVector vector) {
this.name = name;
this.vector = vector;
}
public String getName() { return name; }
public MultiVector getVector() { return vector; }
public List<MultiVector> getVectors() {
// ベクトル自身をリスト化して返す
return Collections.singletonList(vector);
}
}
同様に、
public class Cluster implements Node {
private Node left;
private Node right;
private List<MultiVector> cachedVectors;
public Cluster(Node left, Node right) {
this.left = left;
this.right = right;
}
public Node getLeft() { return left; }
public Node getRight() { return right; }
public List<MultiVector> getVectors() {
// 高速化のため結果をキャッシュする
if (cachedVectors == null) {
cachedVectors = new ArrayList<MultiVector>();
// leftノードとrightノードのベクトル集合を連結
cachedVectors.addAll(left.getVectors());
cachedVectors.addAll(right.getVectors());
}
return cachedVectors;
}
}
距離関数クラスの作成
さて、
この距離の計算には、

戦略を後で簡単に切り替えられるように、 このインターフェイスを実装し、 このdistanceメソッドでは、 ベクトル、 上のbuildメソッドは、 それでは、 クラスタリングの実行結果は、 以上を行うDemoクラスは、 Demoクラスを実行すると、 いかがでしょうか。ブルーとシアン、 今回作成した階層的クラスタリングのプログラムは、 今回は、 次回は視覚的な観点にフォーカスを移し、public interface DistanceEvaluator {
double distance(Node n1, Node n2);
}
public class NearestDistanceEvaluator implements DistanceEvaluator {
public double distance(Node n1, Node n2) {
double minDistSq = Double.MAX_VALUE;
for (MultiVector v1 : n1.getVectors()) {
for (MultiVector v2 : n2.getVectors()) {
// 全てのベクトルの組み合わせに対して距離を計算
double distSq = v1.distanceSq(v2);
// 最小となる距離を採用
if (distSq < minDistSq) {
minDistSq = distSq;
}
}
}
return Math.sqrt(minDistSq);
}
}
階層的クラスタリングの実装
public class ClusterBuilder {
private DistanceEvaluator distanceEvaluator;
public ClusterBuilder(DistanceEvaluator distanceEvaluator) {
this.distanceEvaluator = distanceEvaluator;
}
public Node build(List<? extends Node> nodes) {
// ノードが1つに集約されるまで繰り返す
while (nodes.size() > 1) {
Node merge1 = null;
Node merge2 = null;
double minDist = Double.MAX_VALUE;
// 距離が最小となるノード対を探す
for (int i = 0; i < nodes.size(); i++) {
Node n1 = nodes.get(i);
for (int j = i + 1; j < nodes.size(); j++) {
Node n2 = nodes.get(j);
double dist = distanceEvaluator.distance(n1, n2);
if (dist < minDist) {
merge1 = n1;
merge2 = n2;
minDist = dist;
}
}
}
// 次ステップ用のノードリストを作成
List<Node> nextNodes = new ArrayList<Node>();
for (Node node : nodes) {
// 統合対象にならなかったノードを追加
if (node != merge1 && node != merge2) {
nextNodes.add(node);
}
}
// 統合対象のノード対をクラスタ化して追加
nextNodes.add(new Cluster(merge1, merge2));
nodes = nextNodes;
}
return nodes.get(0);
}
}
色集合の階層的クラスタリング
public class Demo {
public static void main(String[] args) {
new Demo().run();
}
public void run() {
// 入力データを作成
List<Item> input = new ArrayList<Item>();
input.add(new Item("BLUE", colorToVector(Color.BLUE)));
input.add(new Item("CYAN", colorToVector(Color.CYAN)));
input.add(new Item("MAGENTA", colorToVector(Color.MAGENTA)));
input.add(new Item("ORANGE", colorToVector(Color.ORANGE)));
input.add(new Item("PINK", colorToVector(Color.PINK)));
input.add(new Item("RED", colorToVector(Color.RED)));
// 最短距離法に基づく階層的クラスタリングを準備
DistanceEvaluator evaluator = new NearestDistanceEvaluator();
ClusterBuilder builder = new ClusterBuilder(evaluator);
// クラスタリングを実行
Node result = builder.build(input);
// クラスタリング結果を表示
output(result, 0);
}
private MultiVector colorToVector(Color c) {
// 色成分を3次元のベクトルに変換
return new MultiVector(c.getRed(), c.getGreen(), c.getBlue());
}
private void output(Node node, int depth) {
// インデントを表示
for (int i = 0; i < depth; i++) {
System.out.print(" ");
}
if (node instanceof Item) {
// 末端ノードなら項目名を表示
System.out.println(((Item) node).getName());
} else if (node instanceof Cluster) {
// クラスタなら"+"を表示し、子ノードを再帰的に表示
System.out.println("+");
Cluster cluster = (Cluster) node;
output(cluster.getLeft(), depth + 1);
output(cluster.getRight(), depth + 1);
}
}
}
+
+
RED
+
MAGENTA
+
ORANGE
PINK
+
BLUE
CYAN
まとめと次回予告