|
|||||||||||||||||||
| 30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
| Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
| CorrelationEffectMetaAnalysis.java | 75% | 84.8% | 68.8% | 80% |
|
||||||||||||||
| 1 |
package baseCode.math.metaanalysis;
|
|
| 2 |
|
|
| 3 |
import baseCode.math.CorrelationStats;
|
|
| 4 |
import cern.colt.list.DoubleArrayList;
|
|
| 5 |
import cern.jet.stat.Descriptive;
|
|
| 6 |
import cern.jet.stat.Probability;
|
|
| 7 |
|
|
| 8 |
/**
|
|
| 9 |
* Implementation of meta-analysis of correlations along the lines of chapter 18 of Cooper and Hedges, "Handbook of
|
|
| 10 |
* Research Synthesis". Both fixed and random effects models are supported, with z-transformed or untransformed
|
|
| 11 |
* correlations.
|
|
| 12 |
* <hr>
|
|
| 13 |
* <p>
|
|
| 14 |
* Copyright (c) 2004 Columbia University
|
|
| 15 |
*
|
|
| 16 |
* @author pavlidis
|
|
| 17 |
* @version $Id: CorrelationEffectMetaAnalysis.java,v 1.8 2005/03/21 18:01:03 pavlidis Exp $
|
|
| 18 |
*/
|
|
| 19 |
public class CorrelationEffectMetaAnalysis extends MetaAnalysis { |
|
| 20 |
|
|
| 21 |
private boolean transform = false; |
|
| 22 |
private boolean fixed = true; |
|
| 23 |
|
|
| 24 |
private double z; // z score |
|
| 25 |
private double p; // probability |
|
| 26 |
private double q; // q-score; |
|
| 27 |
private double e; // unconditional effect; |
|
| 28 |
private double v; // unconditional variance; |
|
| 29 |
private double n; // total sample size |
|
| 30 |
private double bsv; // between-studies variance component; |
|
| 31 |
|
|
| 32 | 36 |
public CorrelationEffectMetaAnalysis( boolean fixed, boolean transform ) { |
| 33 | 36 |
this.fixed = fixed;
|
| 34 | 36 |
this.transform = transform;
|
| 35 |
} |
|
| 36 |
|
|
| 37 | 0 |
public CorrelationEffectMetaAnalysis() {
|
| 38 |
} |
|
| 39 |
|
|
| 40 |
/**
|
|
| 41 |
* Following CH section 2.2.
|
|
| 42 |
* <p>
|
|
| 43 |
* There are four possible cases (for now):
|
|
| 44 |
* <ol>
|
|
| 45 |
* <li>Fixed effects, Z-transformed. Weights are computed using CH eqns 18-8 and 18-3
|
|
| 46 |
* <li>Fixed effects, untransformed. Weights are computed using CH eqns 18-10 and 18-3
|
|
| 47 |
* <li>Random effects, Z-transformed. Weights are computed using CH eqns 18-20, 18-8 with 18-3
|
|
| 48 |
* <li>Random effects, untransformed. Weights are computed using CH eqns 18-10, 18-20, 18-24.
|
|
| 49 |
* </ol>
|
|
| 50 |
* The default is untransformed, fixed effects.
|
|
| 51 |
*
|
|
| 52 |
* @param correlations - NOT fisher transformed. This routine takes care of that.
|
|
| 53 |
* @param sampleSizes
|
|
| 54 |
* @return p-value. The p-value is also stored in the field p.
|
|
| 55 |
*/
|
|
| 56 | 9 |
public double run( DoubleArrayList effects, DoubleArrayList sampleSizes ) { |
| 57 |
|
|
| 58 | 9 |
DoubleArrayList weights; |
| 59 | 9 |
DoubleArrayList conditionalVariances; |
| 60 | 9 |
this.n = Descriptive.sum( sampleSizes );
|
| 61 |
|
|
| 62 | 9 |
if ( transform ) {
|
| 63 | 5 |
DoubleArrayList fzte = CorrelationStats.fisherTransform( effects ); |
| 64 |
|
|
| 65 |
// initial values.
|
|
| 66 | 5 |
conditionalVariances = fisherTransformedSamplingVariances( sampleSizes ); |
| 67 | 5 |
weights = metaFEWeights( conditionalVariances ); |
| 68 | 5 |
this.q = super.qStatistic( fzte, conditionalVariances, super.weightedMean( fzte, weights ) ); |
| 69 |
|
|
| 70 | 5 |
if ( !fixed ) { // adjust the conditional variances and weights. |
| 71 | 2 |
this.bsv = metaREVariance( fzte, conditionalVariances, weights );
|
| 72 |
|
|
| 73 | 2 |
for ( int i = 0; i < conditionalVariances.size(); i++ ) { |
| 74 | 40 |
conditionalVariances.setQuick( i, conditionalVariances.getQuick( i ) + bsv ); |
| 75 |
} |
|
| 76 | 2 |
weights = metaFEWeights( conditionalVariances ); |
| 77 |
} |
|
| 78 |
|
|
| 79 | 5 |
this.e = super.weightedMean( fzte, weights ); |
| 80 |
} else {
|
|
| 81 |
|
|
| 82 | 4 |
conditionalVariances = samplingVariances( effects, sampleSizes ); |
| 83 | 4 |
weights = metaFEWeights( conditionalVariances ); |
| 84 | 4 |
this.q = super.qStatistic( effects, conditionalVariances, super.weightedMean( effects, weights ) ); |
| 85 |
|
|
| 86 | 4 |
if ( !fixed ) { // adjust the conditional variances and weights. |
| 87 | 1 |
this.bsv = metaREVariance( effects, conditionalVariances, weights );
|
| 88 | 1 |
for ( int i = 0; i < conditionalVariances.size(); i++ ) { |
| 89 | 20 |
conditionalVariances.setQuick( i, conditionalVariances.getQuick( i ) + bsv ); |
| 90 |
} |
|
| 91 |
|
|
| 92 | 1 |
weights = metaFEWeights( conditionalVariances ); |
| 93 |
} |
|
| 94 |
|
|
| 95 | 4 |
this.e = super.weightedMean( effects, weights ); |
| 96 |
} |
|
| 97 | 9 |
this.v = super.metaVariance( conditionalVariances ); |
| 98 | 9 |
this.z = Math.abs( e ) / Math.sqrt( v );
|
| 99 | 9 |
this.p = Probability.errorFunctionComplemented( z );
|
| 100 |
|
|
| 101 |
// System.err.println(effects);
|
|
| 102 |
|
|
| 103 |
// if ( qTest( q, effects.size() ) < 0.05 ) {
|
|
| 104 |
// System.err.println("Q was significant: " + qTest( q, effects.size() ) );
|
|
| 105 |
// }
|
|
| 106 |
|
|
| 107 | 9 |
return p;
|
| 108 |
} |
|
| 109 |
|
|
| 110 |
/**
|
|
| 111 |
* Equation 18-10 from CH. For untransformed correlations.
|
|
| 112 |
* <p>
|
|
| 113 |
*
|
|
| 114 |
* <pre>
|
|
| 115 |
* v_i = ( 1 - r_i ˆ 2 ) ˆ 2 / ( n_i - 1 )
|
|
| 116 |
* </pre>
|
|
| 117 |
*
|
|
| 118 |
* <p>
|
|
| 119 |
* I added a regularization to this, so that we don't get ridiculous variances when correlations are close to 1 (this
|
|
| 120 |
* happens). If the correlation is very close to 1 (or -1), we fudge it to be a value less close to 1 (e.g., 0.999)
|
|
| 121 |
* </p>
|
|
| 122 |
*
|
|
| 123 |
* @param r
|
|
| 124 |
* @param n
|
|
| 125 |
* @return
|
|
| 126 |
*/
|
|
| 127 | 80 |
protected double samplingVariance( double r, double numsamples ) { |
| 128 |
|
|
| 129 | 0 |
if ( numsamples <= 0 ) throw new IllegalArgumentException( "N must be greater than 0" ); |
| 130 |
|
|
| 131 | 80 |
if ( !CorrelationStats.isValidPearsonCorrelation( r ) )
|
| 132 | 0 |
throw new IllegalArgumentException( "r=" + r + " is not a valid Pearson correlation" ); |
| 133 |
|
|
| 134 | 80 |
double FUDGE = 0.001;
|
| 135 | 80 |
if ( Math.abs( r ) - 1.0 < FUDGE ) {
|
| 136 | 80 |
r = Math.abs( r ) - FUDGE; // don't care about sign. any more.
|
| 137 |
} |
|
| 138 |
|
|
| 139 | 80 |
if ( numsamples < 2 ) {
|
| 140 | 0 |
return Double.NaN;
|
| 141 |
} |
|
| 142 | 80 |
double k = 1.0 - r * r;
|
| 143 | 80 |
double var = k * k / ( numsamples - 1 );
|
| 144 |
|
|
| 145 | 80 |
return var;
|
| 146 |
} |
|
| 147 |
|
|
| 148 |
/**
|
|
| 149 |
* Run equation CH 18-10 on a list of sample sizes and effects.
|
|
| 150 |
*
|
|
| 151 |
* @param effectSizes
|
|
| 152 |
* @param sampleSizes
|
|
| 153 |
* @see samplingVariance
|
|
| 154 |
* @return
|
|
| 155 |
*/
|
|
| 156 | 4 |
protected DoubleArrayList samplingVariances( DoubleArrayList effectSizes, DoubleArrayList sampleSizes ) {
|
| 157 |
|
|
| 158 | 0 |
if ( effectSizes.size() != sampleSizes.size() ) throw new IllegalArgumentException( "Unequal sample sizes." ); |
| 159 |
|
|
| 160 | 4 |
DoubleArrayList answer = new DoubleArrayList( sampleSizes.size() );
|
| 161 | 4 |
for ( int i = 0; i < sampleSizes.size(); i++ ) { |
| 162 |
|
|
| 163 | 80 |
double ef = effectSizes.getQuick( i );
|
| 164 |
|
|
| 165 | 80 |
if ( Double.isNaN( ef ) ) {
|
| 166 | 0 |
answer.add( Double.NaN ); |
| 167 |
} else {
|
|
| 168 | 80 |
answer.add( samplingVariance( ef, sampleSizes.getQuick( i ) ) ); |
| 169 |
} |
|
| 170 |
} |
|
| 171 | 4 |
return answer;
|
| 172 |
} |
|
| 173 |
|
|
| 174 |
/**
|
|
| 175 |
* Equation 18-8 from CH. For z-transformed correlations.
|
|
| 176 |
*
|
|
| 177 |
* <pre>
|
|
| 178 |
* v_i = 1 / ( n_i - 3 )
|
|
| 179 |
* </pre>
|
|
| 180 |
*
|
|
| 181 |
* @param n
|
|
| 182 |
* @return
|
|
| 183 |
*/
|
|
| 184 | 100 |
protected double fisherTransformedSamplingVariance( double sampleSize ) { |
| 185 |
|
|
| 186 | 0 |
if ( sampleSize <= 3.0 ) throw new IllegalStateException( "N is too small" ); |
| 187 |
|
|
| 188 | 100 |
return 1.0 / ( sampleSize - 3.0 );
|
| 189 |
} |
|
| 190 |
|
|
| 191 |
/**
|
|
| 192 |
* Run equation CH 18-8 on a list of sample sizes.
|
|
| 193 |
*
|
|
| 194 |
* @param sampleSizes
|
|
| 195 |
* @return
|
|
| 196 |
*/
|
|
| 197 | 5 |
protected DoubleArrayList fisherTransformedSamplingVariances( DoubleArrayList sampleSizes ) {
|
| 198 |
|
|
| 199 | 5 |
DoubleArrayList answer = new DoubleArrayList( sampleSizes.size() );
|
| 200 | 5 |
for ( int i = 0; i < sampleSizes.size(); i++ ) { |
| 201 | 100 |
answer.add( fisherTransformedSamplingVariance( sampleSizes.getQuick( i ) ) ); |
| 202 |
} |
|
| 203 | 5 |
return answer;
|
| 204 |
} |
|
| 205 |
|
|
| 206 | 0 |
public void setFixed( boolean fixed ) { |
| 207 | 0 |
this.fixed = fixed;
|
| 208 |
} |
|
| 209 |
|
|
| 210 | 0 |
public void setTransform( boolean transform ) { |
| 211 | 0 |
this.transform = transform;
|
| 212 |
} |
|
| 213 |
|
|
| 214 | 0 |
public double getP() { |
| 215 | 0 |
return p;
|
| 216 |
} |
|
| 217 |
|
|
| 218 | 1 |
public double getQ() { |
| 219 | 1 |
return q;
|
| 220 |
} |
|
| 221 |
|
|
| 222 | 2 |
public double getZ() { |
| 223 | 2 |
return z;
|
| 224 |
} |
|
| 225 |
|
|
| 226 | 2 |
public double getE() { |
| 227 | 2 |
return e;
|
| 228 |
} |
|
| 229 |
|
|
| 230 | 2 |
public double getV() { |
| 231 | 2 |
return v;
|
| 232 |
} |
|
| 233 |
|
|
| 234 | 0 |
public double getN() { |
| 235 | 0 |
return n;
|
| 236 |
} |
|
| 237 |
|
|
| 238 | 2 |
public double getBsv() { |
| 239 | 2 |
return bsv;
|
| 240 |
} |
|
| 241 |
} |
|
||||||||||