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,mapMaybe) import Controller (Controller,onView,onGridView,onGridModel) import qualified Controller.Grid as Grid import Controller.Dialog (nonNumericDataWarning) import Controller.Canonical (addRowExpr) import Util (justWhen) import Model.CellContent (CellExpr (NumberExpr)) 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 Grid.addColumns $ paramsToLabels params forM_ (reverse result) $ \desc -> do addRowExpr (Just $ label desc) $ descriptiveToRow desc when (not $ null invalidCols) $ nonNumericDataWarning $ reverse invalidCols paramsToLabels :: Parameters -> [String] paramsToLabels params = let toLabel (get,label) = if get params then Just label else Nothing in mapMaybe toLabel [ (calcMean,__ "Mean") , (calcMedian, __ "Median") , (calcStdDeviation, __ "Standard Deviation") , (calcMax, __ "Maximum") , (calcMin, __ "Minimum")] 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 -> [CellExpr] descriptiveToRow desc = map NumberExpr $ catMaybes [ mean desc , median desc , stdDeviation desc , max desc, min desc ]