C:/programs/etirm/src/DiscreteLatentDist.h

Go to the documentation of this file.
00001 /*! \file DiscreteLatentDist.h
00002  
00003   \brief
00004   Class containing information about a discrete latent variable distribution
00005   for one or more groups of examinees.
00006 
00007   Estimation Toolkit for Item Response Models (ETIRM)
00008   http://www.smallwaters.com/software/cpp/etirm.html
00009 
00010   Author(s): 
00011   Werner Wothke, maintenance (http://www.smallwaters.com)
00012   Brad Hanson (http://www.b-a-h.com/)
00013   See the file LICENSE for information on usage and redistribution.
00014 
00015   Copyright (C) 2008, Werner Wothke
00016   Copyright (c) 2000-2001, Bradley A. Hanson
00017  */
00018 
00019 #ifndef ETIRM_DISCRETELATENTDIST_H_
00020 #define ETIRM_DISCRETELATENTDIST_H_
00021 
00022 #ifdef ETIRM_NO_DIR_PREFIX
00023 #include "etirmtypes.h"
00024 #else
00025 #include "etirm/etirmtypes.h"
00026 #endif
00027 
00028 #include <vector>
00029 #include <cmath> // for sqrt
00030 // for compilers which do not put C library functions in std namespace
00031 #ifdef BOOST_NO_STDC_NAMESPACE
00032 namespace std { using ::sqrt;}
00033 #endif
00034 
00035 namespace etirm
00036 {
00037 
00038   /*!
00039     \brief
00040     Class approximating the latent ability distribution by numeric quadrature.
00041 
00042     \section template_args Template Parameters
00043    
00044     \param  L  Type of latent variable.
00045    */
00046   template <class L> class DiscreteLatentDist
00047   {
00048 
00049 public:
00050 
00051     typedef L latentvar_type; //!< Type of latent variable.
00052     typedef std::vector<L> point_container; //!< Type of container for points.
00053     typedef typename std::vector<L>::iterator point_iterator; //!< Type of iterator over quadrature points.
00054     typedef RealMatrix::row_iterator weight_iterator; //!< Type of iterator over quadrature weigths.
00055 
00056     //! Constructor using number of quadrature points, number of groups, etc.
00057     DiscreteLatentDist(int ntheta, int ngroups = 1, bool uniquePoints = false);
00058     
00059     //! Copy constructor using DiscreteLatentDist object.
00060     DiscreteLatentDist(const DiscreteLatentDist &dist);
00061 
00062     point_iterator begin_points(int g = 1);
00063     // Returns iterator to first point for group g (1-offset).
00064 
00065     weight_iterator begin_weights(int g = 1);
00066     // Returns iterator to first weight for group g (1-offset).
00067 
00068     int size();
00069     // Returns number of discrete points.
00070 
00071     int NumGroups();
00072     // Returns number of examinee groups.
00073 
00074     int NumGroupsUnique();
00075     // Returns number of examinee groups for which points of latent distribution are unique.
00076 
00077     void Transform(Real slope, Real intercept);
00078     // Transforms scale of points using a specified linear transformation.
00079 
00080     void Scale(Real mean, Real sd, int group, Real &slope, Real &intercept);
00081     // Modifies points to obtain a specified mean and s.d. for a group.
00082 
00083     void MeanSD(int group, Real &mean, Real &sd);
00084     // Computes mean and s.d. of distribution for a group.
00085 
00086     virtual Real MStep(const RealVector &eprob, int g);
00087     // Computes an M-step for the probabilities of the multinomial discrete latent variable 
00088     // distribution in one examinee group.
00089     
00090 private:
00091 
00092     bool mUniquePoints;
00093     //!< If true then unique latent distribution points are used for each group.
00094 
00095     int mNumPoints;
00096     //!< Number of points in latent variable distribution for each group.
00097 
00098     int mNumGroups;
00099     //!< Number of examinee groups.
00100 
00101     std::vector<L> mPoints;
00102     /*!<  Vector of latent distribution points.
00103       If unique latent distribution points are used for each group
00104       then the size of this vector will be the product of the number
00105       of points in each group and the number of groups.
00106       The points for each group are stacked in this vector, so the
00107       first point for group g is mPoints[mNumPoints * (g-1)].
00108      */
00109 
00110     RealMatrix mWeights;
00111     //!< Each row gives the distribution for one group of examinees.
00112   };
00113 
00114   /* Constructor */
00115   template<class L> DiscreteLatentDist<L>::DiscreteLatentDist(int ntheta, int ngroups,
00116       bool uniquePoints) :
00117     mNumPoints(ntheta), mPoints(ntheta), mWeights(ngroups, ntheta), mUniquePoints(uniquePoints),
00118         mNumGroups(ngroups)
00119   {
00120     if (uniquePoints)
00121       mPoints.resize(ntheta*ngroups);
00122   }
00123 
00124   /* Copy constructor */
00125   template<class L> DiscreteLatentDist<L>::DiscreteLatentDist(const DiscreteLatentDist &dist) :
00126     mUniquePoints(dist.mUniquePoints), mNumPoints(dist.mNumPoints), mNumGroups(dist.mNumGroups),
00127         mPoints(dist.mPoints), mWeights(dist.mWeights)
00128   {
00129   }
00130 
00131   /*! 
00132     \brief
00133     Returns the number of discrete points for each group.
00134 
00135     \section template_args Template Parameters
00136 
00137     \param  L  Type of latent variable. 
00138    */
00139   template<class L> inline int DiscreteLatentDist<L>::size()
00140   {
00141     return mNumPoints;
00142   }
00143 
00144   /*! 
00145     \brief
00146     Returns the number of groups of examinees.
00147 
00148     \section template_args Template Parameters
00149  
00150     \param  L  Type of latent variable. 
00151    */
00152   template<class L> inline int DiscreteLatentDist<L>::NumGroups()
00153   {
00154     return mNumGroups;
00155   }
00156 
00157   /*!
00158     \brief
00159     Returns number of examinee groups for which points of latent distribution are unique.
00160     
00161     \section template_args Template Parameters
00162  
00163     \param  L  Type of latent variable.
00164    */
00165   template<class L> inline int DiscreteLatentDist<L>::NumGroupsUnique()
00166   {
00167     return mUniquePoints ? mNumGroups : 1;
00168   }
00169 
00170   /*! 
00171     \brief
00172     Returns iterator to first point for group g (1-offset).
00173 
00174     \section template_args Template Parameters
00175  
00176     \param  L  Type of latent variable.
00177 
00178     \section function_args Function Parameters
00179    
00180     \param[in]  g Group to return pointer for, where first group = 1, second group = 2, etc.
00181    */
00182   template<class L> inline typename std::vector<L>::iterator DiscreteLatentDist<L>::begin_points(
00183       int g)
00184   {
00185     return mUniquePoints ? (mPoints.begin()+mNumPoints*(g-1)) : mPoints.begin();
00186   }
00187 
00188   /*! 
00189     \brief
00190     Returns iterator to first weight for group g (1-offset).
00191 
00192     \section template_args Template Parameters
00193  
00194     \param  L  Type of latent variable.
00195 
00196     \section function_args Function Parameters
00197    
00198     \param  g Group to return weights for, where first group = 1, second group = 2, etc.
00199    */
00200   template<class L> inline typename RealMatrix::row_iterator DiscreteLatentDist<L>::begin_weights(
00201       int g)
00202   {
00203     return mWeights.begin_row(g);
00204   }
00205 
00206   /*! 
00207     \brief 
00208     Computes mean and standard deviation in the specified group.
00209 
00210     \section template_args Template Parameters
00211  
00212     \param  L  Type of latent variable.
00213       
00214     \section function_args Function Parameters
00215    
00216     \param[in]  group Group whose distribution is used.
00217     \param[out] &mean  Address if mean of distribution.
00218     \param[out] &sd Address of standard deviation of distribution.
00219    */
00220   template<class L> void DiscreteLatentDist<L>::MeanSD(int group, Real &mean, Real &sd)
00221   {
00222     /* Check for valid group */
00223     if (group < 1 || group > mNumGroups)
00224     throw InvalidArgument("Invalid examinee group", "DiscreteLatentDist::MeanSD");
00225 
00226     int i;
00227 
00228     /* Compute mean of distribution in specified group */
00229     point_iterator ip = begin_points(group);
00230     weight_iterator iw = begin_weights(group);
00231     mean = 0.0;
00232     for (i = mNumPoints; i--; ++ip, ++iw)
00233     {
00234       mean += *ip * *iw;
00235     }
00236 
00237     /* Compute s.d. of distribution in specified group */
00238     ip = begin_points(group);
00239     iw = begin_weights(group);
00240     sd = 0.0;
00241     for (i = mNumPoints; i--; ++ip, ++iw)
00242     {
00243       Real dev = *ip - mean;
00244       sd += dev * dev * *iw;
00245     }
00246     sd = std::sqrt(sd);
00247   }
00248 
00249   /*!
00250     \brief
00251     Transforms scale of points using a specified linear transformation.
00252 
00253     Points for all groups are transformed.
00254 
00255     \section template_args Template Parameters
00256  
00257     \param  L  Type of latent variable.
00258       
00259     \section function_args Function Parameters
00260    
00261     \param[in] slope  Slope of scale transformation.
00262     \param[in] intercept Intercept of scale transformation.
00263    */
00264   template<class L>
00265   void DiscreteLatentDist<L>::Transform(Real slope, Real intercept)
00266   {
00267 
00268     int n, i;
00269 
00270     if (mUniquePoints) n = mNumGroups;
00271     else n = 1;
00272 
00273     /* Transform points for all groups */
00274     for (int g = 1; g <= n; ++g)
00275     {
00276       point_iterator ip = begin_points(g);
00277       for (i = mNumPoints; i--; ++ip)
00278       {
00279         *ip *= slope;
00280         *ip += intercept;
00281       }
00282     }
00283   }
00284 
00285   /*! 
00286     \brief
00287     Transforms scale of points so that mean and standard deviation in the specified
00288     group are equal to the values indicated. 
00289     
00290     Points for all groups are transformed.
00291    
00292     \section template_args Template Parameters
00293  
00294     \param  L  Type of latent variable. 
00295       
00296     \section function_args Function Parameters
00297    
00298     \param[in] mean   Desired mean of distribution.
00299     \param[in] sd   Desired standard deviation of distribution.
00300     \param[in] group  Group for which mean and s.d. are specified.
00301     \param[out] slope Slope of scale transformation.
00302     \param[out] intercept Intercept of scale transformation.
00303    
00304    */
00305   template<class L>
00306   void DiscreteLatentDist<L>::Scale(Real mean, Real sd, int group,
00307   Real &slope, Real &intercept)
00308   {
00309 
00310     /* Check for valid group */
00311     if (group < 1 || group> mNumGroups)
00312     throw InvalidArgument("Invalid examinee group", "DiscreteLatentDist::Scale");
00313 
00314     Real oldmean;
00315     Real oldsd;
00316     MeanSD(group, oldmean, oldsd);
00317 
00318     /* Compute slope and intercept to transfer from old scale to new scale */
00319     slope = sd / oldsd;
00320     intercept = mean - slope * oldmean;
00321 
00322     /* Transform points */
00323     Transform(slope, intercept);
00324   }
00325 
00326   /*! 
00327     \brief
00328     Computes an M-step for the probabilities of the multinomial discrete latent variable distribution
00329     in one examinee group.
00330    
00331     \section template_args Template Parameters
00332  
00333     \param  L  Type of latent variable.
00334       
00335     \section function_args Function Parameters
00336    
00337     \param[in] &eprob Address of vector containing probabilities for each latent variable 
00338         category, computed in last E-step for group. 
00339     \param[in] g  Group to estimate latent distribution for (g = 1, 2, ..., "number of groups").
00340    */
00341   template<class L>
00342   Real DiscreteLatentDist<L>::MStep(const RealVector &eprob, int g)
00343   {
00344     int ncat = mNumPoints;
00345 
00346     if (ncat != eprob.size())
00347     {
00348       throw RuntimeError("Mismatch in number of latent variable points",
00349       "DiscreteLatentDist::MStep");
00350     }
00351 
00352     if (g < 1 || g> mNumGroups)
00353     {
00354       throw RuntimeError("Invalid examinee group number",
00355       "DiscreteLatentDist::MStep");
00356     }
00357 
00358     /* Find total N for this group */
00359     RealVector::const_iterator in = eprob.begin();
00360     Real ntot = 0.0;
00361     int i;
00362     for (i = ncat; i--; ++in) ntot += *in;
00363 
00364     /* Update group latent variable distribution */
00365     Real reldiff = 0.0;
00366     Real typicalValue = 1.0 / ncat;
00367     in = eprob.begin();
00368     weight_iterator iw = begin_weights(g);
00369     for (i = ncat; i--; ++in, ++iw)
00370     {
00371       Real old = *iw;
00372       *iw = *in / ntot;
00373 
00374       // Compute absolute relative difference between old and new probability
00375       Real d = old - *iw;
00376       Real denom = (*iw < typicalValue) ? typicalValue : *iw;
00377       if (denom != 0.0) d /= denom;
00378       d = (d < 0.0) ? -d : d; // fabs(d)
00379       if (reldiff < d) reldiff = d;
00380     }
00381 
00382     return reldiff;
00383 
00384   }
00385 
00386 } // namespace etirm
00387 
00388 #endif // ETIRM_DISCRETELATENTDIST_H_

Generated on Sat Mar 1 21:40:15 2008 for ETIRM by  doxygen 1.5.4