必要なパッケージを読み込む。
::p_load(tidyverse,
pacman
patchwork,
GGally, clustrd)
<- tibble(
df1 x1 = c(1, 1, 2, 3, 6, 7, 7, 9, 3, 3),
x2 = c(4, 8, 7, 8, 4, 2, 3, 4, 2, 1)
)
散布図を作る。
<- ggplot(df1, aes(x = x1, y = x2)) +
p geom_point()
plot(p)
df1
に含まれる観測点を、\(k=3\)の\(K\)平均法で3つのクラスタに分けてみよう。
(1)
まず、ランダムに3点を選び、それらを別のクラスに分類する。
set.seed(2021-11-06)
<- df1 %>%
df2 mutate(cluster = sample(c(1:3, rep(NA, n() - 3)), size = n()),
cluster = factor(cluster))
選んだ3点を表示する。
<- p +
p1 geom_point(data = filter(df2, !is.na(cluster)),
aes(x = x1, y = x2, color = cluster),
shape = 4, size = 4) +
theme(legend.position = "none")
plot(p1)
3点までの距離を測り、最も近いグループに分類する。今回は、ユークリッド距離を利用する。 平面上でユークリッド距離を測る関数を作る。
<- function(x, y, a, b) {
dist_euc # (x, y) と (a, b) のユークリッド距離
sqrt((x - a)^2 + (y - b)^2)
}
クラスタ1〜3の座標を取り出す。
# クラスタ1
<- df2 %>% filter(cluster == 1) %>% pull(x1)
x1_c1 <- df2 %>% filter(cluster == 1) %>% pull(x2)
x2_c1
# クラスタ2
<- df2 %>% filter(cluster == 2) %>% pull(x1)
x1_c2 <- df2 %>% filter(cluster == 2) %>% pull(x2)
x2_c2
# クラスタ3
<- df2 %>% filter(cluster == 3) %>% pull(x1)
x1_c3 <- df2 %>% filter(cluster == 3) %>% pull(x2) x2_c3
各点とそれぞれのクラスタの「中心」との距離を測る。
<- df2 %>%
df2 mutate(dist_1 = dist_euc(x1, x2, x1_c1, x2_c1),
dist_2 = dist_euc(x1, x2, x1_c2, x2_c2),
dist_3 = dist_euc(x1, x2, x1_c3, x2_c3))
各点を、最も距離が短いクラスに分類する。
<- df2 %>%
df2 mutate(cluster = case_when(
<= dist_2 & dist_1 < dist_3 ~ 1,
dist_1 < dist_1 & dist_2 <= dist_3 ~ 2,
dist_2 TRUE ~ 3))
途中経過を可視化する。
<- ggplot(df2, aes(x = x1, y = x2,
p2 color = as.factor(cluster),
shape = as.factor(cluster))) +
geom_point() +
theme(legend.position = "none")
plot(p2)
(2)
ここまでの分類結果から、各クラスタの重心の座標を求める。
# クラスタ1
<- df2 %>%
x1_c1 filter(cluster == 1) %>%
pull(x1) %>%
mean()
<- df2 %>%
x2_c1 filter(cluster == 1) %>%
pull(x2) %>%
mean()
# クラスタ2
<- df2 %>%
x1_c2 filter(cluster == 2) %>%
pull(x1) %>%
mean()
<- df2 %>%
x2_c2 filter(cluster == 2) %>%
pull(x2) %>%
mean()
# クラスタ3
<- df2 %>%
x1_c3 filter(cluster == 3) %>%
pull(x1) %>%
mean()
<- df2 %>%
x2_c3 filter(cluster == 3) %>%
pull(x2) %>%
mean()
## tibble にまとめる
<- tibble(
df2_center x1 = c(x1_c1, x1_c2, x1_c3),
x2 = c(x2_c1, x2_c2, x2_c3),
cluster = factor(1:3))
各クラスタの重心を図に加える。
<- p2 +
p2_c geom_point(data = df2_center,
aes(x = x1, y = x2,
color = cluster,
shape = cluster),
shape = 4, size = 4)
plot(p2_c)
各点とそれぞれのクラスタの重心との距離を測る。
<- df2 %>%
df3 mutate(dist_1 = dist_euc(x1, x2, x1_c1, x2_c1),
dist_2 = dist_euc(x1, x2, x1_c2, x2_c2),
dist_3 = dist_euc(x1, x2, x1_c3, x2_c3))
各点を、最も距離が短いクラスに分類する。
<- df3 %>%
df3 mutate(cluster = case_when(
<= dist_2 & dist_1 < dist_3 ~ 1,
dist_1 < dist_1 & dist_2 <= dist_3 ~ 2,
dist_2 TRUE ~ 3))
途中経過を可視化する。
<- ggplot(df3, aes(x = x1, y = x2,
p3 color = as.factor(cluster),
shape = as.factor(cluster))) +
geom_point() +
theme(legend.position = "none")
plot(p3)
(3)
ここまでの分類結果から、各クラスタの重心の座標を求める。
# クラスタ1
<- df3 %>%
x1_c1 filter(cluster == 1) %>%
pull(x1) %>%
mean()
<- df3 %>%
x2_c1 filter(cluster == 1) %>%
pull(x2) %>%
mean()
# クラスタ2
<- df3 %>%
x1_c2 filter(cluster == 2) %>%
pull(x1) %>%
mean()
<- df3 %>%
x2_c2 filter(cluster == 2) %>%
pull(x2) %>%
mean()
# クラスタ3
<- df3 %>%
x1_c3 filter(cluster == 3) %>%
pull(x1) %>%
mean()
<- df3 %>%
x2_c3 filter(cluster == 3) %>%
pull(x2) %>%
mean()
## tibble にまとめる
<- tibble(
df3_center x1 = c(x1_c1, x1_c2, x1_c3),
x2 = c(x2_c1, x2_c2, x2_c3),
cluster = factor(1:3))
各クラスタの重心を図に加える。
<- p3 +
p3_c geom_point(data = df3_center,
aes(x = x1,
y = x2,
color = cluster,
shape = cluster),
shape = 4, size = 4)
plot(p3_c)
各点とそれぞれのクラスタの重心との距離を測る。
<- df3 %>%
df4 mutate(dist_1 = dist_euc(x1, x2, x1_c1, x2_c1),
dist_2 = dist_euc(x1, x2, x1_c2, x2_c2),
dist_3 = dist_euc(x1, x2, x1_c3, x2_c3))
各点を、最も距離が短いクラスに分類する。
<- df4 %>%
df4 mutate(cluster = case_when(
<= dist_2 & dist_1 < dist_3 ~ 1,
dist_1 < dist_1 & dist_2 <= dist_3 ~ 2,
dist_2 TRUE ~ 3))
途中経過を可視化する。
<- ggplot(df4, aes(x = x1,
p4 y = x2,
color = as.factor(cluster),
shape = as.factor(cluster))) +
geom_point() +
theme(legend.position = "none")
plot(p4)
(4)
ここまでの分類結果から、各クラスタの重心の座標を求める。
# クラスタ1
<- df4 %>%
x1_c1 filter(cluster == 1) %>%
pull(x1) %>%
mean()
<- df4 %>%
x2_c1 filter(cluster == 1) %>%
pull(x2) %>%
mean()
# クラスタ2
<- df4 %>%
x1_c2 filter(cluster == 2) %>%
pull(x1) %>%
mean()
<- df4 %>%
x2_c2 filter(cluster == 2) %>%
pull(x2) %>%
mean()
# クラスタ3
<- df4 %>%
x1_c3 filter(cluster == 3) %>%
pull(x1) %>%
mean()
<- df4 %>%
x2_c3 filter(cluster == 3) %>%
pull(x2) %>%
mean()
## tibble にまとめる
<- tibble(
df4_center x1 = c(x1_c1, x1_c2, x1_c3),
x2 = c(x2_c1, x2_c2, x2_c3),
cluster = factor(1:3))
各クラスタの重心を図に加える。
<- p4 +
p4_c geom_point(data = df4_center,
aes(x = x1,
y = x2,
color = cluster,
shape = cluster),
shape = 4, size = 4)
plot(p4_c)
各点とそれぞれのクラスタの重心との距離を測る。
<- df4 %>%
df5 mutate(dist_1 = dist_euc(x1, x2, x1_c1, x2_c1),
dist_2 = dist_euc(x1, x2, x1_c2, x2_c2),
dist_3 = dist_euc(x1, x2, x1_c3, x2_c3))
各点を、最も距離が短いクラスに分類する。
<- df5 %>%
df5 mutate(cluster = case_when(
<= dist_2 & dist_1 < dist_3 ~ 1,
dist_1 < dist_1 & dist_2 <= dist_3 ~ 2,
dist_2 TRUE ~ 3))
途中経過を可視化する。
<- ggplot(df5, aes(x = x1,
p5 y = x2,
color = as.factor(cluster),
shape = as.factor(cluster))) +
geom_point() +
theme(legend.position = "none")
plot(p5)
p4
と p5
を比べてみよう。
plot(p4 | p5)
グルーピングに変化がないことがわかる。よって、ここでクラスタリングを終了する。
clustrdパッケージに含まれる macro
データを使って実習を行う。
data(macro)
glimpse(macro)
## Rows: 20
## Columns: 6
## $ GDP <dbl> 4.8, 3.2, 3.9, 2.3, 3.6, 4.1, 4.1, 2.9, 3.2, 2.3, 2.8, 1.1, 1.4, 1…
## $ LI <dbl> 8.4, 2.5, -1.0, 0.7, 2.5, 1.1, 1.4, 1.6, 0.6, 5.6, -7.5, 0.6, -0.1…
## $ UR <dbl> 8.1, 8.4, 11.8, 11.7, 19.0, 8.9, 4.5, 4.2, 10.3, 3.2, 4.9, 4.7, 9.…
## $ IR <dbl> 5.32, 5.02, 3.60, 3.69, 4.83, 4.20, 5.59, 3.69, 11.70, 20.99, 4.84…
## $ TB <dbl> 0.7, 1.6, 8.8, 3.9, 1.2, 7.0, -1.4, 7.0, -8.3, 0.0, -8.7, -0.6, 4.…
## $ NNS <dbl> 4.7, 5.2, 7.7, 7.3, 9.6, 4.0, 7.0, 15.8, 8.0, 12.7, 14.0, 9.4, 12.…
このデータセットは、GDPをはじめとする6つの経済指標を20か国について記録している。データに含まれる国は、row.names()
で確認できる。
row.names(macro)
## [1] "Australia" "Canada" "Finland" "France" "Spain"
## [6] "Sweden" "USA" "Netherlands" "Greece" "Mexico"
## [11] "Portugal" "Austria" "Belgium" "Denmark" "Germany"
## [16] "Italy" "Japan" "Norway" "Switzerland" "UK"
階層的クラスタリング (hierarchical clustering) はhclust()
で実行できる。 method
引数で、グループ間の距離の測り方を指定する。選択肢は、
"single"
:単リンク法(最短距離法)"complete"
:完全リンク法(最長距離法)"average"
:群平均法"centroid"
:重心法"ward.D2"
:ウォード法である。
まず、単リンク法でクラスタリングを実行してみよう。hclust()
の第1引数は、dist()
の結果として出力される非類似度行列(dissimilarity matrix)なので、dist()
の結果を保存してから実行しよう。
<- dist(macro)
macro_dist <- hclust(macro_dist, method = "single") c_single
この結果を plot()
すると、樹形図(dendrogram, デンドログラム)が表示される。
plot(c_single)
各ステップでのグループ間の距離は、樹形図の枝の高さで示されている。 この高さは、次のようにして確認できる。
$height c_single
## [1] 2.158333 3.195638 4.375214 4.389032 4.426839 4.505830 5.050980
## [8] 5.138959 5.206726 5.444089 5.458470 5.490902 5.591386 5.934172
## [15] 6.516134 8.491148 11.001459 12.502400 15.850050
この結果を元に、クラスタの数を\(k\)に指定するにはcutree()
を使う。\(k=3\)にしてみよう。
cutree(c_single, k = 3)
## Australia Canada Finland France Spain Sweden
## 1 1 1 1 1 1
## USA Netherlands Greece Mexico Portugal Austria
## 1 1 1 2 3 1
## Belgium Denmark Germany Italy Japan Norway
## 1 1 1 1 1 1
## Switzerland UK
## 1 1
メキシコとポルトガルがそれぞれ単独でグループを作り、残りの18か国が1つのグループに分けられれた。
次に、完全リンク法でクラスタリングを行ってみよう。
<- hclust(macro_dist, method = "complete") c_complete
樹形図を示す。
plot(c_complete)
\(k=3\)でクラスタに分ける。
cutree(c_complete, k = 3)
## Australia Canada Finland France Spain Sweden
## 1 1 1 1 1 1
## USA Netherlands Greece Mexico Portugal Austria
## 1 1 2 3 2 1
## Belgium Denmark Germany Italy Japan Norway
## 1 1 1 1 1 1
## Switzerland UK
## 1 1
メキシコは相変わらず単独でクラスタを構成しているが、ポルトガルはギリシャと同じクラスタになった。残りの17か国は同じクラスタに分類されている。
実習課題
\(K\)平均法は、kmeans()
で実行できる。クラスタの数は centers
引数で指定する。 \(K=3\)で分類してみよう。
set.seed(2021-11-07)
<- kmeans(macro, centers = 3) c_k3
結果を確認しよう。
$cluster c_k3
## Australia Canada Finland France Spain Sweden
## 2 2 2 2 2 2
## USA Netherlands Greece Mexico Portugal Austria
## 3 1 3 3 3 3
## Belgium Denmark Germany Italy Japan Norway
## 2 2 2 2 1 1
## Switzerland UK
## 1 3
階層的クラスタリングとは異なる結果が得られている。
kmeans()
関数は、最初のステップにおけるグループの中心をランダムに選ぶ。 nstart
引数を1より大きい値に設定すると、複数の初期値をランダムに選び、最もよい結果のみを返す。 試しに、nstart = 50
にしてみよう。
<- kmeans(macro, centers = 3, nstart = 50) c_k3b
結果を確認しよう。
$cluster c_k3b
## Australia Canada Finland France Spain Sweden
## 2 2 2 2 2 2
## USA Netherlands Greece Mexico Portugal Austria
## 2 3 1 1 1 2
## Belgium Denmark Germany Italy Japan Norway
## 3 2 2 2 3 3
## Switzerland UK
## 3 2
先ほどとは異なる結果が得られた。ただし、グループのラベルの付け方が異なる。(注意:日本やノルウェーなどを含むグループのラベルが1から3に変わっているが、ラベルの変更は結果の違いとは言えない。)
各グループのばらつき具合は、クラスタ内総平方和で測られる。先程の結果と比べてみよう。
rbind(c_k3$tot.withinss,
$tot.withinss) c_k3b
## [,1]
## [1,] 895.9534
## [2,] 863.4262
クラスタ内総平方和が小さくなっており、先程よりも各グループが似た者同士の集まりになっていると考えられる。
結果を可視化するために、元のデータにクラスタリングの結果を加えた tibble を作る。
<- macro %>%
df_k3b mutate(cluster = factor(c_k3b$cluster))
GGallyパッケージの ggpairs()
で散布図行列を作る。
ggpairs(df_k3b,
columns = 1:6,
aes(color = cluster),
legend = 1) +
theme(legend.position = "bottom")
、