NO_WAIT

主にプログラミング

DBpediaから科学者の情報を取得→Rでクラスタリング(K-means)→結果をD3.jsで可視化

クラスタリングアルゴリズムに興味を持ち、実際のデータに適用してみたくなりました。 いきなり巨大なデータを解析するのは難しいので、手元のMacBook 1台で解析するのに無理のないサイズで、 かつ自分の興味のあるデータを解析することを考えました。 そこで解析対象としてDBpedia(日本語)上の科学者データおよび そのWikipediaにおけるリンク情報を選びました。2014年9月に取得したのは科学者2088人分のデータで、 思っていたより少なく、この程度なら無理ということはありません。 解析方法はR言語で利用可能なK-meansアルゴリズムを若干の前処理を行ったデータセットに対して愚直に適用するのみという 単純なものです。 こんな方法で検出されたクラスタは果たして人間にとって意味のある分類になっているのでしょうか? 科学者は学問分野で分類されるのでしょうか、それとも出身地で分類されるのでしょうか? 結論から言えば、「分類」としてはあまりうまくいかず、解釈の難しいクラスタばかりが得られましたが、 ともかくも結果をJavaScriptライブラリD3.jsで可視化しました。

f:id:shinaisan:20140923123111p:plain

解析結果を表示するサイトです。ドロップダウンリストからクラスタを選択すると、 クラスタ内からランダムに取得された科学者の名前、およびそれらが共有するリンクをネットワーク状に表示します。

実験の流れ

  1. DBpediaから科学者名をリストする。
  2. DBpediaからWikipedia記事のリンク情報をダウンロードする。
  3. ダウンロードされたデータから不要なデータを削除するなど前処理を行う。
  4. Rでクラスタリング
  5. 結果の可視化

DBpediaからの科学者データとリンク情報の取得および前処理

科学者のリスト

解析対象のデータはWikipediaのデータを構造化して提供しているDBpedia(日本語のみ)から取得します。 科学者データに興味があったので、下記SPARQLを発行し、まずは科学者の名前をリストしました。

select ?s where {
  ?s a dbpedia-owl:Scientist
}

リンク情報のダウンロードとフィルタリング

各科学者に対してWikipedia上に記事があります。各記事はWikipedia上の関連記事へのリンクを含みます。 このリンク情報はwiki.dbpedia.org : Downloads 39からダウンロードできます。 圧縮済みの巨大なファイルです。 解析対象を日本語に絞っていたので4. Localized Datasetsのデータセットを利用します。 Wikipedia Pagelinksのja列が目的のデータ(ntファイル)です。 2014年9月初頭に取得したこのリンクデータの解凍後データサイズは8.7GB、総行数は48,340,657行にもなります。 ファイルの各行は<リンク元リソース> dbpedia-owl:wikiPageWikiLink <リンク先リソース> .という3つ組になっていますので、 1行ずつスキャンして、リンク元リソースがSPARQLで取得した科学者名のリストに含まれていればその行を出力するという 単純なフィルタプログラムでまずは必要なリンク情報のみを準備しておきます。

不要な情報の削除

科学者データをクラスタリングするにあたり、年月日などの時間に関するデータは除外することにしました。 当初の目的は、愚直にクラスタリングアルゴリズムを適用したときにどんな分類がなされるのかを見ることにあったので、 年月日データも含めていたのですが、実際に表示までしてみて、あまり分類に寄与していないように見えたため、 ノイズとして除外することにしました。

年月日に関するデータは下記2種類のSPARQLによって取得し、フィルタ済みのリンク情報ファイルから除去しました:

select distinct * where {
  ?s prop-ja:wikiPageUsesTemplate template-ja:Year-definition .
}

日付

select distinct * where {
  ?s prop-ja:wikiPageUsesTemplate template-ja:1年の月と日 .
}

Rによる解析

ファイルの読み込み

ここまでリンク情報ファイルのフィルタリングを行い、以下のような内容のファイルが得られたとします:

ドナルド・クヌース Open_Content_Alliance
ドナルド・クヌース ウィスコンシン州
〜略〜
ドナルド・クヌース Category:数学に関する記事
マービン・ミンスキー ニューヨーク
マービン・ミンスキー 認知科学
〜略〜

科学者名、空白、リンク名からなる行が続きます。 このテキストファイルに対し、文書検索などの分野で言うところのDocument-term Matrixを作成します。 ここでのDocumentは科学者名、Termはリンクに相当します。 行列の要素は関連があれば1、なければ0という単純な二値とします。

Rを起動し、ファイルをまずは読み込みます。

tbl <- read.table(ファイル名, sep = " ", quote = "", header = FALSE)
head(tbl)
#                   V1                    V2
# 1 ドナルド・クヌース Open_Content_Alliance
# 2 ドナルド・クヌース      ウィスコンシン州
# 3 ドナルド・クヌース        ミルウォーキー
# 4 ドナルド・クヌース                  数学
# 5 ドナルド・クヌース            計算機科学
# 6 ドナルド・クヌース    スタンフォード大学

読み込んだデータフレームの列はfactorになっています。 これをまずは数値に変換します。各名前に対して番号を振るようなものです。

FI <- tbl[, 1] # Factor of Items(Scientists).
FL <- tbl[, 2] # Factor of Links.
A <- cbind(as.numeric(FI), as.numeric(FL)) # Associations.

Document-term Matrixの作成

Document-term Matrixをsparse matrixとして作成します(D)。MatrixライブラリのsparseMatrix関数を利用します。

D <- sparseMatrix(i = A[, 1], j = A[, 2], x = 1, dims = c(max(A[, 1]), max(A[, 2])))
D[D > 1] <- 1 # 時々重複があるので除去しておく。

不要な行と列の削除

次に、重要でないリンクを削除します。リンクの被参照回数(いわゆるDocument Frequency - DF)について、 それが1であるようなリンクはクラスタリングにとって全く役に立たないはずですので当然除外します。 DFの最小値を大きくして、不要なリンクを削除していけば次元削減が可能ですが、 どこまで削除すればよいかは、明らかではないので、適当にDFの最小値を3としました。 今回、あまり細粒度のクラスタリングをする気はなく、数えられる程度の数のクラスタが できれば良かったので、DFの大きなリンク(Term)以外を切り捨てても良いような気がしますが、 可能な限り愚直な方法を採用します。(ただしクラスタリング計算時間は余計にかかります。)

L <- which(colSums(D) > 2) # Links: DFが3以上のもの。
D <- D[, L] # Dから不要なリンクを削除。
I <- which(rowSums(D) > 0) # Items: もやはリンクを含まなくなってしまった者は除外する。
D <- D[I, ]

ここまでで科学者数2001(length(I))、リンク数6709(length(L))となりました。

K-means実行

Rのkmeans関数を実行します。オプションでアルゴリズムの指定が可能ですが、 気にせずデフォルトのままで実行します。クラスタ数8は適当です。

cl <- kmeans(D, 8)

クラスタのサイズを確認します。

cl$size
# [1] 178  81 316   1 595 653 157  20
which(cl$size < 200)
# [1] 1 2 4 7 8

見事にサイズが不均一です。 さすがにサイズ300も500もあるクラスタは大きすぎて解釈は難しいでしょう。 K-means法はランダムな初期値に依存して結果が変わるので、 実行するたびクラスタサイズの分布も変わりますが、 特異的に巨大な「その他大勢」的クラスタがほぼ毎回検出されます。

クラスタの調査

まずクラスタNo. 1(サイズ178)からその顔ぶれを見ていきます。 代表20名を表示します。

k <- 1
levels(FI)[head(I[cl$cluster == k], 20)]
#  [1] "S._R._シュリニヴァーサ・ヴァラダン"    
#  [2] "アーネスト・チェザロ"                  
#  [3] "アイザック・ニュートン"                
#  [4] "アウグスト・フェルディナント・メビウス"
#  [5] "アグナー・アーラン"                    
#  [6] "アディ・シャミア"                      
#  [7] "アドリアン=マリ・ルジャンドル"        
#  [8] "アラン・チューリング"                  
#  [9] "アルキメデス"                          
# [10] "アルバート・タッカー"                  
# [11] "アルベルト・ブルゼフスキ"              
# [12] "アレクサンドリアのテオン"              
# [13] "アレクサンドリアのヘロン"              
# [14] "アレクサンドル・グロタンディーク"      
# [15] "アレクサンドル・リャプノフ"            
# [16] "アンジェイ・モストフスキ"              
# [17] "アンドリュー・ワイルズ"                
# [18] "アンドレ・ヴェイユ"                    
# [19] "アンドレ=マリ・アンペール"            
# [20] "アンドレイ・コルモゴロフ"

数学者か物理学者のようです。このクラスタに属する科学者から最もよくリンクされている記事を20件表示して確認してみます。

levels(FL)[L[head(order(colSums(D[cl$cluster == k, ]), decreasing = T), 20)]]
#  [1] "数学者"                    "Category:数学に関する記事"
#  [3] "数学"                      "Category:20世紀の数学者"  
#  [5] "Category:19世紀の数学者"   "Category:21世紀の数学者"  
#  [7] "ドイツ"                    "フランス"                 
#  [9] "Category:日本の数学者"     "物理学"                   
# [11] "Category:存命人物"         "パリ"                     
# [13] "数論"                      "Category:フランスの数学者"
# [15] "物理学者"                  "アメリカ合衆国"           
# [17] "ケンブリッジ大学"          "王立協会"                 
# [20] "天文学"                    "プリンストン大学"

数学者がトップに来ますが、物理学から天文学までいろいろ混じっています。

特徴的なクラスタ「歯学者」

次は81名からなるクラスタNo. 2を見てみます。

k <- 2
levels(FI)[head(I[cl$cluster == k], 20)]
#  [1] "安藤正一"            "伊藤公一_(歯周病学)" "井上孝_(歯学者)"    
#  [4] "浦出雅裕"            "岡野友宏"            "葛西一貴"           
#  [7] "丸山剛郎"            "亀山洋一郎"          "吉成正雄"           
# [10] "吉田憲司_(歯学者)"   "久光久"              "久保田康耶"         
# [13] "興地隆史"            "金子譲"              "月星光博"           
# [16] "古森孝英"            "古谷野潔"            "後藤滋巳"           
# [19] "向井美惠"            "荒川浩久"

すべて全く聞いたこともない日本人の名前です。「歯学者」という文字列が目につきますが、 リンクを確認してみます。すると…

levels(FL)[L[head(order(colSums(D[cl$cluster == k, ]), decreasing = T), 20)]]
#  [1] "歯学者"                   "Category:日本の歯学者"
#  [3] "歯科医師"                 "日本"
#  [5] "医歯薬出版"               "Category:存命人物"
#  [7] "歯学博士"                 "Category:日本の歯科医師"
#  [9] "クインテッセンス出版"     "国際歯科研究学会"
# [11] "永末書店"                 "日本歯科医学教育学会"
# [13] "日本歯科医学会"           "日本口腔科学会"
# [15] "日本顎関節学会"           "歯学部"
# [17] "東京医科歯科大学"         "歯界展望"
# [19] "日本口蓋裂学会"           "国際歯科研究学会日本部会"

見事なまでに歯、歯、歯…。歯に関係しないものは「存命人物」くらいなもの。 「クインテッセンス出版」と「永末書店」は一見無関係に見えますが、 どちらも歯学関係の出版社です。 このクラスタは今回の実験の中で最もきれいに抽出されたクラスタです。 リンク「歯学者」のDFは81でこのクラスタサイズと等しくなっています。 もっともK-means法はランダムに選ばれた初期値に非常に左右されるアルゴリズムですので、 このクラスタが必ず精度よく検出される訳ではありません。

アインシュタインが孤立?

クラスタNo. 4はサイズ1であり、どこにも分類されず孤立した科学者がいることを示し、目を引きます。 誰でしょうか?

k <- 4
levels(FI)[head(I[cl$cluster == k], 20)]
# [1] "アルベルト・アインシュタイン"

アインシュタインです。なぜこのような結果になったのでしょうか。 実際にアインシュタインについてのWikipediaの記事を見てみると比較的規模が大きいことがわかります。 そのため、記事が含むリンクもかなりの数に上ると思われます。

そこで、まず科学者ごとにそれが含むリンク数を集計してみます。 Document-term Matrix Dはリンクがあれば対応するセルが1となりますので、 列についての和を取れば集計できます。 (注: 解析の前にリンク数をかなり削減しているため、実際のリンク数は倍以上あります。)

itemSizes <- rowSums(D)
summary(itemSizes)
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#    2.00   20.00   29.00   32.52   40.00  223.00 

平均約32であるところ、最大は223とかなり大きいです。 上位10名ほどリストアップしてみます。

top <- head(order(itemSizes, decreasing = T), 10)
data.frame(name = levels(FI)[I[top]], size = itemSizes[top])
#                              name size
# 1    アルベルト・アインシュタイン  223
# 2                  マリ・キュリー  179
# 3          チャールズ・ダーウィン  177
# 4            ライナス・ポーリング  139
# 5      アントワーヌ・ラヴォアジエ  137
# 6                        湯川秀樹  136
# 7                  レオ・シラード  134
# 8  エルヴィン・シュレーディンガー  131
# 9                        河合隼雄  129
# 10       ジョン・フォン・ノイマン  125

やはりアインシュタインの記事は突出して規模が大きいようです。実際に読んでみると記事は相当の充実ぶりであり、訪日時に天ぷら弁当を食っただのといったどうでもいいエピソードまで載っており、有名なだけあって好奇の視線に晒されやすいのでしょうか。(実際のWikipediaの記事には500以上のリンクが含まれています。) マリ・キュリーが次点ですが、この人もクラスタリングを繰り返す中で 高頻度で孤立することを確認しています。

K-means法は、クラスタリング対象の各ベクトルと、クラスタの中心ベクトルとのEuclid距離を評価し、 それが最小となるクラスタにベクトルを割り当てることを繰り返します。 リンクを多く含むデータがひとたび中心となれば、それとのEuclid距離はどうしても大きくならざるを得ず、結果として孤立するという結果に至ると推測できます。 また、そもそもの問題としてDocument-term Matrixの作り方に問題があるという見方もできます。 リンクの重要度を全く考慮せず一律に1と評価しているため、ノイズを含んでしまっていると言えるためです。

謎のクラスタ

クラスタNo. 7を見てみます。

k <- 7
levels(FI)[head(I[cl$cluster == k], 20)]
#  [1] "ジェラルド・カーティス" "伊吹裕子"               "伊勢村護"              
#  [4] "伊藤圭祐"               "伊藤敬一"               "伊藤創平"              
#  [7] "伊藤邦彦"               "雨谷敬史"               "影山喜一"              
# [10] "奥村昭博"               "奥直人"                 "奥田都子"              
# [13] "横越英彦"               "横田勇"                 "下位香代子"            
# [16] "加治屋勝子"             "河原崎泰昌"             "河端恵美子"            
# [19] "賀川義之"               "貝沼やす子"            
levels(FL)[L[head(order(colSums(D[cl$cluster == k, ]), decreasing = T), 20)]]
#  [1] "日本"                        "大学院"                     
#  [3] "学位"                        "静岡県立大学"               
#  [5] "Category:静岡県立大学の教員" "教授"                       
#  [7] "Category:存命人物"           "助教授"                     
#  [9] "研究"                        "公式ウェブサイト"           
# [11] "博士課程"                    "薬学研究科"                 
# [13] "講師"                        "東京大学"                   
# [15] "薬学部"                      "Category:日本の薬学者"      
# [17] "助手"                        "修士課程"                   
# [19] "薬学"                        "3月"

またも全く知らない人物名だらけですが、リンクに「静岡県立大学」や「薬学」と言った文字列が見えます。 これらリンクのDFを集計してみます。

Shizuoka <- which(L %in% grep("静岡県立大学|薬学", levels(FL)))
data.frame(name = levels(FL)[L[Shizuoka]], df = colSums(D[, Shizuoka]), df7 = colSums(D[cl$cluster == k, Shizuoka]))
#                           name  df df7
# 1  Category:静岡県立大学の教員 138 134
# 2        Category:日本の薬学者  60  49
# 3        バイオマーカー_(薬学)   3   3
# 4                 静岡県立大学 151 142
# 5       静岡県立大学短期大学部  14  14
# 6                   日本薬学会  29  20
# 7                  博士_(薬学)  15  13
# 8                         薬学  66  42
# 9                       薬学科  22  19
# 10                薬学系研究科   3   3
# 11                  薬学研究院   8   8
# 12                  薬学研究科  59  58
# 13                      薬学士   5   5
# 14                      薬学者  45  34
# 15                    薬学専攻   4   4
# 16                    薬学博士  43  32
# 17                      薬学部  63  53

df列は総DF、df7列はクラスタNo. 7内におけるDF数です。 どうやら静岡県立大学と薬学に関するクラスタと考えて良さそうです。 (ただし、ジェラルド・カーティスは関係ないようです。)

生物学者

最後にサイズ20のクラスタNo. 8を見てみます。

k <- 8
levels(FI)[head(I[cl$cluster == k], 20)]
#  [1] "J・B・S・ホールデン"                "アウグスト・ヴァイスマン"          
#  [3] "アルフレッド・ラッセル・ウォレス"   "ウィリアム・ドナルド・ハミルトン"  
#  [5] "エドモンド・ブリスコ・フォード"     "エドワード・オズボーン・ウィルソン"
#  [7] "エルンスト・マイヤー"               "ギャヴィン・デ・ビーア"            
#  [9] "ジュリアン・ハクスリー"             "ジョージ・ロマネス"                
# [11] "ジョセフ・ダルトン・フッカー"       "ジョン・メイナード=スミス"        
# [13] "スティーヴン・ジェイ・グールド"     "チャールズ・ダーウィン"            
# [15] "トマス・ヘンリー・ハクスリー"       "フランシス・ゴルトン"              
# [17] "リチャード・オーウェン"             "リチャード・ドーキンス"            
# [19] "レイ・ランケスター"                 "ロナルド・フィッシャー"            
levels(FL)[L[head(order(colSums(D[cl$cluster == k, ]), decreasing = T), 20)]]
#  [1] "チャールズ・ダーウィン"       "Category:進化生物学者"       
#  [3] "イギリス"                     "王立協会"                    
#  [5] "進化生物学"                   "自然選択"                    
#  [7] "オックスフォード大学"         "ダーウィン・メダル"          
#  [9] "ダーウィン=ウォレス・メダル" "遺伝学"                      
# [11] "進化"                         "Category:イギリスの生物学者" 
# [13] "種の起源"                     "ケンブリッジ大学"            
# [15] "コプリ・メダル"               "自然選択説"                  
# [17] "生物学"                       "J・B・S・ホールデン"         
# [19] "ロナルド・フィッシャー"       "ロンドン"

生物学者のクラスタですが、生物学者はもっと多いはずです。特に上には日本人の生物学者がいません。なぜでしょうか。

記事の被参照状況をクラスタごとに可視化

特に歯学者(No. 2)、静岡県立大学(No. 7)、生物学者(No. 8)は独立性が高いと思われます。 つまり、他のクラスタに属する科学者からはあまりリンクされていない記事を多く 参照していると予想されます。 これをDocument-term Matrixの二次元的可視化によって視覚的に確認する方法はないでしょうか。 以下、試行錯誤の結果です。

Rのimage関数でDocument-term Matrix Dを表示することを考えます。 Dは0、1の値をとる行列ですので、単純に表示すると所々点々が見えるだけの グラフが表示されます。これでは何がなんだかわかりません。 そこで下記手順を考案しました。これでクラスタの特徴がつかみやすくなると思います。 (下記のかわりにheatmapを使う手もあるかもしれません。)

  • Dの行をクラスタインデックス(cl$cluster)によって並び替える。
  • そのままではDは大きすぎるので、注目しているクラスタが多く参照している列(リンク)のみを残し、 他の列を削除する。
  • 列を削減したDクラスタによって色分けしてimageで表示する。
k <- 2 # 注目するクラスタ番号
S <- D[order(cl$cluster), ] # S(Sorted): クラスタインデックスによるソート。
R <- S[, head(order(colSums(D[cl$cluster == k, ]), decreasing = T), 100)] # R(Reduced): クラスタkが多く参照するリンク以外削除。
R <- as.matrix(R) # image関数に渡すための前処理。
image(z = t(diag(cl$cluster[order(cl$cluster)]) %*% R), col = c("black", rainbow(8))) # 表示。

歯学者(No. 2)

歯学者が参照するリンクはやはり、このクラスタ固有のものが多いということが下記画像からはっきりわかります。 歯学関連リンクは帯状(図中黄色)に集まり、他のクラスタからの参照状況はまばらです。

f:id:shinaisan:20140921224237p:plain

静岡県立大学(No. 7)

このクラスタは少しわかりにくいですが、上部に紫色の帯として見えます。

f:id:shinaisan:20140921224259p:plain

生物学者(No. 8)

最上段の赤紫色の帯が見えます。

f:id:shinaisan:20140921224309p:plain

D3.jsによる可視化

D3.jsを使用したのは、やはりデータ可視化例を集めたギャラリーに触発されたのがきっかけではあります。 実際に使用してみると、なるほど多くの可視化事例が集まるのもうなずけるその強力さを実感できます。

拙作の可視化サイトでは科学者、リンクされた記事ともに円で表示しています。 参照関係はそれらを線で結ぶことで可視化しています。 力学的な動きによる各要素の自動配置をforceレイアウトによって実現しています。

forceレイアウト初期化

var force = d3.layout.force().sizel([width, height]);

レイアウト対象データ

forceレイアウトはnodeslinksという2種類のデータを設定できます。 簡単に言うとnodesが点で、linksは点たちを結ぶ線です。 どちらもオブジェクトの配列として準備します。 拙作サイトではこれらはサーバーから取得した JSONであり、科学者名などを含んでいます。

var nodes = オブジェクトの配列;
var links = source, target要素を含むオブジェクトの配列;
force.nodes(nodes).links(links);

linksの各要素はsourcetargetという2つのキーに対して nodesへのインデックスを設定します。 これでノード間の接続を表現します。

シミュレーション開始

forceにデータを設定し、力学シミュレーションを開始すると、 各データにx, y座標をはじめとしたシミュレーションの計算結果が格納されます。 これらを用いてtickハンドラ内で可視化要素(SVG要素)をtransform(translate) することでそれらを動かすことができます。 ノードの次数もweightとして得られるため、 例えばノードの次数に応じて円の半径を変え、 その半径に応じて斥力(charge)を大きくするといったことも容易にできます。

// ノードの次数に応じて対応するcircleの半径を変える。
d3.selectAll("circle").attr("r", function(d) {return 6 + d.weight;});
// ノードの次数に応じて斥力を設定。
force.charge(function(d) {return -200*d.weight;});
// tickハンドラ。
force.on("tick", function() {
    d3.selectAll(ノードのセレクタ).attr("transform", function(d) {
        // ここで一部のデータについてのみdの座標を強制設定することで
        // そのノードをアニメーションさせずに固定することが可能。
        if (その「一部のデータ」を選択する条件) {
            d.x = 適当な値;
            d.y = 適当な値;
        }
        return "translate(" + d.x + "," + d.y + ")";
    });
    d3.selectAll(リンクのセレクタ).attr("x1", function(d) {return d.source.x;})
        .attr("y1", function(d) {return d.source.y;})
        .attr("x2", function(d) {return d.target.x;})
        .attr("y2", function(d) {return d.target.y;});
});
// シミュレーション開始。
force.start();

ドラッグの挙動設定

forceレイアウト向けドラッグ&ドロップ時の挙動は下記のように簡単に設定できます。

var drag = force.drag();
d3.selectAll(ノードのセレクタ).call(drag);

拙作サイトではFreezeボタンを押すことで、forceレイアウトの力学シミュレーションを止め、 ドラッグ&ドロップ時の挙動を変更できます。 Freeze中はforce.drag()ではなく、d3.behavior.dragに変更することで、 力学シミュレーションを伴わないドラッグ&ドロップを実現しています。 この方法については下記stackoverflow記事が参考になりました。

反省

科学者数2001、属性数(ここではリンク数)6709のそれほど大きくはないデータセットが対象とはいえ、 やはり愚直にK-means法を適用しただけではいくつか独立性の高そうなクラスタが検出されたのみでした。 それ以外のクラスタは解釈の難しいものがほとんどでした。つまり、何のクラスタだかわからないということです。

実際には、今回のようにとりあえずアルゴリズムにかけてみてどうなるか見てみるというような場当たり的なことは普通しないと思われます。 科学者を例えば学問分野で分類したいというなら、参照するリンクを 学問のカテゴリを表すものに限定してみたり(これはDBpediaに投げるSPARQLを工夫することでできるのではと思います)、リンクに重み付けをしたり、いろいろやることはあるでしょう。

クラスタリング手法についても、階層的手法、ソフトクラスタリングといった分類は存在しますが、 個別のアルゴリズムとなるとあまりにも多く提案されているため、 適切なアルゴリズムの選択は非常に難しく、解析対象のデータセットそのものと合わせて相当に勉強が必要なようです。 結果の評価についても、多数の評価指標が考案されており、RのclusterCritパッケージに含まれているinternal criteriaだけでも40を超えます。それだけ多数考案されるほど難しい問題ということかもしれません。 本気でクラスタ解析しようとするとこれだけ多くの選択肢を比較考量する必要があるのでしょうか。 軽く目眩がしてきます。

とは言うものの、データ解析にこじつけた遊びのプログラミングとして非常に楽しかったのも事実で、 特に「歯学者」カテゴリの存在はこういう実験をするまで認識することすらなかっただろうなと思うと、 個人的には有益であったと感じます。 時間があれば他のアルゴリズムを試してみたいところです。