具体例で学ぶ!情報可視化のテクニック

第2回 階層的クラスタリングによる特徴抽出

この記事を読むのに必要な時間:およそ 6 分

ベクトルクラスの作成

Javaの標準ライブラリには,多次元ベクトルを扱うクラスが存在しません。そこでまず,独自のベクトルクラスを用意することにします。クラス名は「Vector」でも良いのですが,Java標準のjava.util.Vectorクラスとの混同を避けるため,MultiVectorとしました。

MultiVectorクラスは,double型の実数を成分とし,任意の次元を持つベクトルを表現します。以下に,クラスの一部を示します。完全なソースコードはこちらをご覧ください。

リスト1 MultiVector.java(部分)

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の距離の二乗を計算します。

ノードクラスの作成

次に,クラスタのツリー構造をJavaで表現してみましょう。

まず,末端のデータ項目ノードとクラスタノードを統一的に扱うため,共通インターフェイスとなるNodeインターフェイスを作成します。Nodeには,ノードが含む全てのベクトルを返すgetVectorsメソッドを定義します。

リスト2 Node.java

public interface Node {
    List<MultiVector> getVectors();
}

次に,これを実装する形で,データ項目のノードを表現するItemクラスを作成します。Itemクラスは個々のベクトルに対応するので,getVectorsメソッドの戻り値は,そのベクトルだけを含む長さ1のリストになります。

リスト3 Item.java

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);
    }
}

同様に,Nodeインターフェイスを実装するClusterクラスを作成します。Clusterクラスは,2つの子ノードleftとrightを持ちます。getVectorsメソッドの戻り値は,leftノードとrightノードの同メソッドの呼び出し結果を連結したものになります。

リスト4 Cluster.java

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;
    }
}

著者プロフィール

浜本階生(はまもとかいせい)

1981年生まれ。栃木県出身。東京工業大学情報工学科卒業。技術やアイデアの組み合わせから面白いソフトウェアを生み出したいと日々考えている。現在,ブログの解析および視覚化の試みとして「TopHatenar」「Blogopolis」を開発,運用中。

URLhttp://d.hatena.ne.jp/kaiseh/