library(conflicted) # 関数名の衝突を警告
conflicts_prefer( # 優先的に使う関数を指定
dplyr::filter(),
dplyr::select(),
dplyr::lag(),
)
library(tidyverse)
library(GGally)
library(broom) # 解析結果を tibble 形式に集約
library(gt) # 表の作成
library(gtsummary) # 分析結果の表の作成
library(ggfortify) # バイプロット表示のため
library(ggrepel)
#' macOSのための日本語表示の設定
if(Sys.info()["sysname"] == "Darwin") { # macOSか調べる
jp_font <- "HiraMaruProN-W4"
theme_update(text = element_text(family = jp_font))
update_geom_defaults("text", list(family = theme_get()$text$family))
update_geom_defaults("label", list(family = theme_get()$text$family))
update_geom_defaults("text_repel", list(family = theme_get()$text$family))
update_geom_defaults("label_repel", list(family = theme_get()$text$family))
} else {jp_font <- NULL}第7講 主成分分析
評価と視覚化
準備
以下で利用する共通パッケージを読み込む.
主成分分析に用いる主な関数
分析に用いる主な関数としては以下がある.
stats::prcomp(): 主成分分析の実行base::summary(): 主成分負荷・寄与率を表示broom::tidy(): 主成分得点・負荷・寄与率を tibble クラスで取得broom::augment(): tibble クラスで主成分得点を計算ggfortify::autoplot(): 主成分得点による散布図およびバイプロット
#' データフレーム 'toy_data' を分析
toy_pca <-
toy_data |>
select(計算対象の列) |> # 必要な列を選択するか不要な列を削除
prcomp(必要なら標準化) # 'scale.=TRUE' で標準化
#' 分析結果の確認
summary(toy_pca) # 主成分負荷量と寄与率を確認
toy_pca |>
tidy("eigenvalues") # "scores","loadings","eigenvalues" で指定(別表記もあり)
#' 第1,2主成分得点で散布図を描く
toy_pca |>
autoplot(scale = 0, x = 1, y = 2) jpamenity.csv を用いた例
人口関連データを整理する.
ja_data <-
bind_cols(
## 都道府県名と地方名の取得
read_csv(file = "data/prefecture.csv",
col_select = c(2,4)) |>
set_names("都道府県名", "地方区分"), # 列の名称を"都道府県名"と"地方区分"に変更
## データの取得
read_csv(file = "data/jpamenity.csv",
col_select = !1:2) |> # 不要な列を読み込まない
slice(-1) |> # 不要な行を削除
set_names(names(read_csv(file = "data/jpamenityitem.csv"))) # 簡略化した項目名に変更
) |>
select(1:10) |> # 人口関連のみ利用
mutate(地方区分 = as_factor(地方区分)) # 地方区分を出現順に因子化散布図により変数間の関係を確認する.
ja_data |>
ggpairs(columns = 3:10, # 都道府県名・地方区分を除く
legend = c(2,1), # 2行1列のグラフから凡例を作成
upper = list(continuous = wrap("cor", size = 3.5)),
lower = list(continuous = wrap("points", alpha = .5),
mapping = aes(colour = 地方区分))) +
theme(text = element_text(size = 8)) 主成分分析を行い,バイプロット(主成分得点の散布図と主成分方向の表示)を描画する.
ja_pca <- ja_data |>
select(!1:2) |> # 人口関連データのみ
prcomp(scale. = TRUE) # 主成分分析の実行
ja_pca |>
autoplot(asp = 1, # 縦横比を設定
data = ja_data,
colour = "地方区分", # 地方ごとに色付け
label = TRUE, # ラベルの表示
label.label = "都道府県名",
label.repel = TRUE, # ラベルの表示を自動調整
label.size = 3,
loadings = TRUE,
loadings.colour = "orchid", # 負荷の表示
loadings.label = TRUE,
loadings.label.colour = "darkgray", # 負荷ラベルの表示
loadings.label.size = 4) データセット
以下では2つのデータセットを使用する
japan_social2.csv の概要
総務省統計局より取得した都道府県別の社会生活統計指標の一部
japan_social.csv を日本語化したもの
- 都道府県名 :
- 地方区分 :
- 森林面積割合: 森林面積割合 (%) 2014年
- 農業産出額 : 就業者1人当たり農業産出額(販売農家)(万円) 2014年
- 人口割合 : 全国総人口に占める人口割合 (%) 2015年
- 土地生産性 : 土地生産性(耕地面積1ヘクタール当たり)(万円) 2014年
- 商品販売額 : 商業年間商品販売額[卸売業+小売業](事業所当たり)(百万円) 2013年
- 参考 : https://www.e-stat.go.jp/SG1/estat/List.do?bid=000001083999&cycode=0
データの読み込み方の例.
js_data <-
read_csv("data/japan_social2.csv",
col_types = list(地方区分 = col_factor())) # 地方区分を因子化列の type を指定したい場所が判っていて,先頭の方であれば以下でも可.
#' 1列目は自動判定(?),2列目は因子(f),3列目以降は自動判定
js_data <-
read_csv("data/japan_social2.csv", col_types = "?f")MASS::UScereal の概要
Nutritional and Marketing Information on US Cereals
The UScereal data frame has 65 rows and 11 columns. The data come from the 1993 ASA Statistical Graphics Exposition, and are taken from the mandatory F&DA food label. The data have been normalized here to a portion of one American cup.
詳細は ?MASS::UScereal を参照
データの整理の仕方の例. 元のデータは data.frame クラスなので,tibble クラスに変換して整理しておく.
glimpse(MASS::UScereal) # 各変数の属性を確認する.
uc_data <-
MASS::UScereal |>
rownames_to_column(var = "product") |> # 行名の製品名を product 列に加える
as_tibble() # tibble クラスに変換寄与率・累積寄与率
問題
データセット
japan_social.csvMASS::UScereal
について,標準化の有無の違いで寄与率・累積寄与率がどのように異なるか確認しなさい.
toy_data |> prcomp() # 標準化を行わない場合
toy_data |> prcomp(scale. = TRUE) # 標準化を行う場合正式なオプション名は scale. であるが,sc = TRUE など他のオプションと区別できれば短縮表記も可能.
解答例
MASS::UScereal の場合
データを整理する.また,分析を行う前に適当な方法で視覚化をすることを推奨する.
uc_data <-
MASS::UScereal |>
rownames_to_column(var = "product") |>
as_tibble()
uc_data |>
ggpairs(columns = which(sapply(uc_data,is.double)), # 数値列
legend = c(2,1),
upper = list(continuous = wrap("cor", size = 3.5)),
lower = list(continuous = wrap("points", alpha = .5),
mapping = aes(colour = mfr)))標準化なしの分析は以下のようになる.
uc_pca_raw <-
uc_data |>
select(where(is.double)) |>
prcomp()
uc_pca_raw |>
tidy("eigenvalues") |>
gt() |>
fmt_number(columns = !1, decimals = 3)| PC | std.dev | percent | cumulative |
|---|---|---|---|
| 1 | 203.185 | 0.770 | 0.770 |
| 2 | 98.693 | 0.182 | 0.952 |
| 3 | 50.362 | 0.047 | 0.999 |
| 4 | 6.824 | 0.001 | 1.000 |
| 5 | 2.372 | 0.000 | 1.000 |
| 6 | 1.490 | 0.000 | 1.000 |
| 7 | 0.947 | 0.000 | 1.000 |
| 8 | 0.550 | 0.000 | 1.000 |
uc_pca_raw |>
tidy("eigenvalues") |>
pivot_longer(!PC) |>
mutate(name = as_factor(name)) |>
ggplot(aes(x = PC, y = value, fill = name)) +
geom_bar(stat="identity") +
facet_wrap(~ name, scales = "free_y") +
theme(legend.position = "none") uc_pca_raw |>
tidy("loadings") |>
pivot_wider(names_from = PC,
names_prefix = "PC",
values_from = value) |>
gt() |>
fmt_number(decimals = 3) | column | PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7 | PC8 |
|---|---|---|---|---|---|---|---|---|
| calories | −0.179 | −0.146 | −0.965 | 0.045 | −0.093 | −0.012 | −0.004 | 0.070 |
| protein | −0.011 | 0.003 | −0.015 | −0.072 | −0.170 | −0.069 | 0.905 | −0.376 |
| fat | −0.003 | −0.001 | −0.015 | 0.077 | −0.396 | 0.371 | −0.354 | −0.758 |
| sodium | −0.493 | −0.842 | 0.220 | 0.008 | −0.002 | −0.001 | 0.000 | −0.001 |
| fibre | −0.027 | 0.020 | 0.010 | −0.076 | −0.187 | −0.919 | −0.218 | −0.257 |
| carbo | −0.015 | −0.027 | −0.110 | −0.693 | 0.621 | 0.042 | −0.077 | −0.336 |
| sugars | −0.009 | −0.001 | −0.046 | 0.707 | 0.620 | −0.104 | 0.032 | −0.318 |
| potassium | −0.851 | 0.519 | 0.078 | −0.006 | 0.013 | 0.033 | −0.002 | 0.011 |
uc_pca_raw |>
tidy("loadings") |>
mutate(column = as_factor(column)) |>
ggplot(aes(x = PC, y = value, fill = column)) +
geom_bar(stat = "identity", width = 0.7, position = position_dodge(width = 0.7))uc_pca_raw |>
autoplot(asp =1,
scale = 0,
data = uc_data,
colour = "mfr", # メーカー毎に色付け
label = TRUE,
label.label = "product", # 製品名のラベル
label.repel = TRUE) + # 重なりを低減
labs(colour = "company")標準化ありの分析は以下のようになる.
uc_pca <-
uc_data |>
select(where(is.double)) |>
prcomp(scale. = TRUE)
uc_pca |>
tidy("eigenvalues") |>
gt() |>
fmt_number(columns = !1, decimals = 3)| PC | std.dev | percent | cumulative |
|---|---|---|---|
| 1 | 2.059 | 0.530 | 0.530 |
| 2 | 1.159 | 0.168 | 0.698 |
| 3 | 1.085 | 0.147 | 0.845 |
| 4 | 0.779 | 0.076 | 0.921 |
| 5 | 0.700 | 0.061 | 0.983 |
| 6 | 0.322 | 0.013 | 0.995 |
| 7 | 0.171 | 0.004 | 0.999 |
| 8 | 0.084 | 0.001 | 1.000 |
uc_pca |>
tidy("eigenvalues") |>
pivot_longer(!PC) |>
mutate(name = as_factor(name)) |>
ggplot(aes(x = PC, y = value, fill = name)) +
geom_bar(stat="identity") +
facet_wrap(~ name, scales = "free_y") +
theme(legend.position = "none") uc_pca |>
tidy("loadings") |>
pivot_wider(names_from = PC,
names_prefix = "PC",
values_from = value) |>
gt() |>
fmt_number(decimals = 3) | column | PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7 | PC8 |
|---|---|---|---|---|---|---|---|---|
| calories | −0.410 | −0.401 | 0.196 | 0.007 | −0.203 | 0.053 | −0.350 | 0.683 |
| protein | −0.448 | 0.176 | 0.072 | −0.195 | −0.128 | −0.830 | 0.032 | −0.142 |
| fat | −0.268 | −0.444 | −0.270 | −0.602 | 0.497 | 0.123 | 0.051 | −0.173 |
| sodium | −0.347 | 0.068 | 0.120 | 0.617 | 0.691 | −0.037 | −0.035 | −0.019 |
| fibre | −0.383 | 0.467 | −0.198 | −0.097 | −0.137 | 0.356 | −0.604 | −0.276 |
| carbo | −0.288 | −0.205 | 0.686 | −0.003 | −0.226 | 0.295 | 0.235 | −0.459 |
| sugars | −0.194 | −0.463 | −0.552 | 0.456 | −0.370 | −0.037 | 0.074 | −0.304 |
| potassium | −0.415 | 0.365 | −0.233 | −0.045 | −0.105 | 0.278 | 0.668 | 0.322 |
uc_pca |>
tidy("loadings") |>
mutate(column = as_factor(column)) |>
ggplot(aes(x = PC, y = value, fill = column)) +
geom_bar(stat = "identity", width = 0.7, position = position_dodge(width = 0.7))uc_pca |>
autoplot(asp = 1,
scale = 0,
data = uc_data,
colour = "mfr",
label = TRUE,
label.label = "product",
label.repel = TRUE) +
labs(colour = "company")主成分分析の視覚化
主成分分析の分析結果を可視化するバイプロット表示は, 関数 ggfortify::autoplot() にオプションを指定することで実行できる.
#' データフレームを分析する
toy_pca <-
toy_data |>
select(計算対象の列を指定) |>
prcomp(必要なら標準化)
#' 第1と第2主成分を利用したバイプロット表示
toy_pca |>
autoplot(label = TRUE, # 各データのラベルを表示
loadings = TRUE, # 主成分負荷による成分方向の表示
loadings.label = TRUE) # 成分名の表示
#' 第2と第3主成分を利用した散布図
toy_pca |>
autoplot(loadings = TRUE,
x = 2, y = 3) # 主成分の指定
#' パラメタ s を変更 (既定値は1)
toy_pca |>
autoplot(scale = 0, # パラメタ s の指定
loadings = TRUE)base R には関数 biplot() が用意されており, 同様の描画を行うことができる.
#' 第1と第2主成分を利用した散布図
biplot(toy_pca)
#' 第2と第3主成分を利用した散布図
biplot(toy_pca, choices = c(2,3))
#' パラメタ s を変更 (既定値は1)
biplot(toy_pca, scale=0)問題
データセット
japan_social.csvMASS::UScereal
の主成分分析の結果を利用してバイプロットによる可視化を行いなさい.
- 標準化したデータでの主成分分析を行いなさい
- 第1主成分と第2主成分でのバイプロットを描きなさい
- 第2主成分と第3主成分でのバイプロットを描きなさい
解答欄
解答例
MASS::UScereal の場合
uc_pca |>
autoplot(data = uc_data,
asp = 1,
colour = "mfr", # メーカー毎に色付け
shape = 19,
size = 1,
label = TRUE,
label.label = "product",
label.repel = TRUE,
loadings = TRUE,
loadings.colour = "orange",
loadings.label = TRUE,
loadings.label.repel = TRUE,
loadings.label.colour = "brown",
loadings.label.size = 4) +
labs(colour = "company")uc_pca |>
autoplot(x = 2, y = 3, # 第2 vs 第3
data = uc_data,
asp = 1,
colour = "mfr",
shape = 19,
size = 1,
label = TRUE,
label.label = "product",
label.repel = TRUE,
loadings = TRUE,
loadings.colour = "orange",
loadings.label = TRUE,
loadings.label.repel = TRUE,
loadings.label.colour = "brown",
loadings.label.size = 4) +
labs(colour = "company")第1,2主成分得点で散布図を描き,上と比較してみる.
uc_pca |>
augment(data = uc_data) |>
ggplot(aes(x = .fittedPC1, y = .fittedPC2,
label = product, colour = mfr)) +
geom_point(shape = 19, size = 1) +
geom_text_repel(size = 3, max.overlaps = 40) +
theme(aspect.ratio=1) # 縦横比を1配置は変わらないが,座標軸が異なることがわかる. 関数 autoplot() において scale=0 とするとデータの座標は主成分得点となる.
options(ggrepel.max.overlaps = 40)
uc_pca |>
autoplot(scale = 0,
asp = 1,
data = uc_data,
colour = "mfr",
shape = 19,
size = 1,
label = TRUE,
label.label = "product",
label.repel = TRUE,
label.size = 3,
loadings = TRUE,
loadings.colour = "orange",
loadings.label = TRUE,
loadings.label.repel = TRUE,
loadings.label.colour = "brown",
loadings.label.size = 4) +
theme(legend.position = "none") # 凡例を表示しない