Rの自己組織化マップ(SOM)で文字が重ならないようにする方法
Rでsom関数を使って自己組織化マップを作った後、各ユニットの座標に文字列をplotした場合、同じユニットに配置された文字列の重なりが気になることってありますよね。
下記は、放送大学の「データからの知識発見」という授業のテキストからの引用です。
入門者向けの講義なので、サンプルコードのシンプルさを優先したのか、文字の重なりについての対処はありません。用いられていた入力のデータ数(動物の数)が14、同じ場所に配置されてしまったデータが最大で2個なので、なんとか読み取れます。ライオンとトラが重なると「ライトオン」っぽくなるというのはトリビアですね。
ただ、データ数が多くなって、3つも4つも重なってくると、読み取れなくなって実害が出てくると思います。
「データマイニング入門」(わたせせいぞうの表紙のシリーズのやつ)では、そこに一工夫されていて、$visualで得られる座標をそのまま使うのではなく、乱数を加えることによって、文字が全く同じ位置で重なることを避けています↓
でも、そこはやはり乱数なので、うまく分離されているところもあれば、これじゃ読み取れないでしょってレベルのところもあります。
↓以下はデータマイニング入門に載っていたサンプルをそのまま動かしたものです。
カナリヤとスズメのところは書籍の図よりも見やすいようですが、キツネとタヌキのところは完全に重なってしまっていますね。乱数だからこんなこともあります。
で、私としては不確実なものに頼るよりは、座標が重なった(=同じユニットに配置された)なら、定量だけ座標をずらしてやればいいじゃんと思ったわけです。
上記の考えにしたがって作ったサンプルコードと実行例の図が↓以下です。
grpには、x座標とy座標を組にした因子でグループ分けされたリストが入ります。表示させると↓こんな感じです。
> grp
$`7:0`
[1] 5 13
$`3:1`
[1] 18
$`6:1`
[1] 15
・・・(略)・・・
(7, 0)に配置されたのは5番目と13番目のデータの2つ、(3, 1)に配置されたのは18番目のデータの1つ、・・・という具合です。
for文の中で、それぞれのグループに対し、1つ目のものはそのまま、2つ目のものには0.2を足す、3つ目のものには0.4を足す、という具合にy軸の表示位置を下にずらしていき、重なりを避けています。
0.2という数値は試行錯誤した結果で、cex=0.6と合わせて微調整してください。データ数が多いと、隣のユニットの領域にまではみ出すことになるので、その場合はpngやpdfに描き出すようにして、描画領域を大きく設定し、cexを小さくするようなやり方で対処できると思います。
このやり方で、理屈の上では、一切重なりのない自己組織化マップがRで描けるということになります。お試しあれ。
データからの知識発見 (放送大学教材)
データマイニング入門
下記は、放送大学の「データからの知識発見」という授業のテキストからの引用です。
重なり対処を全く行っていない表示例 |
入門者向けの講義なので、サンプルコードのシンプルさを優先したのか、文字の重なりについての対処はありません。用いられていた入力のデータ数(動物の数)が14、同じ場所に配置されてしまったデータが最大で2個なので、なんとか読み取れます。ライオンとトラが重なると「ライトオン」っぽくなるというのはトリビアですね。
ただ、データ数が多くなって、3つも4つも重なってくると、読み取れなくなって実害が出てくると思います。
「データマイニング入門」(わたせせいぞうの表紙のシリーズのやつ)では、そこに一工夫されていて、$visualで得られる座標をそのまま使うのではなく、乱数を加えることによって、文字が全く同じ位置で重なることを避けています↓
乱数による重なり対処を行った表示例 |
でも、そこはやはり乱数なので、うまく分離されているところもあれば、これじゃ読み取れないでしょってレベルのところもあります。
↓以下はデータマイニング入門に載っていたサンプルをそのまま動かしたものです。
乱数による重なり対処を行った表示例(私の環境で実行させたもの) |
library(som) 動物データ1 <- read.csv("animal1.csv", header=T) 標準化動物データ <- normalize(動物データ1[,2:14], byrow=F) 動物SOM <- som(標準化動物データ, xdim=10, ydim=10, topol="rect") 乱数 <- cbind(rnorm(nrow(動物データ1), 0, 0.15), rnorm(nrow(動物データ1), 0, 0.15)) 動物マップ <- 動物SOM$visual[, 1:2] + 乱数 + 0.5 plot(動物マップ, xlim=c(0,10), ylim=c(0,10)) text(動物マップ[, 1], 動物マップ[, 2], 動物データ1$動物名)
カナリヤとスズメのところは書籍の図よりも見やすいようですが、キツネとタヌキのところは完全に重なってしまっていますね。乱数だからこんなこともあります。
で、私としては不確実なものに頼るよりは、座標が重なった(=同じユニットに配置された)なら、定量だけ座標をずらしてやればいいじゃんと思ったわけです。
上記の考えにしたがって作ったサンプルコードと実行例の図が↓以下です。
定量だけずらすことによる重なり対処を行った表示例 |
# 同じユニットに配置されたものがあれば、定量だけずらすことにより # 重なりを避ける vis <- 動物SOM$visual # x座標とy座標の組を因子(ファクタ)として、グループに分ける grp <- split(1:nrow(vis), vis[,1:2], sep=":", drop=T ) for (i in 1:length(grp)){ # 同じ場所に配置されたものの添え字のベクトル idx <- grp[[i]] # 位置ずらし用の数 0.0, +0.2, +0.4, ... を足す vis[idx, ]$y <- vis[idx, ]$y + seq(0.0, 10, 0.2)[1:length(idx)] } plot(vis$x, vis$y, type="n", xlim=c(0,10), ylim=c(0,10)) text(vis$x, vis$y, 動物データ1$動物名, cex=0.6)
grpには、x座標とy座標を組にした因子でグループ分けされたリストが入ります。表示させると↓こんな感じです。
> grp
$`7:0`
[1] 5 13
$`3:1`
[1] 18
$`6:1`
[1] 15
・・・(略)・・・
(7, 0)に配置されたのは5番目と13番目のデータの2つ、(3, 1)に配置されたのは18番目のデータの1つ、・・・という具合です。
for文の中で、それぞれのグループに対し、1つ目のものはそのまま、2つ目のものには0.2を足す、3つ目のものには0.4を足す、という具合にy軸の表示位置を下にずらしていき、重なりを避けています。
0.2という数値は試行錯誤した結果で、cex=0.6と合わせて微調整してください。データ数が多いと、隣のユニットの領域にまではみ出すことになるので、その場合はpngやpdfに描き出すようにして、描画領域を大きく設定し、cexを小さくするようなやり方で対処できると思います。
このやり方で、理屈の上では、一切重なりのない自己組織化マップがRで描けるということになります。お試しあれ。
データからの知識発見 (放送大学教材)
データマイニング入門
コメント
コメントを投稿