因子分析 Last modified: Aug 31, 2009
目的
因子分析を行う。
R には factanal という関数が用意されている。最尤法により因子を求めプロマックス回転するならそれを利用すべきである。
factanal を使いやすくする関数(factanal2)を作ったので参照されたい。
使用法
pfa(dat)
pfa(dat, method=c("Varimax", "biQuartimax", "Quartimax", "Equimax", "None"), eps1=1e-5, eps2=1e-5, max1=999, max2=999, factors=0)
print(result, before=FALSE)
plot(result, before=FALSE, fac.no=1:2, scores=FALSE, xlab=NULL, ylab=NULL, axis=TRUE, label.cex=0.7, ...)
引数
dat データ行列(行がケース,列が変数)。欠損値 NA を含むケース(行)は分析に先だって自動的に削除される。
または,相関係数行列(正方行列であること)
method 因子軸の回転方法の指定(使用法参照 回転しないときは None)
eps1 共通性の反復推定の終了判定条件
eps2 因子軸の回転の終了判定条件
max1 共通性の反復推定の回数の上限
max2 因子軸の回転の回数の上限
factors 求める因子の数(指定しない場合には,固有値が 1 以上の数になる)
result pfa が返すオブジェクト
before 因子軸の回転前の結果を使うか回転後の結果を使うかを指定
fac.no 横軸と縦軸にプロットする因子番号
scores 因子得点を描くか因子負荷量を描くかの指定
xlab 横軸の名前
ylab 縦軸の名前
axis 座標軸を描くかどうかの指定
label.cex 描画点につけるラベルの文字の大きさ
... plot 関数に引き渡す引数
ソース
インストールは,以下の 1 行をコピーし,R コンソールにペーストする
source("http://aoki2.si.gunma-u.ac.jp/R/src/pfa.R", encoding="euc-jp")
# 因子分析
pfa <- function(dat, # データ行列または相関係数行列
method=c("Varimax", "biQuartimax", "Quartimax", "Equimax", "None"), # 回転法
eps1=1e-5, # 共通性の収束限界
eps2=1e-5, # バリマックス回転の収束限界
max1=999, # 共通性の推定のための収束計算の上限回数
max2=999, # バリマックス回転を行う上限回数
factors=0)
{
method <- match.arg(method) # 回転法を補完
dat <- subset(dat, complete.cases(dat)) # 欠損値を持つケースを除く
nr <- nrow(dat) # ケース数
nc <- ncol(dat) # 変数の個数
if (is.null(colnames(dat))) { # 変数名が無いときには仮の名前を付ける
colnames(dat) <- paste("Var", 1:nc, sep=".")
}
vnames <- colnames(dat) # 変数名を記録
if (nr != nc && is.null(rownames(dat))) { # ケース名がないときには仮の名前を付ける
rownames(dat) <- paste("Obs", 1:nr, sep=".")
}
cnames <- rownames(dat) # ケース名を記録
r0 <- r <- if (nr == nc) dat else cor(dat) # 与えられたのがデータ行列なら,相関係数行列を計算する
communality0 <- 1-1/diag(solve(r)) # 共通性の初期値(SMC)
diag(r) <- communality0 # 相関係数行列の対角成分を共通性で置き換える
result <- eigen(r) # 固有値・固有ベクトルを求める
eval <- result$values # 固有値
evec <- result$vectors # 固有ベクトル
if (factors == 0) { # 因子数の指定がないときには,
factors <- sum(eval >= 1) # 1 以上の固有値の数とする
}
converged <- FALSE # 共通性の収束計算を行う
for (i in 1:max1) { # 上限回数まで繰り返す
eval <- eval[1:factors] # 必要な因子数まで,固有値を取る
evec <- evec[,1:factors] # 必要な因子数まで
evec <- t(sqrt(eval)*t(evec)) # 因子負荷量を計算
r <- r0 # 相関係数行列を復帰
communality <- rowSums(evec^2) # 共通性を計算
if (all(abs(communality-communality0) < eps1)) { # 共通性の変化が限界以内になれば,
converged <- TRUE # 収束したとして,
break # ループを抜ける
}
communality0 <- communality # 現在の共通性を保存
diag(r) <- communality # 相関係数行列の対角成分を置き換える
result <- eigen(r) # 固有値・固有ベクトルを求める
eval <- result$values # 固有値
evec <- result$vectors # 固有ベクトル
}
if (converged == FALSE) { # 収束フラッグが FALSE なら,
warning("Not converged.") # 収束しなかったと警告する
}
else if (any(communality >= 1)) { # 1 を超える共通性があったら,
warning("Communality >= 1.") # 警告する
}
if (factors == 1 || method == "None") { # 因子数が 1 であるか,因子軸の回転をしないのなら,
w <- solve(r0)%*%evec # 因子得点係数を計算する
scores <- (scale(dat)*sqrt(nr/(nr-1)))%*%w # 因子得点を計算する
rownames(evec) <- names(communality) <- vnames # 名前を付けて,結果を返す
rownames(scores) <- cnames
colnames(scores) <- colnames(evec) <- names(eval) <- paste("FAC", 1:factors, sep=".")
return(structure(list(rotation=method, correlation.matrix=r0, communality=communality,
before.rotation.fl=evec, before.rotation.eval=eval, scores=scores), class="pfa"))
}
else { # 因子軸を回転するなら,
fl <- evec/sqrt(communality) # 因子負荷量を共通性で基準化
eig <- numeric(factors)
ov <- 0
wt <- switch (method, # 回転方法によって,重みを変える
"Varimax" = 1,
"biQuartimax" = 0.5,
"Quartimax" = 0,
"Equimax" = nc/2)
fnp <- nc
for (loop in 1:max2) { # 回転の上限回数まで収束計算
for (k in 1:(factors-1)) {
for (k1 in (k+1):factors) {
x <- fl[,k]
y <- fl[,k1]
xy <- x^2-y^2
a <- sum(xy)
b <- 2*sum(x*y)
c <- sum(xy^2-4*x^2*y^2)
d <- 4*sum(x*y*xy)
dd <- d-2*wt*a*b/fnp
theta <- atan(dd/(c-wt*(a^2-b^2)/fnp))
if(sin(theta)*dd < 0) {
if (theta > 0) {
theta <- theta-pi
}
else {
theta <- theta+pi
}
}
theta <- theta/4
cs <- cos(theta)
si <- sin(theta)
fljk <- fl[,k]
fljk1 <- fl[,k1]
fl[,k] <- fljk*cs+fljk1*si
fl[,k1] <- fljk1*cs-fljk*si
}
}
v <- sum((t(fl)^2-colSums(fl^2)*wt/fnp)^2)
if (abs(v-ov) < eps2) { # 収束したら,
break # ループから抜ける
}
ov <- v
}
fl <- fl*sqrt(communality) # 因子負荷量
w <- solve(r0)%*%fl # 因子得点係数
scores <- (scale(dat)*sqrt(nr/(nr-1)))%*%w # 因子得点
eval2 <- colSums(fl^2) # 因子負荷量の二乗和
rownames(evec) <- rownames(fl) <- names(communality) <- vnames
rownames(scores) <- cnames
colnames(scores) <- colnames(evec) <- colnames(fl) <- names(eval) <- names(eval2) <- paste("FAC", 1:factors, sep=".")
return(structure(list(rotation=method, correlation.matrix=r0, communality=communality,
before.rotation.fl=evec, before.rotation.eval=eval,
after.rotation.fl=fl, after.rotation.eval=eval2, scores=scores), class="pfa"))
}
}
# print メソッド
print.pfa <- function( result, # pfa が返すオブジェクト
before=FALSE)
{
communality <- result$communality
vnames <- sapply(names(communality), function(i) substring(i, 1, min(nchar(i), 7)))
if (before || is.null(result$after.rotation.fl)) {
fl <- result$before.rotation.fl
eval <- result$before.rotation.eval
label <- "E-value"
if (result$rotation == "None") {
printf("\nResult without rotation\n\n")
}
else {
printf("\nBefore %s rotation\n\n", result$rotation)
}
}
else {
fl <- result$after.rotation.fl
eval <- result$after.rotation.eval
label <- "Sq.Sum"
printf("\nAfter %s rotation\n\n", result$rotation)
}
nv <- nrow(fl)
nc <- ncol(fl)
cat(" ", sprintf(" Fac.%02i", 1:nc), " Communality\n", sep="")
for (i in 1:nv) {
cat(sprintf("%-7s", vnames[i]), sprintf("%7.3f", fl[i,]), sprintf("%7.3f\n", communality[i]), sep="")
}
cat(sprintf("%-7s", label), sprintf("%7.3f", eval), "\n", sep="")
cat(sprintf("%-7s", "Cont."), sprintf("%7.1f", eval/nv*100), "\n", sep="")
cat(sprintf("%-7s", "Cum."), sprintf("%7.1f", cumsum(eval/nv*100)), "\n", sep="")
}
# plot メソッド
plot.pfa <- function( result, # pfa が返すオブジェクト
before=FALSE, # 因子軸の回転前の結果を使うか回転後の結果を使うかを指定
fac.no=1:2, # 横軸と縦軸にプロットする因子番号
scores=FALSE, # 因子得点を描くか因子負荷量を描くかの指定
xlab=NULL, ylab=NULL, # 横軸,縦軸の名前
axis=TRUE, # 座標軸を描くかどうかの指定
label.cex=0.7, # 描画点につけるラベルの文字の大きさ
...) # plot 関数に引き渡す引数
{
fac.name <- names(result$before.rotation.eval)
if (length(fac.name) > 1) {
ax1 <- fac.no[1]
ax2 <- fac.no[2]
if (is.null(xlab)) {
xlab <- fac.name[ax1]
}
if (is.null(ylab)) {
ylab <- fac.name[ax2]
}
if (scores) {
x <- result$scores[, ax1]
y <- result$scores[, ax2]
labels <- 1:length(x)
}
else {
if (before || is.na(result$after.rotation.fl)) {
fl <- result$before.rotation.fl
}
else {
fl <- result$after.rotation.fl
}
x <- fl[, ax1]
y <- fl[, ax2]
labels <- names(result$communality)
}
plot(x, y, xlab=xlab, ylab=ylab, ...)
old <- par(xpd=TRUE)
text(x, y, labels, cex=label.cex, pos=1)
par(old)
}
if (axis) {
abline(h=0, v=0)
}
}
使用例 1
> dat <- read.table("pfa.data", header=TRUE) # ファイルからデータを読んだ
> dat
var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
1 935 955 926 585 1010 925 1028 807 769 767
2 817 905 901 632 1004 950 957 844 781 738
3 768 825 859 662 893 900 981 759 868 732
4 869 915 856 448 867 874 884 802 804 857
5 787 878 880 592 871 874 884 781 782 807
6 738 848 850 569 814 950 957 700 870 764
7 763 862 839 658 887 900 1005 604 709 753
8 795 890 841 670 853 874 859 701 680 772
9 903 877 919 460 818 849 884 700 718 716
10 761 765 881 485 846 900 981 728 781 714
11 747 792 800 564 796 849 932 682 746 767
12 771 802 840 609 824 874 859 668 704 710
13 785 878 805 527 911 680 884 728 709 747
14 657 773 820 612 810 849 909 698 746 771
15 696 785 791 578 774 725 932 765 706 795
16 724 785 870 509 746 849 807 763 724 760
17 712 829 838 516 875 725 807 754 762 585
18 756 863 815 474 873 725 957 624 655 620
19 622 759 786 619 820 769 807 673 698 695
20 668 753 751 551 834 849 807 601 655 642
> ( result <- pfa(dat, factors=3) ) # 因子分析を実行
After Varimax rotation
Fac.01 Fac.02 Fac.03 Communality
var1 -0.818 0.381 -0.254 0.878
var2 -0.867 0.226 -0.054 0.806
var3 -0.581 0.619 -0.133 0.738
var4 0.041 0.070 0.730 0.539
var5 -0.834 0.026 0.315 0.796
var6 -0.187 0.631 0.371 0.571
var7 -0.412 0.339 0.317 0.385
var8 -0.349 0.560 -0.149 0.457
var9 -0.118 0.734 0.125 0.568
var10 -0.086 0.562 0.055 0.326
Sq.Sum 2.803 2.265 0.997
Cont. 28.0 22.7 10.0
Cum. 28.0 50.7 60.7
その他の結果は,str(result) のようにして要素を確認後,
個別の要素を print 関数によって出力する
> print(result$correlation, digits=3) # 相関係数行列
var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
var1 1.000 0.8449 0.775 -0.2257 0.5586 0.375 0.429 0.4447 0.2466 0.3362
var2 0.845 1.0000 0.614 -0.0212 0.7167 0.239 0.390 0.4467 0.2244 0.3136
var3 0.775 0.6141 1.000 -0.1006 0.4470 0.563 0.403 0.5982 0.4815 0.2651
var4 -0.226 -0.0212 -0.101 1.0000 0.2136 0.331 0.189 -0.0544 0.0718 0.1688
var5 0.559 0.7167 0.447 0.2136 1.0000 0.249 0.483 0.3976 0.2189 -0.0407
var6 0.375 0.2391 0.563 0.3311 0.2486 1.000 0.419 0.1989 0.5319 0.3893
var7 0.429 0.3897 0.403 0.1888 0.4827 0.419 1.000 0.1419 0.4076 0.2742
var8 0.445 0.4467 0.598 -0.0544 0.3976 0.199 0.142 1.0000 0.5689 0.4473
var9 0.247 0.2244 0.481 0.0718 0.2189 0.532 0.408 0.5689 1.0000 0.3638
var10 0.336 0.3136 0.265 0.1688 -0.0407 0.389 0.274 0.4473 0.3638 1.0000
あるいは,print.default(result) のようにすればすべての結果を出力することができる。
> print.default(result)
$rotation # 因子軸の回転法
[1] "Varimax"
$correlation.matrix # 相関係数行列
var1 var2 var3 var4 var5 var6 var7 var8
var1 1.0000000 0.84487442 0.7746348 -0.22572126 0.5585596 0.3746964 0.4288587 0.44474253
var2 0.8448744 1.00000000 0.6140948 -0.02116982 0.7166837 0.2391217 0.3897472 0.44666435
var3 0.7746348 0.61409482 1.0000000 -0.10056276 0.4470492 0.5628616 0.4030052 0.59819255
var4 -0.2257213 -0.02116982 -0.1005628 1.00000000 0.2136123 0.3311048 0.1888090 -0.05438703
var5 0.5585596 0.71668368 0.4470492 0.21361227 1.0000000 0.2485824 0.4826623 0.39756234
var6 0.3746964 0.23912166 0.5628616 0.33110479 0.2485824 1.0000000 0.4191012 0.19885885
var7 0.4288587 0.38974717 0.4030052 0.18880904 0.4826623 0.4191012 1.0000000 0.14187410
var8 0.4447425 0.44666435 0.5981926 -0.05438703 0.3975623 0.1988589 0.1418741 1.00000000
var9 0.2466372 0.22442056 0.4814589 0.07176501 0.2189180 0.5319358 0.4075537 0.56891499
var10 0.3362334 0.31363051 0.2651037 0.16878738 -0.0407357 0.3893330 0.2741899 0.44734094
var9 var10
var1 0.24663719 0.3362334
var2 0.22442056 0.3136305
var3 0.48145888 0.2651037
var4 0.07176501 0.1687874
var5 0.21891798 -0.0407357
var6 0.53193583 0.3893330
var7 0.40755369 0.2741899
var8 0.56891499 0.4473409
var9 1.00000000 0.3637659
var10 0.36376593 1.0000000
$communality # 共通性
var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
0.8782869 0.8058379 0.7376875 0.5392022 0.7962812 0.5710556 0.3852000 0.4574828 0.5682011 0.3258559
$before.rotation.fl # 回転前の因子負荷量
FAC.1 FAC.2 FAC.3
var1 -0.85127134 -0.38093708 -0.09225476
var2 -0.80339537 -0.36668776 0.16104011
var3 -0.83136537 -0.04526261 -0.21088020
var4 -0.06449186 0.52620168 0.50808939
var5 -0.67637810 -0.22526754 0.53670139
var6 -0.57352146 0.49150559 0.02347232
var7 -0.55425633 0.17470446 0.21789501
var8 -0.61545313 0.03446450 -0.27841064
var9 -0.56939070 0.42882823 -0.24515650
var10 -0.42989384 0.30434024 -0.22005500
$before.rotation.eval # 回転前の固有値
FAC.1 FAC.2 FAC.3
4.0468459 1.1590592 0.8591861
$after.rotation.fl # 回転後の因子負荷量
FAC.1 FAC.2 FAC.3
var1 -0.81752240 0.38107025 -0.25441993
var2 -0.86716928 0.22560900 -0.05436886
var3 -0.58096539 0.61850727 -0.13272345
var4 0.04083323 0.06991167 0.72982686
var5 -0.83442587 0.02649545 0.31513908
var6 -0.18699075 0.63098561 0.37141241
var7 -0.41170256 0.33927289 0.31716695
var8 -0.34888652 0.56003956 -0.14871696
var9 -0.11753949 0.73398757 0.12509133
var10 -0.08607321 0.56160143 0.05523714
$after.rotation.eval # 回転後の因子負荷量の二乗和
FAC.1 FAC.2 FAC.3
2.8031906 2.2652820 0.9966186
$scores # 因子得点
FAC.1 FAC.2 FAC.3
1 -2.35863255 0.63067260 0.329220617
2 -1.27981713 0.58243865 1.174903000
3 -0.06492078 1.18107012 0.889716371
4 -0.77918746 0.87233382 -1.166728853
5 -0.34036193 0.73957325 0.183002359
6 0.74062447 1.37316866 0.447702634
7 -0.46817258 -0.47855965 1.607620790
8 -0.30749799 -0.17331089 0.370604867
9 -0.83009214 0.44493091 -1.941757717
10 0.41762185 0.65611503 -0.170693247
11 0.79272411 0.15871956 -0.164168839
12 0.22027197 -0.14173994 -0.006421007
13 -1.08753778 -1.29541749 -0.451412084
14 1.16049740 0.05318377 0.933139700
15 1.31738669 -0.05099253 -0.583232459
16 1.33358487 0.69466121 -1.145936980
17 0.03587380 -0.72694479 -0.881790129
18 -0.43093588 -1.74621258 -0.621098893
19 1.07368915 -1.08529807 0.731121280
20 0.85488189 -1.68839164 0.466208591
attr(,"class")
[1] "pfa"
plot メソッド
> plot(result, axis=FALSE, pch=19, main="因子負荷量") # 因子負荷量のプロット
> plot(a, pch=19, scores=TRUE) # 因子得点のプロット
解説ページ
直前のページへ戻る
E-mail to Shigenobu AOKI