View Javadoc

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     public CorrelationEffectMetaAnalysis( boolean fixed, boolean transform ) {
33        this.fixed = fixed;
34        this.transform = transform;
35     }
36  
37     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     public double run( DoubleArrayList effects, DoubleArrayList sampleSizes ) {
57  
58        DoubleArrayList weights;
59        DoubleArrayList conditionalVariances;
60        this.n = Descriptive.sum( sampleSizes );
61  
62        if ( transform ) {
63           DoubleArrayList fzte = CorrelationStats.fisherTransform( effects );
64  
65           // initial values.
66           conditionalVariances = fisherTransformedSamplingVariances( sampleSizes );
67           weights = metaFEWeights( conditionalVariances );
68           this.q = super.qStatistic( fzte, conditionalVariances, super.weightedMean( fzte, weights ) );
69  
70           if ( !fixed ) { // adjust the conditional variances and weights.
71              this.bsv = metaREVariance( fzte, conditionalVariances, weights );
72  
73              for ( int i = 0; i < conditionalVariances.size(); i++ ) {
74                 conditionalVariances.setQuick( i, conditionalVariances.getQuick( i ) + bsv );
75              }
76              weights = metaFEWeights( conditionalVariances );
77           }
78  
79           this.e = super.weightedMean( fzte, weights );
80        } else {
81  
82           conditionalVariances = samplingVariances( effects, sampleSizes );
83           weights = metaFEWeights( conditionalVariances );
84           this.q = super.qStatistic( effects, conditionalVariances, super.weightedMean( effects, weights ) );
85  
86           if ( !fixed ) { // adjust the conditional variances and weights.
87              this.bsv = metaREVariance( effects, conditionalVariances, weights );
88              for ( int i = 0; i < conditionalVariances.size(); i++ ) {
89                 conditionalVariances.setQuick( i, conditionalVariances.getQuick( i ) + bsv );
90              }
91  
92              weights = metaFEWeights( conditionalVariances );
93           }
94  
95           this.e = super.weightedMean( effects, weights );
96        }
97        this.v = super.metaVariance( conditionalVariances );
98        this.z = Math.abs( e ) / Math.sqrt( v );
99        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       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 &circ; 2 ) &circ; 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    protected double samplingVariance( double r, double numsamples ) {
128 
129       if ( numsamples <= 0 ) throw new IllegalArgumentException( "N must be greater than 0" );
130 
131       if ( !CorrelationStats.isValidPearsonCorrelation( r ) )
132             throw new IllegalArgumentException( "r=" + r + " is not a valid Pearson correlation" );
133 
134       double FUDGE = 0.001;
135       if ( Math.abs( r ) - 1.0 < FUDGE ) {
136          r = Math.abs( r ) - FUDGE; // don't care about sign. any more.
137       }
138 
139       if ( numsamples < 2 ) {
140          return Double.NaN;
141       }
142       double k = 1.0 - r * r;
143       double var = k * k / ( numsamples - 1 );
144 
145       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    protected DoubleArrayList samplingVariances( DoubleArrayList effectSizes, DoubleArrayList sampleSizes ) {
157 
158       if ( effectSizes.size() != sampleSizes.size() ) throw new IllegalArgumentException( "Unequal sample sizes." );
159 
160       DoubleArrayList answer = new DoubleArrayList( sampleSizes.size() );
161       for ( int i = 0; i < sampleSizes.size(); i++ ) {
162 
163          double ef = effectSizes.getQuick( i );
164 
165          if ( Double.isNaN( ef ) ) {
166             answer.add( Double.NaN );
167          } else {
168             answer.add( samplingVariance( ef, sampleSizes.getQuick( i ) ) );
169          }
170       }
171       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    protected double fisherTransformedSamplingVariance( double sampleSize ) {
185 
186       if ( sampleSize <= 3.0 ) throw new IllegalStateException( "N is too small" );
187 
188       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    protected DoubleArrayList fisherTransformedSamplingVariances( DoubleArrayList sampleSizes ) {
198 
199       DoubleArrayList answer = new DoubleArrayList( sampleSizes.size() );
200       for ( int i = 0; i < sampleSizes.size(); i++ ) {
201          answer.add( fisherTransformedSamplingVariance( sampleSizes.getQuick( i ) ) );
202       }
203       return answer;
204    }
205 
206    public void setFixed( boolean fixed ) {
207       this.fixed = fixed;
208    }
209 
210    public void setTransform( boolean transform ) {
211       this.transform = transform;
212    }
213 
214    public double getP() {
215       return p;
216    }
217 
218    public double getQ() {
219       return q;
220    }
221 
222    public double getZ() {
223       return z;
224    }
225 
226    public double getE() {
227       return e;
228    }
229 
230    public double getV() {
231       return v;
232    }
233 
234    public double getN() {
235       return n;
236    }
237 
238    public double getBsv() {
239       return bsv;
240    }
241 }