packages feed

lhae-0.0.2: src/Controller/Menu/Statistics/Descriptive.hs

module Controller.Menu.Statistics.Descriptive (eventHandler)
    where

import Prelude hiding (max,min)
import Control.Monad (foldM,forM_,when)
import Control.Applicative ((<$>))
import Data.Maybe (catMaybes)
import Controller (Controller,onView,onGridView,onGridModel)
import qualified Controller.Grid as Grid
import Controller.Dialog (nonNumericDataWarning)
import Controller.Menu.Table.Add (addColumn)
import Controller.Canonical (addRow)
import Util (justWhen)
import Model.Grid (getColumnValues)
import Model.CellExpression.Evaluator.Common (getNumList)
import qualified Model.CellExpression.Evaluator.Math as Math
import View.Component.Grid (getColumnLabel,getColumnLabels)
import View.Dialog.Complex (Layout (..),Widget (..),Modifier (..)
                           ,showSimpleDialog,okButton,cancelButton)
import I18n (__)

data Parameters = Parameters { columns :: [Int]
                             , calcMean :: Bool
                             , calcMedian :: Bool
                             , calcStdDeviation :: Bool
                             , calcMax :: Bool
                             , calcMin :: Bool
                             }

data Descriptive = Descriptive { label :: String
                               , mean :: Maybe Double
                               , median :: Maybe Double
                               , stdDeviation :: Maybe Double
                               , max :: Maybe Double
                               , min :: Maybe Double
                               }

type Result = ([String],[Descriptive])

eventHandler :: Controller ()
eventHandler = do
  labels <- onGridView getColumnLabels
  params <- onView $ showSimpleDialog (__ "Descriptive Statistics")
            (dialog (__ "Columns") labels) $ Parameters [] True True True True True
  justWhen params calcDescriptiveTable

dialog :: String -> [String] -> Layout Parameters ()
dialog labelsCaption labels =
    let setColumns p cols = p {columns = cols}
        setCalcMean p b = p {calcMean = b}
        setCalcMedian p b = p {calcMedian = b}
        setCalcStdDev p b = p {calcStdDeviation = b}
        setCalcMax p b = p {calcMax = b}
        setCalcMin p b = p {calcMin = b}
    in
      Modifier Margin $ Column
        [ Row [ Grid
                [[ Label $ __ "Mean"
                 , Widget $ CheckBox "" calcMean setCalcMean]
                ,[ Label $ __ "Median"
                 , Widget $ CheckBox "" calcMean setCalcMedian]
                ,[ Label $ __ "Standard Deviation"
                 , Widget $ CheckBox "" calcStdDeviation setCalcStdDev]
                ,[ Label $ __ "Maximum", Widget $ CheckBox "" calcMax setCalcMax]
                ,[ Label $ __ "Minimum", Widget $ CheckBox "" calcMin setCalcMin]]
              , Modifier (Boxed labelsCaption) 
                  $ Widget $ MultiListBox labels columns setColumns]
        , Modifier Center $ Row [okButton (),cancelButton]]

calcDescriptiveTable :: Parameters -> Controller ()
calcDescriptiveTable params = do
  (invalidCols,result) <- foldM (descriptive params) ([],[]) 
                          $ columns params

  when (not $ null result) $
       do Grid.new Nothing
          when (calcMean params) $ addColumn $ Just $ __ "Mean"
          when (calcMedian params) $ addColumn $ Just $ __ "Median"
          when (calcStdDeviation params) $ addColumn $ Just $ __ "Standard Deviation"
          when (calcMax params) $ addColumn $ Just $ __ "Maximum"
          when (calcMin params) $ addColumn $ Just $ __ "Minimum"

          forM_ (reverse result) $ \desc ->
              do addRow (Just $ label desc,descriptiveToRow desc)

  when (not $ null invalidCols) 
    $ nonNumericDataWarning $ reverse invalidCols

descriptive :: Parameters -> Result -> Int -> Controller Result
descriptive params (invalidCols,xs) col = do
  colLabel <- onGridView $ getColumnLabel col
  result <- getNumList <$> (onGridModel $ getColumnValues col)
  case (result) of
    Left _ -> return (colLabel : invalidCols,xs)
    Right values -> 
        let calculate condition v = if condition params 
                                    then Just v else Nothing
            mean = calculate calcMean $ Math.mean values
            median = calculate calcMedian $ Math.median values
            stdDev = calculate calcStdDeviation $ Math.stdDeviation values
            max = calculate calcMax $ maximum values
            min = calculate calcMax $ minimum values
        in
          return ( invalidCols
                 , Descriptive colLabel mean median stdDev max min : xs
                 )

descriptiveToRow :: Descriptive -> [String]
descriptiveToRow desc =
    map show $ catMaybes [ mean desc
                         , median desc
                         , stdDeviation desc
                         , max desc, min desc
                         ]