hirt-0.0.1.1: Driver.hs
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Driver where
import Args
import Engine
import Input
import Output
import Parse
import Types
import Irt
import Statistics
import Control.Monad
import Data.List
import qualified Data.Text as T
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Vector.Generic as V
import Text.Printf
import System.IO
setupEngine :: HIrt -> EngineParams
setupEngine s = EngineParams
{ algorithm = Args.algorithm s
, maxRounds = Args.maxRounds s
, precision = Args.precision s
, intPrec = Args.intPrec s
}
statistic :: (StatisticType -> State -> IO Statistic) -> StatisticType -> State -> IO [Column]
statistic stat t s = return . toCols =<< stat t s
where
toCols (SingleStatistic xs) = [column 11 (T.pack . show $ t) . map fixed' $ xs]
toCols (ListStatistic names xss) =
zipWith (column 11) names . transpose . map (map fixed') $ xss
readParams :: FilePath -> State -> IO State
readParams name state =
let taskNames = V.toList . tasks . Engine.responses $ state
in return . updateParams state =<< fromFile (taskParamsParser $ taskNames) name
readThetas :: FilePath -> State -> IO State
readThetas name state = return . updateThetas state =<< fromFile thetasParser name
formatResp :: RespFormat -> Responses -> Builder
formatResp List = tableBody . responseColumns
formatResp HeadedList = buildTable . responseColumns
formatResp Dot = formatDottedResponses
printStats :: [a] -> Responses -> IO ()
printStats resp responses =
printf "Read %d responses of %d subjects on %d tasks, %d (%4.1f%%) unanswered %d ignored\n"
nread ncont ntask unans punan (nread - nresp)
:: IO ()
where
nread = length resp
ncont = V.length . contestants $ responses
ntask = V.length . tasks $ responses
nresp = V.length . respAll $ responses
unans = ncont * ntask - nresp
punan = fromIntegral unans / fromIntegral (ncont * ntask) * 100.0 :: Double
calcBayes :: State -> ([Column], [Column])
calcBayes State {..} = (bounds, values)
where
merge c ys = map (\(x,y) -> (c,x,y)) ys
(bs,vs) = unzip . map (bayes 0.95) $ groupByContestant responses thetas params
(cs,xs,ys) = unzip3 . concat $ zipWith merge (V.toList $ contestants responses) vs
(lbs,ubs) = unzip bs
bounds = [ numberColumn 12 8 "BayesLB" lbs
, numberColumn 12 8 "BayesUB" ubs ]
values = [ textColumn "contestant" cs
, numberColumn 12 8 "x" xs
, numberColumn 12 8 "p" ys ]
run :: HIrt -> IO ()
run Convert {..} = return ()
run args@Estimate {..} = do
putStrLn "Reading..."
resp <- readResponses responses
let responses = responsesFromList resp
istate = Engine.init responses
engine = setupEngine args in do
printStats resp responses
when (length resp > 0) $ do
maybeWriteFile oResponses (formatResp oRespFormat responses)
putStrLn "Reading initial task parameters..."
istate <- maybe return readParams iTaskParams $ istate
putStrLn "Reading initial contestant abilities..."
istate <- maybe return readThetas iThetas $ istate
putStrLn "Fitting IRT model..."
results <- runEngine engine istate
thetas results `seq` params results `seq` return ()
putStrLn "Calculating bayes probabilities..."
let (bayesBounds, bayesValues) = calcBayes results in do
putStrLn "Saving bayes probability values..."
maybeWriteFile oBayesPlot . buildTable $ bayesValues
putStrLn "Writing task parameters..."
let taskBase = tableTaskParams . getTaskParamsList $ results in do
taskStats <- zipWithM (statistic taskStatistic) taskStats (repeat results)
writeDefFile stdout oTaskParams . buildTable $ taskBase ++ concat taskStats
putStrLn "Writing contestant parameters..."
let thetaBase = tableThetas . getThetasList $ results in do
thetaStats <- zipWithM (statistic thetaStatistic) thetaStats (repeat results)
maybeWriteFile oTheta . buildTable $ thetaBase ++ concat thetaStats ++ bayesBounds
main :: IO ()
main = getArgs >>= run