シェッフェの一対比較法(浦の変法)

自分の仕事でよく使うシェッフェの一対比較法(浦の変法)を、Rの勉強がてら実装してみました。佐藤信『統計的官能検査法』(日科技連出版社)に書かれている方法をそのままコードにしただけなので、詳しいことは同書を参照して下さい。

使用例は以下の通り。

numStim <- 3
numSubj <- 3
Y <- matrix(data=c(
## comparison result of pairs 1-2, 1-3, 2-1, 2-3, 3-1, and 3-2
              -2, -1,  3,  1,  0, -2,   # subject 1
              -2, -2,  1,  2, -1, -2,   # subject 2
              -3,  0,  3,  3,  1,  0),  # subject 3
            ncol=numSubj, byrow=FALSE)
scheffe.ura(Y, numStim, numSubj)

すると平均嗜好度・分散分析表・信頼区間がこんな感じで出てきます。

Average Preferences
---------------
a1 =   -0.9444
a2 =    1.3333
a3 =   -0.3889
---------------

ANOVA Table
--------------+---------------------------------------------
Source        |   SS        df   MS         F         p
--------------+---------------------------------------------
Main          |   50.7778    2   25.3889   27.2836    0.0003
Main x Indiv  |    1.2222    4    0.3056    0.3284    0.8515
Combi         |    0.0556    1    0.0556    0.0597    0.8131
Order         |    0.0556    1    0.0556    0.0597    0.8131
Order x Indiv |    5.4444    2    2.7222    2.9254    0.1113
Error         |    7.4444    8    0.9306
Total         |   65.0000   18
--------------+---------------------------------------------
                Y(0.05)=0.9188, Y(0.01)=1.2813

Confidence Interval
------------------+-----------------------+----------------------
                  |         95%CI         |         99%CI        
     ai - aj      +-----------+-----------+-----------+----------
                  |   +0.9188 |   -0.9188 |   +1.2813 |   -1.2813 
------------------+-----------+-----------+-----------+----------
a1-a2 =   -2.2778 |   -1.3590 |   -3.1966 |   -0.9965 |   -3.5591
a1-a3 =   -0.5556 |    0.3633 |   -1.4744 |    0.7258 |   -1.8369
a2-a3 =    1.7222 |    2.6410 |    0.8034 |    3.0035 |    0.4409
------------------+-----------+-----------+-----------+----------

コードは以下の通り。

この関数は単に計算結果を画面に表示するだけなので、計算結果を次の分析やグラフ作成に使おうと思うと面倒です。aov関数のように構造体のような値の返し方がしたいので、どうすればいいか調べてみないと。あと、cat(sprintf(...))のあたりとか、for文を使っているとか、プログラムも汚いですね……。

## Scheffe's Pairwise Comparison
## (Ura's Variation, considering stimulus order effect)
## 
## 2012-08-31 by MARUI Atsushi (marui@ms.geidai.ac.jp)
##
## Reference: Chapter 19, Satoh "Statistical Methods in Sensory Tests" Nikkagiren Publishing (1985)


scheffe.ura <- function(Y, numStim, numSubj) {
  ## data preparation (Stim1 x Stim2 x Subj)
  X <- array(data=0, dim=c(numStim, numStim, numSubj))
  k <- 1
  for (r in 1:numStim) {
    for (c in 1:numStim) {
      if (r != c) {
        X[r,c,] <- Y[k,]
        k <- k+1
      }
    }
  }
  
  ## calculate statistics
                                        # average preference
  Xi.. <- apply(X, 1, sum)
  X.i. <- apply(X, 2, sum)
  alphai <- (Xi.. - X.i.) / (2*numStim*numSubj)

                                        # individual differences
  Xi.k <- apply(X, c(1,3), sum)
  X.ik <- apply(X, c(2,3), sum)
  alphaik <- (Xi.k - X.ik) / (2*numStim) - alphai

                                        # combination effect
  Xij. <- apply(X, c(1,2), sum)
  Xji. <- t(apply(X, c(1,2), sum))
  gammaij <- (Xij. - Xji.) / (2*numSubj) - (matrix(alphai, nrow=length(alphai), ncol=length(alphai), byrow=FALSE) - matrix(alphai, nrow=length(alphai), ncol=length(alphai), byrow=TRUE))

                                        # average order effect
  delta <- sum(X) / (numStim*(numStim-1)*numSubj)

                                        # individual differences of order effect
  X..k <- apply(X, 3, sum)
  deltak <- X..k / (numStim*(numStim-1)) - delta

  ## calculate sum-of-squares
                                        # main effect S_\alpha
  S.alpha <- sum((Xi.. - X.i.)^2) / (2*numStim*numSubj)
  df.alpha <- numStim-1
                                        # main x subject S_{\alpha(B)}
  S.alphaB <- sum((Xi.k - X.ik)^2) / (2*numStim) - S.alpha
  df.alphaB <- (numStim-1)*(numSubj-1)
                                        # combination effect S_\gamma
  tmp <- (Xij. - Xji.)^2
  S.gamma <- sum(tmp[upper.tri(tmp, diag=TRUE)]) / (2*numSubj) - S.alpha
  df.gamma <- (numStim-1)*(numStim-2)/2
                                        # order effect S_\delta
  S.delta <- sum(X)^2 / (numSubj*numStim*(numStim-1))
  df.delta <- 1
                                        # order x subject S_{\delta(B)}
  S.deltaB <- sum(X..k^2) / (numStim*(numStim-1)) - S.delta
  df.deltaB <- numSubj-1
                                        # total S_T
  S.T <- sum(X^2)
  df.T <- numStim*(numStim-1)*numSubj
                                        # error S_e
  S.e <- S.T - S.alpha - S.alphaB - S.gamma - S.delta - S.deltaB
  df.e <- numStim*numStim*numSubj - numStim*numStim/2 - 2*numStim*numSubj + 3/2*numStim - 1

  ## average preferences
  cat(sprintf("\n"))
  cat(sprintf("Average Preferences\n"))
  cat(sprintf("---------------\n"))
  for (r in 1:numStim) {
    cat(sprintf("a%d = %9.4f\n", r, alphai[r]))
  }
  cat(sprintf("---------------\n"))
  
  ## ANOVA table
  cat(sprintf("\n"))
  cat(sprintf("ANOVA Table\n"))
  cat(sprintf("--------------+---------------------------------------------\n"))
  cat(sprintf("Source        |   SS        df   MS         F         p\n"))
  cat(sprintf("--------------+---------------------------------------------\n"))
  cat(sprintf("Main          | %9.4f %4d %9.4f %9.4f %9.4f\n", S.alpha, df.alpha, S.alpha/df.alpha, (S.alpha/df.alpha)/(S.e/df.e), pf((S.alpha/df.alpha)/(S.e/df.e), df.alpha, df.e, lower.tail=FALSE)))
  cat(sprintf("Main x Indiv  | %9.4f %4d %9.4f %9.4f %9.4f\n", S.alphaB, df.alphaB, S.alphaB/df.alphaB, (S.alphaB/df.alphaB)/(S.e/df.e), pf((S.alphaB/df.alphaB)/(S.e/df.e), df.alphaB, df.e, lower.tail=FALSE)))
  cat(sprintf("Combi         | %9.4f %4d %9.4f %9.4f %9.4f\n", S.gamma, df.gamma, S.gamma/df.gamma, (S.gamma/df.gamma)/(S.e/df.e), pf((S.gamma/df.gamma)/(S.e/df.e), df.gamma, df.e, lower.tail=FALSE)))
  cat(sprintf("Order         | %9.4f %4d %9.4f %9.4f %9.4f\n", S.delta, df.delta, S.delta/df.delta, (S.delta/df.delta)/(S.e/df.e), pf((S.delta/df.delta)/(S.e/df.e), df.delta, df.e, lower.tail=FALSE)))
  cat(sprintf("Order x Indiv | %9.4f %4d %9.4f %9.4f %9.4f\n", S.deltaB, df.deltaB, S.deltaB/df.deltaB, (S.deltaB/df.deltaB)/(S.e/df.e), pf((S.deltaB/df.deltaB)/(S.e/df.e), df.deltaB, df.e, lower.tail=FALSE)))
  cat(sprintf("Error         | %9.4f %4d %9.4f\n", S.e, df.e, S.e/df.e))
  cat(sprintf("Total         | %9.4f %4d\n", S.T, df.T))
  cat(sprintf("--------------+---------------------------------------------\n"))

  ## calculate yardsticks
  Y001 <- qtukey(1-0.01, numStim, df.e) * sqrt(S.e/df.e / (2*numSubj*numStim))
  Y005 <- qtukey(1-0.05, numStim, df.e) * sqrt(S.e/df.e / (2*numSubj*numStim))
  cat(sprintf("                Y(0.05)=%.4f, Y(0.01)=%.4f\n", Y005, Y001))
  cat(sprintf("\n"))

  ## confidence interval
  cat(sprintf("Confidence Interval\n"))
  cat(sprintf("------------------+-----------------------+----------------------\n"))
  cat(sprintf("                  |         95%%CI         |         99%%CI        \n"))
  cat(sprintf("     ai - aj      +-----------+-----------+-----------+----------\n"))
  cat(sprintf("                  | %+9.4f | %+9.4f | %+9.4f | %+9.4f \n", Y005, -Y005, Y001, -Y001))
  cat(sprintf("------------------+-----------+-----------+-----------+----------\n"))
  for (r in 1:(numStim-1)) {
    for (c in (r+1):numStim) {
      z <- alphai[r]-alphai[c]
      cat(sprintf("a%d-a%d = %9.4f | %9.4f | %9.4f | %9.4f | %9.4f\n", r, c, z, z+Y005, z-Y005, z+Y001, z-Y001))
    }
  }
  cat(sprintf("------------------+-----------+-----------+-----------+----------\n"))
  cat(sprintf("\n"))
}