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 }