標準化した分割表



先日、Staged approachデータをJoint analysisする件についての記事を書いた(こちら)。その中で、Staged approachでの各ステージでのサンプルサイズは、ケース・コントロールで同数であるものとして統計量z_iを求め、その値の関係を論じた(引用文献に準拠)。そのとき、ケース・コントロールのサンプルサイズが異なる場合に生じる注意点は、Staged approachの問題というよりも、分割表検定そのものに由来することだという1文を書いた。

今日の記事はそれに関する記事である。記載を検証するエクセルはこちら

それに用いたJavaソースは記事の末尾

  • 分割表における検定標準化1(普通の標準化)
    • 2x2セルをa,b,c,d、その周辺分布をe,f,g,h、総検体数をnとする
    • ある要因の母比率を2群(ケース・コントロール)で比較している
    • ケースの人数とコントロールの人数の総和を同じにしたときは、ケースの人数とコントロールの人数が等しいときにもっとも検出力が高くなる
    • これを逆に言うと、次のようになる
      • ケースの人数とコントロールの人数が異なるときには、総人数を減らして、ケースの人数とコントロールの人数を等しくて同様の結果を得ることができる、と
    • では、ケースの人数e,コントロールの人数f,総人数nのときは、ケース・コントロール同数(e'=f'=nH=n'/2)にすると、e',f',nH,n'はどのような値になるだろうか
    • その値は、nH=調和平均(e,f)である
      • ただし、調和平均は、2ef/(e+f)で与えられる
  • この標準化の妥当性
    • こちらのエクセルの2系列(青系と緑系)のテーブルは、オリジナルデータの分割表とその期待度数、カイ自乗計算結果である
    • 観測データに入れる値として、ケース・コントロールで大きく差がない値を入れると、青系の検定結果と緑系のそれとに大差はない
  • この標準化の妥当性の根拠
    • 両系のad-bcとその値をnで割った値を分割表の横に示した。nで割った値は一致していることがわかる。ad-bcはデータぶれの程度を表すスカラー量であるので、この値をnで割った値が等しいということは、青系と緑系の分割表が代表している偏りの程度が同程度であることを示している
    • これは、ad/bcがこの2x2分割表のオッズ比であることを思い出せば、2x2分割表は、オッズ比が同じで、総サンプル数が同じとき、ケース・コントロール人数比、因子の有り無し人数比によらず、同程度のP値を与える、ということと同じことである
  • 標準化1’
    • 2x2分割表は縦と横について対称なので、ケース・コントロール軸で標準化してもカイ自乗値が変わらないのなら、要因の軸について標準化しても変わらないはずで、確かに変わらない
    • もちろん(ad-bc)/nの値はこの標準化によって変わっていない
  • 標準化2
    • 2x2分割表の2軸のどちらについて標準化してもカイ自乗値が変わらないのなら、両軸について標準化することもできそうである
    • 両軸について標準化するとは、e=fかつg=hということである
    • この条件はa=dかつb=cに他ならない
    • 今、標準化1と標準化1’において重要だったのは、(ad-bc)/nを変えないことであった
    • 標準化2においてもこの条件を満足させることとする
    • この条件は、a=d, b=cの値について、それぞれ、aとd、bとcの相乗平均(SQRT(axd),SQRT(bxc))によって、a''=d'',b''=c''を求め、その値について、総和(n''=a''+b''+c''+d'')をとり、x'=x''*n/n''なる値をa',b',c',d'とすることによって実現される
    • この標準化により、周辺度数(e',f',g',h')のすべてが等しくなっていることに注意する
    • 標準化が2重に行われているので、2x2度数の偏りが非常に強かったり、セルの期待度数が小さい場合には、標準化1、標準化1’に比べて値の違いの大きい場合もあるが、十分に相関していると言える
  • ソース

public class StandardizedChi {
public static double[][] StandardCaseCont(int[][] d){
double[][] ret = new double[d.length][d[0].length];
int[] marginal1 = marginal1(d);
int[] marginal2 = marginal2(d);
double[] ncasecont = new double[d.length];
for(int i=0;i<ncasecont.length;i++){
ncasecont[i] = (double)marginal1[i];
}

double nh = Calculator.harmonicMean(ncasecont);

for(int i=0;i<ret.length;i++){
for(int j=0;j<ret[i].length;j++){
ret[i][j] = (double)d[i][j]*nh/ncasecont[i];
}
}

return ret;

}

public static double[][] StandardFactor(int[][] d){
double[][] ret = new double[d.length][d[0].length];
int[] marginal1 = marginal1(d);
int[] marginal2 = marginal2(d);
double[] ncasecont = new double[d[0].length];
for(int i=0;i<ncasecont.length;i++){
ncasecont[i] = (double)marginal2[i];
}

double nh = Calculator.harmonicMean(ncasecont);

for(int i=0;i<ret.length;i++){
for(int j=0;j<ret[i].length;j++){
ret[i][j] = (double)d[i][j]*nh/ncasecont[j];
}
}

return ret;
}
public static double[][] StandardBoth(int[][] d){//2x2table Only

double[] adV = {(double)d[0][0],(double)d[1][1]};
double[] bcV = {(double)d[0][1],(double)d[1][0]};
double ad = Calculator.geometricMean(adV);
double bc = Calculator.geometricMean(bcV);
//System.out.println("ad bc " + ad + " " + bc);
double sumOriginal=0;
double sumSt=0;

//int[] marginal1 = marginal1(d);
//int[] marginal2 = marginal2(d);

/*
double[] ncasecont = new double[d[0].length];
for(int i=0;i<ncasecont.length;i++){
ncasecont[i] = (double)marginal2[i];
}

double nh = Calculator.harmonicMean(ncasecont);
*/
/*
for(int i=0;i<ret.length;i++){
for(int j=0;j<ret[i].length;j++){
ret[i][j] = (double)d[i][j]*nh/ncasecont[j];
}
}
*/
double[][] ret = {{ad,bc},{bc,ad}};
for(int i=0;i<ret.length;i++){
for(int j=0;j<ret[i].length;j++){
sumOriginal += (double)d[i][j];
sumSt +=ret[i][j];
}
}
double ratio = sumOriginal/sumSt;
//System.out.println("ratio " + ratio);
for(int i=0;i<ret.length;i++){
for(int j=0;j<ret[i].length;j++){
ret[i][j] /=ratio;
//System.out.println("ret " + ret[i][j]);
}
}
return ret;
}
public static double[] StCaesContChiStChiFisher(int[][] d){
double[] ret = new double[5];//Chi, ChiP,stChi,stChiP,FisherP
double[][] stTable = StandardCaseCont(d);
double[][] dtable = new double[d.length][d[0].length];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[i].length;j++){
dtable[i][j]=(double)d[i][j];

}
}
ret[0] = Chi.chi(dtable);
ret[1] = Chi2Distribution.upper(1,ret[0]);
ret[2] = Chi.chi(stTable);
ret[3] = Chi2Distribution.upper(1,ret[2]);
ret[4] = Fisher.Fishernxm2(d);

return ret;
}
public static double[] StFactorChiStChiFisher(int[][] d){
double[] ret = new double[5];//Chi, ChiP,stChi,stChiP,FisherP
double[][] stTable = StandardFactor(d);
double[][] dtable = new double[d.length][d[0].length];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[i].length;j++){
dtable[i][j]=(double)d[i][j];

}
}
ret[0] = Chi.chi(dtable);
ret[1] = Chi2Distribution.upper(1,ret[0]);
ret[2] = Chi.chi(stTable);
ret[3] = Chi2Distribution.upper(1,ret[2]);
ret[4] = Fisher.Fishernxm2(d);

return ret;
}
public static double[] StBothChiStChiFisher(int[][] d){
double[] ret = new double[5];//Chi, ChiP,stChi,stChiP,FisherP
double[][] stTable = StandardBoth(d);
double[][] dtable = new double[d.length][d[0].length];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[i].length;j++){
dtable[i][j]=(double)d[i][j];

}
}
ret[0] = Chi.chi(dtable);
ret[1] = Chi2Distribution.upper(1,ret[0]);
ret[2] = Chi.chi(stTable);
ret[3] = Chi2Distribution.upper(1,ret[2]);
ret[4] = Fisher.Fishernxm2(d);

return ret;
}
public static int[] marginal1(int[][] d){
int[] ret = new int[d.length];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[i].length;j++){
ret[i] += d[i][j];
}
}
return ret;
}
public static int[] marginal2(int[][] d){
int[] ret = new int[d[0].length];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[i].length;j++){
ret[j] += d[i][j];
}
}
return ret;
}

}