cmtkGeneralLinearModel.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2010 SRI International
00006 //
00007 //  This file is part of the Computational Morphometry Toolkit.
00008 //
00009 //  http://www.nitrc.org/projects/cmtk/
00010 //
00011 //  The Computational Morphometry Toolkit is free software: you can
00012 //  redistribute it and/or modify it under the terms of the GNU General Public
00013 //  License as published by the Free Software Foundation, either version 3 of
00014 //  the License, or (at your option) any later version.
00015 //
00016 //  The Computational Morphometry Toolkit is distributed in the hope that it
00017 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00018 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 //  GNU General Public License for more details.
00020 //
00021 //  You should have received a copy of the GNU General Public License along
00022 //  with the Computational Morphometry Toolkit.  If not, see
00023 //  <http://www.gnu.org/licenses/>.
00024 //
00025 //  $Revision: 2398 $
00026 //
00027 //  $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkGeneralLinearModel.h"
00034 
00035 #include <math.h>
00036 #include <stdlib.h>
00037 #include <stdio.h>
00038 
00039 #ifdef HAVE_IEEEFP_H
00040 #  include <ieeefp.h>
00041 #endif
00042 
00043 #include <System/cmtkProgress.h>
00044 
00045 #include <Base/cmtkMathUtil.h>
00046 
00047 namespace
00048 cmtk
00049 {
00050 
00053 
00054 #define TOL 1.0e-5
00055 GeneralLinearModel::GeneralLinearModel
00056 ( const size_t nParameters, const size_t nData, const double* designMatrix ) :
00057   NParameters( nParameters ),
00058   NData( nData ),
00059   DesignMatrix( nData, nParameters, designMatrix ),
00060   Up( nParameters ),
00061   Vp( nParameters ),
00062   Wp( nParameters ),
00063   VariableMean( nParameters ),
00064   VariableSD( nParameters )
00065 {
00066   U = new Matrix2D<double>( NData, NParameters );
00067   V = new Matrix2D<double>( NParameters, NParameters );
00068   W = new std::vector<double>( NParameters );
00069 
00070   double wmax, thresh;
00071 
00072   std::vector<double> data( this->NData );
00073   for ( size_t j=0; j < NParameters; ++j ) 
00074     {
00075     // set up data vector for parameter 'j'
00076     for ( size_t i=0; i < this->NData; ++i ) 
00077       {
00078       data[i] = DesignMatrix[i][j];
00079       (*U)[i][j] = DesignMatrix[i][j];
00080       }
00081 
00082     // compute variance
00083     this->VariableMean[j] = MathUtil::Mean<double>( data );
00084     this->VariableSD[j] = MathUtil::Variance<double>( data, this->VariableMean[j] );
00085     
00086     // convert variance to standard deviation
00087     this->VariableSD[j] = sqrt( this->VariableSD[j] );
00088     }
00089   
00090   // perform SVD of design matrix
00091   //svdcmp( this->U, this->NData, this->NParameters, this->W, this->V );
00092 
00093   MathUtil::SVD( this->U, this->NData, this->NParameters, this->W, this->V );
00094  
00095   // prepare partial regressions, each with one of the parameters omitted
00096   for ( size_t p=0; p < this->NParameters; ++p) 
00097     {
00098     Up[p] = new Matrix2D<double>( NData, NParameters-1 );
00099     Vp[p] = new Matrix2D<double>( NParameters-1, NParameters-1 );
00100     Wp[p] = new std::vector<double>( NParameters-1 );
00101     
00102     // create partial design matrix, omitting parameter 'p'
00103     for ( size_t i=0; i < this->NData; ++i ) 
00104       {
00105       size_t jj = 0;
00106       for ( size_t j=0; j < this->NParameters; ++j ) 
00107         {
00108         if ( j != p ) 
00109           {
00110           (*(this->Up[p]))[i][jj] = DesignMatrix[i][j];
00111           ++jj;
00112           }
00113         }
00114       }
00115     
00116     //svdcmp( this->Up[p], this->NData, this->NParameters-1, this->Wp[p], this->Vp[p] );
00117     MathUtil::SVD( this->Up[p], this->NData, this->NParameters-1, this->Wp[p], this->Vp[p] );
00118     }
00119   
00120   wmax=0.0;
00121   for ( size_t j=0;j<NParameters;j++)
00122     if ((*W)[j] > wmax) wmax=(*W)[j];
00123   thresh=TOL*wmax;
00124   for ( size_t j=0;j<NParameters;j++)
00125     if ((*W)[j] < thresh) (*W)[j]=0.0;
00126 }
00127 
00128 
00129 Matrix2D<double>*
00130 GeneralLinearModel::GetCorrelationMatrix() const
00131 {
00132   Matrix2D<double>* CC = new Matrix2D<double>( this->NParameters, this->NParameters );
00133 
00134   std::vector<double> pi( this->NData );
00135   std::vector<double> pj( this->NData );
00136 
00137   for ( size_t i = 0; i < this->NParameters; ++i )
00138     {
00139     for ( size_t n = 0; n < this->NData; ++n ) 
00140       {
00141       pi[n] = this->DesignMatrix[n][i];
00142       }
00143     
00144     for ( size_t j = 0; j < this->NParameters; ++j ) 
00145       {
00146       if ( i <= j )
00147         {
00148         for ( size_t n = 0; n < this->NData; ++n ) 
00149           {
00150           pj[n] = this->DesignMatrix[n][j];
00151           }
00152         (*CC)[i][j] = MathUtil::Correlation( pi, pj );
00153         }
00154       else
00155         {
00156         (*CC)[i][j] = (*CC)[j][i];
00157         }
00158       }
00159     }
00160   
00161   return CC;
00162 }
00163 
00164 GeneralLinearModel::~GeneralLinearModel()
00165 {
00166   for ( size_t p=0; p < this->NParameters; ++p) 
00167     {
00168     delete this->Wp[p];
00169     delete this->Vp[p];
00170     delete this->Up[p];
00171     }
00172   delete this->W; 
00173   delete this->V; 
00174   delete this->U; 
00175 }
00176 
00177 void
00178 GeneralLinearModel::InitResults( const size_t nPixels )
00179 {
00180   Model.clear();
00181   TStat.clear();
00182   for (size_t p = 0; (p<this->NParameters); ++p ) 
00183     {
00184     TypedArray::SmartPtr nextModel( TypedArray::Create( TYPE_FLOAT, nPixels ) );
00185     Model.push_back( nextModel );
00186     
00187     TypedArray::SmartPtr nextTStat( TypedArray::Create( TYPE_FLOAT, nPixels ) );
00188     TStat.push_back( nextTStat );
00189     }
00190   
00191   FStat = TypedArray::SmartPtr( TypedArray::Create( TYPE_FLOAT, nPixels ) );
00192 }
00193 
00194 void
00195 GeneralLinearModel::FitModel
00196 ( std::vector<TypedArray::SmartPtr>& y, const bool normalizeParameters )
00197 {
00198   assert( y.size() == this->NData );
00199   const size_t nPixels = y[0]->GetDataSize();
00200   this->InitResults( nPixels );
00201   
00202   std::vector<double> lm_params( this->NParameters );
00203   std::vector<double> b( this->NData );
00204   std::vector<double> valueYhat( this->NData );
00205   
00206   // number of degrees of freedom for t-statistics
00207   // note: we omit "-1" because the constant in our model is either suppressed or an explicit parameter
00208   const int df = this->NData - this->NParameters;
00209 
00210   const size_t pixelUpdateIncrement = 10000;
00211   Progress::Begin( 0, nPixels, pixelUpdateIncrement, "Linear model fitting" );
00212 
00213   for ( size_t n = 0; n < nPixels; ++n ) 
00214     {
00215     if ( ! (n % pixelUpdateIncrement) )
00216       if ( Progress::SetProgress( n ) != Progress::OK ) break;
00217     
00218     bool missing = false;
00219     Types::DataItem value;
00220     for (size_t i = 0; (i<this->NData) && !missing; i++) 
00221       if ( y[i]->Get( value, n ) && finite( value ) )
00222         b[i] = value;
00223       else
00224         missing = true;
00225 
00226     if ( missing )
00227       {
00228       for (size_t p = 0; (p<this->NParameters); ++p ) 
00229         {
00230         this->Model[p]->SetPaddingAt( n );
00231         this->TStat[p]->SetPaddingAt( n );
00232         }
00233       } 
00234     else 
00235       {
00236       // use SVD of design matrix to compute model parameters lm_params[] from data b[]
00237       MathUtil::SVDLinearRegression( this->U, this->NData, this->NParameters, this->W, this->V, &b[0], lm_params );
00238 
00239       // compute variance of data
00240       double varY, avgY;
00241       avgY = MathUtil::Mean<double>( this->NData, &b[0] );
00242       varY = MathUtil::Variance<double>( this->NData, &b[0], avgY );
00243       
00244       // copy model parameters into output
00245       for (size_t p = 0; (p<this->NParameters); ++p ) 
00246         {
00247         value = lm_params[p];
00248         if ( normalizeParameters )
00249           // Cohen & Cohen, Eq. (3.5.2)
00250 //        Model[p]->Set( lm_params[p] * this->GetNormFactor( p ) / sqrt( varY ), n );
00251           value *= this->GetNormFactor( p );
00252 
00253         if ( finite( value ) )
00254           this->Model[p]->Set( value, n );
00255         else
00256           this->Model[p]->SetPaddingAt( n );
00257         }
00258       
00259       // compute variance of approximated data using entire model
00260       double varYhat, avgYhat;
00261       for (size_t i = 0; i<this->NData; i++) 
00262         { 
00263         valueYhat[i] = 0.0;
00264         for (size_t pi = 0; (pi<this->NParameters); ++pi )
00265           valueYhat[i] += lm_params[pi] * this->DesignMatrix[i][pi];
00266         }
00267       avgYhat = MathUtil::Mean<double>( this->NData, &valueYhat[0] );
00268       varYhat = MathUtil::Variance<double>( this->NData, &valueYhat[0], avgYhat );
00269       
00270       // compute multiple R square
00271       const double R2 = varYhat / varY;
00272       this->FStat->Set( (R2*df) / ((1-R2)*this->NParameters), n );
00273       
00274       std::vector<double> lm_params_P( this->NParameters-1 );
00275       std::vector<double> valueYhatp( this->NData );
00276       
00277       // for each parameter, evaluate R^2_i for model without parameter Xi
00278       for (size_t p = 0; p < this->NParameters; ++p ) 
00279         {
00280         // exclude constant parameter
00281 //      if ( this->VariableSD[p] > 0 )
00282           {
00283 //        // use SVD of partial design matrix to compute partial regression
00284           MathUtil::SVDLinearRegression( this->Up[p], this->NData, this->NParameters-1, this->Wp[p], this->Vp[p], &b[0], lm_params_P );
00285 
00286           // compute variance of data
00287           for (size_t i = 0; i < this->NData; i++) 
00288             { 
00289             valueYhatp[i] = 0.0;
00290             size_t pip = 0;
00291             for (size_t pi = 0; pi < this->NParameters; ++pi ) 
00292               {
00293               if ( p != pi ) 
00294                 {
00295                 valueYhatp[i] += lm_params_P[pip] * this->DesignMatrix[i][pi];
00296                 ++pip;
00297                 }
00298               }
00299             }
00300           
00301           double varYhatp, avgYhatp;
00302           avgYhatp = MathUtil::Mean<double>( valueYhatp );
00303           varYhatp = MathUtil::Variance<double>( valueYhatp, avgYhatp );
00304           
00305           // copmpute R^2_p
00306           const double R2p = varYhatp / varY;
00307 //        assert( (R2p >= 0) && (R2p < 1) );
00308           // compute sr_p^2 from R^2 and R^2_p
00309           const double srp = sqrt( R2 - R2p );
00310           //          assert( (sr2p >= 0) && (sr2p <= 1 ) );
00311           // compute T-statistics
00312           double tStat = static_cast<double>( srp * sqrt( df / (1.0-R2) ) );
00313           // export T-statistics (set to zero if NAN)
00314           if ( MathUtil::IsNaN( tStat ) ) 
00315             tStat = 0;
00316           this->TStat[p]->Set( tStat, n ); 
00317           }
00318         }
00319       }
00320     }
00321   
00322   Progress::Done();
00323 }
00324 
00325 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines