データサイエンティスト上がりのDX参謀・起業家

データサイエンティスト上がりのDX参謀・起業家のブログ。データ分析や事業について。自身はアーティスト、経営者、事業家。

Self Organizing Map(SOM)の可視化

自己組織化マップ(SOM)はクラスタリングに使える!ということで、Rで試してみました。Rではsomパッケージやkohonenパッケージで絵の出力もしてくれますが、ちょっと図が綺麗ではないです。

そこで今回はggplot2で綺麗っぽく可視化してみました。データはirisを使います。プログラム→結果の順で進めます。



まずはいつものkohonenプロット。

library(kohonen)
library(ggplot2)

Dim     <- 10				#SOMの次元
UseData <- do.call(cbind, iris[, -5])	#使用するデータ


GRID      <- somgrid(xdim = Dim, ydim = Dim, topo = "hexa")
DataSom   <- som(UseData, grid = GRID)

plot(DataSom, type = "mapping", labels = 1:nrow(UseData))


この結果にはirisデータの花弁とかの情報はまったく出ておらず、ただどのサンプルが"近いか"という情報だけを表現しています。

通常の階層型クラスタリングは樹形図でサンプルの近さを示しているのに対して、SOMは2次元のマップで表現しているということです。



次に、この図に花弁の情報を色で付与します。

#---kohonen.plot
i <- 1				#チェックしたい変数の番号
plot(DataSom, type = "mapping", labels = 1:nrow(UseData), 
	bgcol = cm.colors(nrow(DataSom$codes))[rank(DataSom$codes[, i])])

cm.colorsのようなカラーパレットを使ってグラデーションを付けることで、花弁の情報を色で表現できます。

bgcolの中にあるrank(DataSom$codes[, i])で色の濃さを指定してます。

このiは変数の番号を示します。

DataSom$codesには各ノードのSOMでの推定値が格納されているので、これのランクで色分けしているのです。


図をみると、sepal.lengthで2群に分かれるんだろうな〜って事が示唆されます。



そして最後に、この図をもう少し見やすくするためにggplot2を使って描き直します。

DataHex       <- data.frame(DataSom$codes)
DataHexStd    <- data.frame(scale(DataHex))
DataHexStd$x  <- rep(1:Dim, Dim)
DataHexStd$y  <- rep(1:Dim, rep(Dim, Dim))

#---グラフ
HighCol <- "#950000"
LowCol  <- "#3500BF"



#---textの追加
DataSomMap   <- data.frame(Data = 1:nrow(UseData), 
		x = DataSom$unit.classif - trunc*1 + 
	scale_colour_gradient2(high = HighCol, low = LowCol) + 
	geom_point(size = 160 / Dim, shape = 15) + 
	opts(legend.position = "none", title = names(DataHexStd)[i])
q + geom_text(data = DataSomMap, aes(x, y, label = Data, col = 0), size = 3)


どうでしょうか!?

きれいになってませんか!?



表現しているものはkohonenのプロットと同じです。それぞれのセグメントがノードを示して、色の濃さがノードの推定値です。赤が高い、青が低いです。

また、DataSom$unit.classifに各サンプルのSOMでのクラス番号が振られているので、これを使ってxとyの番号を作ってます。


このプログラムは基本的に変更しなくても、コピペで使えます(まぁどこかエラーが出る可能性はないことはないですがww)。

グラフを描くためにSOMを計算し直すのが面倒なので、関数にはしていませんが、最初のUseDataとかiとかを変えればいろいろ応用できます。

ggplotのsomだけど描くために、コピペ用のプログラムを下に書いておきます。


library(kohonen)
library(ggplot2)

#---ここのDimとUseDataを必要に応じて変更する
Dim     <- 10
UseData <- do.call(cbind, iris[, -5])
UseData <- scale(UseData)


#------SOMの実行
GRID      <- somgrid(xdim = Dim, ydim = Dim, topo = "hexa")
DataSom   <- som(UseData, grid = GRID)

DataHex       <- data.frame(DataSom$codes)
DataHexStd    <- data.frame(scale(DataHex))
DataHexStd$x  <- rep(1:Dim, Dim)
DataHexStd$y  <- rep(1:Dim, rep(Dim, Dim))

#---------グラフ
HighCol <- "#950000"
LowCol  <- "#3500BF"


#------textの追加
DataSomMap   <- data.frame(Data = 1:nrow(UseData), 
		x = DataSom$unit.classif - trunc*2 + 
	scale_colour_gradient2(high = LowCol, low = HighCol) + 
	geom_point(size = 190 / Dim, shape = 15) + 
	opts(legend.position = "none", title = names(DataHexStd)[i])



このプログラムで、どのノードにどのサンプルが入っているかチェックできます。

#---check map
xMap <- 5
yMap <- 1

DataSomMap[(xMap - 0.5) <= DataSomMap$x & DataSomMap$x <= (xMap + 0.5) & 
	   (yMap - 0.5) <= DataSomMap$y & DataSomMap$y <= (yMap + 0.5), ]
mean(UseData[, i])

また、RapidMinerでもSOMができます。以前少し記事を書いていたので紹介です。


http://d.hatena.ne.jp/isseing333/20100604/1275636361


この結果は、等高線でサンプルの近さを表しているようです。サンプル自体の色は実際の観測値。だから、2次元上の距離が同じに見えても、色が違うところ同士は実際には遠いクラスターになってるって事です。あと、SOMは英語では「ソム」って発音するようです。

*1:DataSom$unit.classif - 1)/Dim) * Dim, y = trunc((DataSom$unit.classif - 1)/Dim) + 1) DataSomMap$x <- DataSomMap$x + runif(nrow(UseData), -0.5, 0.5) DataSomMap$y <- DataSomMap$y + runif(nrow(UseData), -0.5, 0.5) DataSomMap <- cbind(DataSomMap, UseData) #---ggplot2 q <- ggplot(DataHexStd, aes(x, y, col = DataHexStd[, i]

*2:DataSom$unit.classif - 1)/Dim) * Dim, y = trunc((DataSom$unit.classif - 1)/Dim) + 1) DataSomMap$x <- DataSomMap$x + runif(nrow(UseData), -0.5, 0.5) DataSomMap$y <- DataSomMap$y + runif(nrow(UseData), -0.5, 0.5) DataSomMap <- cbind(DataSomMap, UseData) #------ggplot2 i <-1 ggplot(DataHexStd, aes(x, y, col = DataHexStd[, i]