クラスタ作成に向けた準備
前エントリで期間を空けて〜と言いましたが,完全に放置してしまいました.
3章のクラスタリングをプログラミングしていたのですが,バグバグになってしまい完全にハマってしまっていました.
時間は掛かりましたが,バグも解きほぐれてまともに動くようになったので,掲載します.
今回は階層型クラスタを作成する訳ですが,そのためには必要なデータ構造について準備しておく必要があります.
おおまかな流れはこんな感じになります.
準備
Rubytreeによる木構造の表現
SetのようなもののRubyでの表現
本題
階層型クラスタの作成
K平均法クラスタの作成
木構造の表現
後ほど作成する階層型クラスタは,最終的に木構造のデータとして表現されます.なので,Ruby上でも木を扱えなければなりません.
ArrayやHashのように標準ライブラリとして実装されていればいいのですが,さすがに日常的に使うやつはいないのか,残念ながら標準ではありません.
幸い,Gemsにいくつか登録されているものがあるので,ありがたく使わせていただきましょう.
今回は名前もそのまんまな「Rubytree」を使います.
Rubytreeの使い方
インストールはgemコマンド一発で,さくっと済ませます.
% sudo gem install rubytree
Rubytreeでは通常の木(Tree::TreeNode)と二分木(Tree::BinaryTreeNode)のどちらも作れます.
階層型クラスタは二分木で作成しますので,ここからは二分木(Tree::BinaryTreeNode)を使うことを前提とします.
基本的な使い方は,以下の通りです.
非常に直感的に操作できるので,使い方に迷うことはないでしょう.
ちなみにRubytreeのメソッドは全てCapitalizeされています.Rubyistにはちょっと違和感ありますね...
require 'rubygems' require 'tree/binarytree' include Tree # ルートノードを作成 root = BinaryTreeNode.new('root') # 子ノードを作成 left_child = BinaryTreeNode.new('left_child') # 子ノードに子ノード(ルートから見たら孫)を追加 left_child << BinaryTreeNode.new('left_grand_child') left_child << BinaryTreeNode.new('right_grand_child') # ルートに子ノードを追加 root << left_child # ルートに直接子ノードを追加することもできます root << BinaryTreeNode.new('right_child') # 最後に全体を表示 root.printTree
実行すると↓のようになります.
* root |---+ left_child | |---> left_grand_child | +---> right_grand_child +---> right_child
Rubytreeをちょっと改変
これで二分木を手に入れた訳ですが,この先都合が悪い面もあります.
子ノードとしてBinaryTreeNodeしか受け付けないので,必ずBinaryTreeNode.newして追加しなければならない
→正直面倒くさい
全ての階層のノードを表示してしまう
→クラスタにした際には,末端のノード(葉)のみ表示したい
この不満を解消するために,Rubytreeを継承したクラスを作り,一部メソッドをオーバーライドします.
module My class Node < Tree::BinaryTreeNode # 新しい枝を生成する # 葉には何を入れてもOK def Node.new_branch(name, left, right) branch = Node.new(name) left.respond_to?(:name) ? (branch << left) : (branch << Node.new(left)) right.respond_to?(:name) ? (branch << right) : (branch << Node.new(right)) branch end # ツリー表示をオーバーライド def printTree(level = 0) if isRoot? print name else print "|" unless parent.isLastSibling? print(' ' * (level - 1) * 2) print(isLastSibling? ? "+" : "|") print "----" print(hasChildren? ? "+" : ">") end # 枝は名前を表示しない isLeaf? ? (puts " #{name}") : puts children { |child| child.printTree(level + 1) } end end end
先ほどとほぼ同じ結果を得られるコードはこのようになります.
include My root = Node.new('root') # いきなり子ノードと孫ノードを作って追加する root << Node.new_branch('left_child', 'left_grand_child', 'right_grand_child') root << Node.new('right_child') root.printTree
上記のクラスを使った場合の結果はこんな感じ.
root |----+ | |----> left_grand_child | +----> right_grand_child +----> right_child