packages feed

irt (empty) → 0.2.0.0

raw patch · 18 files changed

+503/−0 lines, 18 filesdep +addep +basedep +data-default-classsetup-changed

Dependencies added: ad, base, data-default-class, statistics

Files

+ LICENSE view
@@ -0,0 +1,28 @@+Copyright (c) 2014, Elliot Robinson+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Elliot Robinson nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Math/IRT/Fisher.hs view
@@ -0,0 +1,74 @@+module Math.IRT.Fisher+    ( FisherInfo(..)+    , fisherInfoObserved+    , fisherInfoExpected+    , l'+    , l''+    ) where++import Statistics.Distribution+import Math.IRT.Internal.Distribution+++data FisherInfo = FisherInfo { items :: [Double]+                             , test  :: !Double+                             , sem   :: !Double+                             } deriving (Show)+++-- |The observed Fisher Information+--  Applies a min/max prior to ensure a real-valued SEM+fisherInfoObserved :: (ContDistr d, DensityDeriv d) => Double -> [Bool] -> [d] -> FisherInfo+fisherInfoObserved theta resps params =+    let is = zipWith go resps params+        t  = sum is+        s  = sqrt (1 / t)+    in FisherInfo is t s+    where go u x = negate $ l'' u x theta+++-- |The expected Fisher Information+fisherInfoExpected :: ContDistr d => Double -> [d] -> FisherInfo+fisherInfoExpected theta params =+    let is = map go params+        t  = sum is+        s  = sqrt (1 / t)+    in FisherInfo is t s+    where go x = let (cdf, ccdf, pdf) = pqDers x theta+                 in (square pdf) / (cdf * ccdf)+++-- |The first derivative of `l`, whatever `l` is... Once again, the catIrt documentation disappoints.+-- According to catIrt, "u is the response, and x are the parameters."+-- We only implement the MLE route+l' :: ContDistr d => Bool -> d -> Double -> Double+l' u x theta =+    let (cdf, ccdf, pdf) = pqDers x theta+        denom = cdf * ccdf+        f k   = k * pdf / denom+    in case u of+         True  -> f ccdf+         False -> f cdf+++-- |The second derivative of `l` (same one as in `l'`)+l'' :: (ContDistr d, DensityDeriv d) => Bool -> d -> Double -> Double+l'' u x theta =+    let (cdf, ccdf, pdf) = pqDers x theta+        pdf'             = densityDeriv x theta+    in case u of+         True  -> (((-1) / square cdf) * (square pdf))+                  + (1 / cdf * pdf')+         False -> negate $ ((1 / square ccdf) * square pdf)+                           + (1 / ccdf * pdf')+++pqDers :: ContDistr d => d -> Double -> (Double, Double, Double)+pqDers x theta = let pComp = cumulative x theta+                     p'    = density x theta+                 in ( pComp+                    , (1 - pComp)+                    , p')++square :: Double -> Double+square = (^^ (2 :: Int))
+ Math/IRT/Internal/Distribution.hs view
@@ -0,0 +1,4 @@+module Math.IRT.Internal.Distribution where++class DensityDeriv a where+    densityDeriv :: a -> Double -> Double
+ Math/IRT/Internal/LogLikelihood.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE GADTs #-}+module Math.IRT.Internal.LogLikelihood where++import Numeric.AD (Mode, Scalar)+import Statistics.Distribution (Distribution)++class (Distribution d) => LogLikelihood d where+    logLikelihood :: (Mode a, Floating a, Scalar a ~ Double) => Bool -> d -> a -> a++logLikeFunc :: (Distribution d, Mode a, Floating a, Scalar a ~ Double) => (d -> a -> a) -> Bool -> d -> a -> a+logLikeFunc f True  = (log .) . f+logLikeFunc f False = ((log . (1-)) .) . f
+ Math/IRT/MLE.hs view
@@ -0,0 +1,25 @@+module Math.IRT.MLE+  ( DF (..)+  , MLEResult (..)+  , mleEst+  ) where++import Data.Default.Class++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.MLE.Internal.Generic+++data DF = DF { steps         :: !Int+             , thetaEstimate :: !Double }++instance Default DF where+  def = DF 10 0.0+++mleEst :: (Distribution d, ContDistr d, DensityDeriv d, LogLikelihood d) => DF -> [Bool] -> [d] -> MLEResult+mleEst (DF n vTheta) rs params =+    generic_mleEst rs params n vTheta
+ Math/IRT/MLE/Fenced.hs view
@@ -0,0 +1,31 @@+module Math.IRT.MLE.Fenced+  ( DF (..)+  , MLEResult (..)+  , mleEst+  ) where++import Data.Default.Class++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.MLE.Internal.Generic+import Math.IRT.Model.Generic+++data DF = DF { steps                :: !Int+             , thetaEstimate        :: !Double+             , lower_fence          :: !Double+             , upper_fence          :: !Double+             , fence_discrimination :: !Double }++instance Default DF where+  def = DF 10 0.0 (-3.5) 3.5 3.0+++mleEst :: (ContDistr d, DensityDeriv d, LogLikelihood d, GenericModel d) => DF -> [Bool] -> [d] -> MLEResult+mleEst (DF n vTheta lf uf fd) rs params =+    let resp = True : False : rs+        pars = fromThreePLM fd lf 0 : fromThreePLM fd uf 0 : params+    in generic_mleEst resp pars n vTheta
+ Math/IRT/MLE/Internal/Generic.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE GADTs #-}+module Math.IRT.MLE.Internal.Generic+  ( MLEResult (..)+  , generic_mleEst+  , logLike+  ) where++import Numeric.AD (Mode, Scalar)+import Numeric.AD.Halley++import Statistics.Distribution (Distribution, ContDistr)++import Math.IRT.Fisher+import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.MLE.Internal.MLEResult+++generic_mleEst :: (Distribution d, ContDistr d, DensityDeriv d, LogLikelihood d) => [Bool] -> [d] -> Int -> Double -> MLEResult+generic_mleEst rs params steps vTheta =+    let est    = (!! steps) $ extremumNoEq (logLike rs params) vTheta+        fisher = fisherInfoObserved est rs params+    in case fisher of+         (FisherInfo _ t s) -> MLEResult est t s+++logLike :: (Mode a, Floating a, Scalar a ~ Double, Distribution d, LogLikelihood d) => [Bool] -> [d] -> a -> a+logLike responses params vTheta =+    let logLik   = zipWith (\a b -> logLikelihood a b vTheta) responses params+        bmePrior = 1+    in sum logLik + log bmePrior
+ Math/IRT/MLE/Internal/MLEResult.hs view
@@ -0,0 +1,6 @@+module Math.IRT.MLE.Internal.MLEResult where++data MLEResult = MLEResult { theta :: !Double+                           , info  :: !Double+                           , sem   :: !Double+                           } deriving (Show)
+ Math/IRT/MLE/Truncated.hs view
@@ -0,0 +1,28 @@+module Math.IRT.MLE.Truncated+  ( DF (..)+  , MLEResult (..)+  , mleEst+  ) where++import Data.Default.Class++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.MLE.Internal.Generic+++data DF = DF { steps         :: !Int+             , thetaEstimate :: !Double+             , lower_bound   :: !Double+             , upper_bound   :: !Double }++instance Default DF where+  def = DF 10 0.0 (-3.5) 3.5+++mleEst :: (Distribution d, ContDistr d, DensityDeriv d, LogLikelihood d) => DF -> [Bool] -> [d] -> MLEResult+mleEst (DF n th lb ub) rs params =+    let res = generic_mleEst rs params n th+    in res { theta = min ub $ max lb $ theta res }
+ Math/IRT/Model/FourPLM.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE GADTs #-}+module Math.IRT.Model.FourPLM+  ( FourPLM (..)+  ) where++import Numeric.AD (Mode, Scalar, auto)+import Numeric.AD.Mode.Forward.Double+import qualified Numeric.AD.Mode.Tower as T++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.Model.Generic+++data FourPLM = FourPLM { discrimination :: !Double+                       , difficulty     :: !Double+                       , pseudoGuessing :: !Double+                       , asymptote      :: !Double+                       } deriving (Show)++instance Distribution FourPLM where+    cumulative = cumulative4PL++instance ContDistr FourPLM where+    density  x = diff (cumulative4PL x)+    quantile _ = error "This shouldn't be needed"++instance DensityDeriv FourPLM where+    densityDeriv x = (!! 2) . T.diffs (cumulative4PL x)++instance GenericModel FourPLM where+    fromRasch         b = FourPLM 1.0 b 0.0 1.0+    fromOnePLM        b = FourPLM 1.7 b 0.0 1.0+    fromTwoPLM      a b = FourPLM   a b 0.0 1.0+    fromThreePLM  a b c = FourPLM   a b   c 1.0+    fromFourPLM         = FourPLM++instance LogLikelihood FourPLM where+    logLikelihood = logLikeFunc cumulative4PL+++cumulative4PL :: (Mode a, Floating a, Scalar a ~ Double) =>+       FourPLM -- ^The IRT parameters+    -> a       -- ^Theta+    -> a+cumulative4PL params theta =+    let (a, b, c, d) = makeParamAutos params in+    c + (              (d - c)+         -----------------------------------+         / (1 + exp ((-a) * (theta - b))))+    where makeParamAutos (FourPLM sa sb sc sd) =+              let a = auto sa+                  b = auto sb+                  c = auto sc+                  d = auto sd+              in (a, b, c, d)
+ Math/IRT/Model/Generic.hs view
@@ -0,0 +1,8 @@+module Math.IRT.Model.Generic where++class GenericModel a where+    fromRasch    :: Double -> a+    fromOnePLM   :: Double -> a+    fromTwoPLM   :: Double -> Double -> a+    fromThreePLM :: Double -> Double -> Double -> a+    fromFourPLM  :: Double -> Double -> Double -> Double -> a
+ Math/IRT/Model/OnePLM.hs view
@@ -0,0 +1,37 @@+module Math.IRT.Model.OnePLM+  ( OnePLM (..)+  ) where++import Statistics.Distribution++import Math.IRT.Internal.Distribution +import Math.IRT.Internal.LogLikelihood+import Math.IRT.Model.FourPLM ( FourPLM(..) )+import Math.IRT.Model.Generic+++data OnePLM = OnePLM { difficulty :: !Double+                     } deriving (Show)++instance Distribution OnePLM where+    cumulative = cumulative . toFourPLM++instance ContDistr OnePLM where+    density    = density . toFourPLM+    quantile _ = error "This shouldn't be needed"++instance DensityDeriv OnePLM where+    densityDeriv = densityDeriv . toFourPLM++instance GenericModel OnePLM where+    fromRasch           = OnePLM+    fromOnePLM          = OnePLM+    fromTwoPLM      _ b = OnePLM b+    fromThreePLM  _ b _ = OnePLM b+    fromFourPLM _ b _ _ = OnePLM b++instance LogLikelihood OnePLM where+    logLikelihood b = logLikelihood b . toFourPLM++toFourPLM :: OnePLM -> FourPLM+toFourPLM (OnePLM sb) = FourPLM 1.7 sb 0.0 1.0
+ Math/IRT/Model/Rasch.hs view
@@ -0,0 +1,37 @@+module Math.IRT.Model.Rasch+  ( RaschModel (..)+  ) where++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.Model.FourPLM ( FourPLM(..) )+import Math.IRT.Model.Generic+++data RaschModel = RaschModel { difficulty :: !Double+                             } deriving (Show)++instance Distribution RaschModel where+    cumulative = cumulative . toFourPLM++instance ContDistr RaschModel where+    density    = density . toFourPLM+    quantile _ = error "This shouldn't be needed"++instance DensityDeriv RaschModel where+    densityDeriv = densityDeriv . toFourPLM++instance GenericModel RaschModel where+    fromRasch           = RaschModel+    fromOnePLM          = RaschModel+    fromTwoPLM      _ b = RaschModel b+    fromThreePLM  _ b _ = RaschModel b+    fromFourPLM _ b _ _ = RaschModel b++instance LogLikelihood RaschModel where+    logLikelihood b = logLikelihood b . toFourPLM++toFourPLM :: RaschModel -> FourPLM+toFourPLM (RaschModel sb) = FourPLM 1.0 sb 0.0 1.0
+ Math/IRT/Model/ThreePLM.hs view
@@ -0,0 +1,39 @@+module Math.IRT.Model.ThreePLM+  ( ThreePLM (..)+  ) where++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.Model.FourPLM ( FourPLM(..) )+import Math.IRT.Model.Generic+++data ThreePLM = ThreePLM { discrimination :: !Double+                         , difficulty     :: !Double+                         , pseudoGuessing :: !Double+                         } deriving (Show)++instance Distribution ThreePLM where+    cumulative = cumulative . toFourPLM++instance ContDistr ThreePLM where+    density    = density . toFourPLM+    quantile _ = error "This shouldn't be needed"++instance DensityDeriv ThreePLM where+    densityDeriv = densityDeriv . toFourPLM++instance GenericModel ThreePLM where+    fromRasch         b = ThreePLM 1.0 b 0.0+    fromOnePLM        b = ThreePLM 1.7 b 0.0+    fromTwoPLM      a b = ThreePLM   a b 0.0+    fromThreePLM        = ThreePLM+    fromFourPLM a b c _ = ThreePLM   a b   c++instance LogLikelihood ThreePLM where+    logLikelihood b = logLikelihood b . toFourPLM++toFourPLM :: ThreePLM -> FourPLM+toFourPLM (ThreePLM sa sb sc) = FourPLM sa sb sc 1.0
+ Math/IRT/Model/TwoPLM.hs view
@@ -0,0 +1,38 @@+module Math.IRT.Model.TwoPLM+  ( TwoPLM (..)+  ) where++import Statistics.Distribution++import Math.IRT.Internal.Distribution+import Math.IRT.Internal.LogLikelihood+import Math.IRT.Model.FourPLM ( FourPLM(..) )+import Math.IRT.Model.Generic+++data TwoPLM = TwoPLM { discrimination :: !Double+                     , difficulty     :: !Double+                     } deriving (Show)++instance Distribution TwoPLM where+    cumulative = cumulative . toFourPLM++instance ContDistr TwoPLM where+    density    = density . toFourPLM+    quantile _ = error "This shouldn't be needed"++instance DensityDeriv TwoPLM where+    densityDeriv = densityDeriv . toFourPLM++instance GenericModel TwoPLM where+    fromRasch         b = TwoPLM 1.0 b+    fromOnePLM        b = TwoPLM 1.7 b+    fromTwoPLM          = TwoPLM+    fromThreePLM  a b _ = TwoPLM   a b+    fromFourPLM a b _ _ = TwoPLM   a b++instance LogLikelihood TwoPLM where+    logLikelihood b = logLikelihood b . toFourPLM++toFourPLM :: TwoPLM -> FourPLM+toFourPLM (TwoPLM sa sb) = FourPLM sa sb 0.0 1.0
+ README.md view
@@ -0,0 +1,6 @@+IRT+===++A Haskell library providing Item Response Theory functions for use in computerized adaptive testing. Provided functionality is similar to the CAT portions of the `catIrt` R library, though only the binary-response model is currently supported.++IRT bucks the standard trend of starting Haskell libraries/executables with 'H', since `hirt` already exists.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ irt.cabal view
@@ -0,0 +1,39 @@+name: irt+version: 0.2.0.0+cabal-version: >=1.10+build-type: Simple+license: BSD3+license-file: LICENSE+maintainer: elliot.robinson@argiopetech.com+homepage: https://github.com/argiopetech/irt+synopsis: A Haskell library providing Item Response Theory functions for use in computerized adaptive testing+category: Math+author: Elliot Robinson+extra-source-files:+    README.md++library+    exposed-modules:+        Math.IRT.Model.OnePLM+        Math.IRT.Model.TwoPLM+        Math.IRT.Model.ThreePLM+        Math.IRT.Model.FourPLM+        Math.IRT.Model.Rasch+        Math.IRT.Model.Generic+        Math.IRT.MLE+        Math.IRT.MLE.Fenced+        Math.IRT.MLE.Truncated+        Math.IRT.MLE.Internal.Generic+        Math.IRT.MLE.Internal.MLEResult+        Math.IRT.Fisher+        Math.IRT.Internal.Distribution+        Math.IRT.Internal.LogLikelihood+    build-depends:+        base >=4.7 && <4.10,+        ad >=4.2 && <4.4,+        data-default-class ==0.1.*,+        statistics ==0.13.*+    default-language: Haskell2010+    default-extensions: TemplateHaskell+    other-extensions: GADTs+