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 +28/−0
- Math/IRT/Fisher.hs +74/−0
- Math/IRT/Internal/Distribution.hs +4/−0
- Math/IRT/Internal/LogLikelihood.hs +12/−0
- Math/IRT/MLE.hs +25/−0
- Math/IRT/MLE/Fenced.hs +31/−0
- Math/IRT/MLE/Internal/Generic.hs +31/−0
- Math/IRT/MLE/Internal/MLEResult.hs +6/−0
- Math/IRT/MLE/Truncated.hs +28/−0
- Math/IRT/Model/FourPLM.hs +58/−0
- Math/IRT/Model/Generic.hs +8/−0
- Math/IRT/Model/OnePLM.hs +37/−0
- Math/IRT/Model/Rasch.hs +37/−0
- Math/IRT/Model/ThreePLM.hs +39/−0
- Math/IRT/Model/TwoPLM.hs +38/−0
- README.md +6/−0
- Setup.hs +2/−0
- irt.cabal +39/−0
+ 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+