2値変数だけのデータにk-meansをすると詰む
ブログ執筆から逃げるな
まだgitには上げてません。
さっき上げました♨
教師なし学習で大変に代表的な手法であるところのk-means(K平均法)ですが、
2値変数にたいして適用すると悲しいことが起きるので注意が必要です。
地味な設計
以下のようなデータを用意します。x1
から x3
までは、パラメータ $p$ をそれっぽく変えて、
rbinom
から生成した2値データ、 x4
のみ、平均をそれっぽく変えた正規分布に従う乱数を生成したデータです。
x2
と x3
は、それぞれ x1
の値に依存して反応確率が変わります*1。
library(ggplot2) library(gridExtra) set.seed(1234) library(ggplot2) library(gridExtra) set.seed(1234) x11 <- rbinom(30, 1, 0.5) x12 <- rbinom(20, 1, 0.75) x13 <- rbinom(50, 1, 0.2) x21 <- ifelse(x11==1, rbinom(30, 1, 0.7), rbinom(30, 1, 0.1)) x22 <- ifelse(x12==1, rbinom(20, 1, 0.8), rbinom(20, 1, 0.05)) x23 <- ifelse(x13==1, rbinom(50, 1, 0.75), rbinom(50, 1, 0.25)) x31 <- ifelse(x11==1, rbinom(30, 1, 0.9), rbinom(30, 1, 0.1)) x32 <- ifelse(x12==1, rbinom(20, 1, 0.6), rbinom(20, 1, 0.04)) x33 <- ifelse(x13==1, rbinom(50, 1, 0.59), rbinom(50, 1, 0.01)) # 検証のために連続データもいれとこ。 x41 <- rnorm(30, 0, 1) x42 <- rnorm(20, 5, 1) x43 <- rnorm(50, -3, 1) kmeans_data <- data.frame( id = paste0("id_", c(1:100)), x1 = c(x11, x12, x13), x2 = c(x21, x22, x23), x3 = c(x31, x32, x33), x4 = c(x41, x42, x43) )
コードをみて分かるとおり、3つのクラスタに分かれてほしい気持ちがあふれています。
Spearmanの相関係数(離散に強いタイプ)も、以下の感じで、 x1
~ x3
の間の相関は相対的に強めです。
x1 x2 x3 x4 x1 1.0000000 0.7112844 0.6130185 0.4874672 x2 0.7112844 1.0000000 0.5072037 0.4667618 x3 0.6130185 0.5072037 1.0000000 0.4976532 x4 0.4874672 0.4667618 0.4976532 1.0000000
さらに言えば、ID昇順で分かれてくれると嬉しいですね。
まずは x1
から x3
だけを使ってk-meansを実施してみます。
kmeans
関数がある
Rであれば stats::kmeans()
でやってくれる。最高では?
k_means <- stats::kmeans(kmeans_data[,-c(1,5)], centers = 3)
結果はグラフでみてみましょう。 IDを横軸、各変数を縦軸に置いた時、ID方向に綺麗に色が分かれれば成功ですね。
ダメそうです。ちなみに連続値 x4
があるといけます。
k_means <- stats::kmeans(kmeans_data[,-1], centers = 3)
なんで?
単純な話で、2値変数同士の散布図は、(0,0), (0,1), (1,0), (1,1)にしか点が置かれないので、
k平均法で重要な「重心」の計算が進まず、初期値から収束しないまま計算が終わってしまうからですね。それはそう。
でも周りだと意外とこれをやらかす人がいるので、世の中って怖いですよね。
ちなみに
「バイナリ距離で階層クラスタリングしたら割と行けちゃったりするのでは?」と思った
d_mat <- dist(kmeans_data[,-c(1,5)], method = "binary") h_cluster <- hclust(d_mat, method = "average")
なんかデンドログラムはイケてるようにみえたんですが、やっぱりダメそう。
結論
2値データでクラスタリングをすると悲しいらしいね。
とはいえググると2値変数から上手いこと重心を計算してkmeansする方法などのレビューや
モデルベースでのクラスタリングなどがあるようで、研究は相応にされている模様。
上手い方法があれば教えてください。おわり。