前回の続き。
交互平均法で出てくるのは第一成分だけなので,複数の次元を出すために残差行列を計算してさらに交互平均法をする,という作業を繰り返す必要がある。
実装してみたコードがこちら。このコードの中に交互平均法のアルゴリズムも含まれています。
DualScaling <- function(dat,eps=1e-10,iter.max=1000){
nc <- ncol(dat)
nr <- nrow(dat)
cname <- colnames(dat)
rname <- rownames(dat)
mg.col <- colSums(dat)
mg.row <- rowSums(dat)
mg.t <- sum(mg.row)
dm <- min(nc,nr)-1
normed.col <- matrix(ncol=nc,nrow=dm)
projected.col <- matrix(ncol=nc,nrow=dm)
normed.row <- matrix(ncol=nr,nrow=dm)
projected.row <- matrix(ncol=nr,nrow=dm)
singular <- c()
dat.tmp <- dat
# 0-exp
for( i in 1:nrow(dat)){
for( j in 1:ncol(dat)){
dat.tmp[i,j] <- dat.tmp[i,j]- (mg.row[i]*mg.col[j]/mg.t)
}
}
# 1-n
for(i in 1:dm){
FLG <- FALSE
iter <- 0
u <- rnorm(nc)
tmp <- 0
while(FLG==FALSE){
#algorithm
v <- u %*% t(dat.tmp)
v <- v / mg.row
av <- (mg.row %*% t(v))/mg.t
v <- v - (av * rep(1,nr))
gy <- max(abs(v))
v <- v/gy
u <- v %*% dat.tmp
u <- u / mg.col
av <- (mg.col %*% t(u))/mg.t
u <- u - (av *rep(1,nc))
gx <- max(abs(u))
u <- u/gx
#conv. check
if(abs(tmp-gx)<eps){FLG <- TRUE}else{tmp <- gx}
if(iter>iter.max){FLG <- TRUE}else{iter <- iter + 1}
}
if(u[which.max(abs(u))]<0) u *-1
if(v[which.max(abs(v))]<0) v *-1
eta2 <- gx * gy # Correlation ratio
nu.c <- sqrt(sum(mg.col)/mg.col%*%t(u^2)) #multiplied constant
nv.c <- sqrt(sum(mg.row)/mg.row%*%t(v^2))
nu <- nu.c %*% u #normed u
nv <- nv.c %*% v #normed v
pu <- nu * sqrt(eta2)
pv <- nv * sqrt(eta2)
if(iter>iter.max){
cat("Warning::did not converge.")
}else{
singular[i] <- sqrt(eta2)
normed.col[i,] <- nu
normed.row[i,] <- nv
projected.col[i,] <- pu
projected.row[i,] <- pv
}
# residual Matrix
dat.tmp <- dat.tmp-(mg.row %*% t(mg.col)/mg.t)*((sqrt(eta2)*t(nv)%*%nu))
}
# Correct dims
delta_k <- singular^2 / sum(singular^2) * 100
cum_delta_k <- c()
cum_delta_k[1] <- delta_k[1]
for(i in 2:dm)
cum_delta_k[i] <- cum_delta_k[i-1]+delta_k[i]
dm.cr <- which.max(cum_delta_k)
if(dm.cr<dm){
singular <- singular[-(dm.cr+1:dm)]
delta_k <- delta_k[-(dm.cr+1:dm)]
cum_delta_k <- cum_delta_k[-(dm.cr+1:dm)]
normed.col <- normed.col[-(dm.cr+1:dm),]
normed.row <- normed.row[-(dm.cr+1:dm),]
projected.row <- projected.row[-(dm.cr+1:dm),]
projected.col <- projected.col[-(dm.cr+1:dm),]
}
# var names
colnames(normed.col) <- cname
colnames(projected.col) <- cname
colnames(normed.row) <- rname
colnames(projected.row) <- rname
return(list(ndims=dm.cr,sing=singular,eig=singular^2,deltaK=delta_k,cum_dk=cum_delta_k,nc=normed.col,nr=normed.row,pc=projected.col,pr=projected.row))
}
ちなみに,青木先生のサイトにも双対尺度法のコードがあって,こちらはRの固有値分解関数などをつかっているので,より安心安全にお使いいただけます。こっちのコードは交互平均法による習作ですから。
あと,西里先生の研究チームによる双対尺度法のRパッケージ,dualScaleというのもある。
dualScale: Dual Scaling Analysis of Multiple Choice Data
これは多肢選択法にdual scalingをするdsMC,強制分類法をするdsFCという二つの関数しかないけれども,どちらもとても魅力的な手法なので,パッケージで供給されているのはとてもありがたい。今後このパッケージがどんどん発展してくれるといいですね。
dsMCをつかって5件法でデータ取ってる研究全部やり直したいぐらいだよ,ほんとに。