ideas-statistics-1.0: src/Domain/Statistics/Component.hs
-----------------------------------------------------------------------------
-- Copyright 2020, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
{-# LANGUAGE FlexibleInstances #-}
module Domain.Statistics.Component
( Choice (..), Component (..), ComponentId (..)
, Sided (..), TestType (..), RejectionHypotheses(..)
, choices, getCId, fromCId, sidedRelation
, isData, isExpr, isRelation
, isTestType, isSided, isRejectionHypotheses, isConclusion
) where
import Control.Applicative ((<|>))
import Control.Monad
import Data.Char
import Data.Function
import Data.List
import Domain.Statistics.Symbols
import Domain.Math.Data.Relation
import Domain.Math.Expr
import Domain.Statistics.Data
import Ideas.Common.Rewriting hiding (trueSymbol,falseSymbol)
import Prelude hiding (lookup)
data Component
= CData Data -- Data component type (sample data)
| CExpr Expr -- Expression component type
| CRelation (Relation Expr)
| CChoice Choice -- Expressing various choices
deriving Eq
instance Show Component where
show (CRelation r) = show r
show (CExpr e) = show e
show (CChoice c) = show c
show (CData d) = show d
instance IsTerm Component where
toTerm (CData x) = unary cDataSymbol (toTerm x)
toTerm (CChoice x) = toTerm x
toTerm (CRelation x) = toTerm x
toTerm (CExpr x) = toTerm x
-- Check to see if the whole x can be parsed as an expression
fromTerm (TCon s [val])
| s == cDataSymbol = CData <$> fromTerm val
-- First try to convert it to a relation because relation is more specific
-- than an Expression (IMPORTANT)
fromTerm t = CChoice <$> fromTerm t <|>
CRelation <$> fromTerm t <|>
CExpr <$> fromTerm t
data Choice
= TestType TestType
| Sided Sided
| RejectionHypotheses RejectionHypotheses
| Conclusion Bool
deriving Eq
instance Show Choice where
show (TestType tt) = show tt
show (Sided sd) = show sd
show (RejectionHypotheses rh) = show rh
show (Conclusion c) = map toLower (show c)
instance IsTerm Choice where
toTerm (TestType x) = toTerm x
toTerm (Sided x) = toTerm x
toTerm (RejectionHypotheses rh) = toTerm rh
toTerm (Conclusion x) = toTermBool x
fromTerm t =
Sided <$> fromTerm t <|>
TestType <$> fromTerm t <|>
RejectionHypotheses <$> fromTerm t <|>
Conclusion <$> fromTermBool t
choices :: [Choice]
choices = [ TestType tt | tt <- [minBound .. maxBound] ] ++
[ Sided sd | sd <- [minBound .. maxBound] ] ++
[ RejectionHypotheses rj | rj <- [minBound .. maxBound] ] ++
[ Conclusion c | c <- [minBound .. maxBound] ]
data TestType
= TTestOne
| TTestTwo
| TTestPaired
| ZTest
| RPearson
| Anova
| ChiSquared
deriving (Eq, Enum, Bounded)
instance Show TestType where
-- to do: use same string as corresponding symbols (without dashes)
show TTestOne = "t-test-one-sample"
show TTestTwo = "t-test-two-sample"
show TTestPaired = "t-test-paired"
show ZTest = "z-test"
show RPearson = "r-pearson"
show Anova = "anova"
show ChiSquared = "chi-squared"
data Sided
= TwoSided
| LeftSided
| RightSided
deriving (Eq, Enum, Bounded)
instance Show Sided where
show TwoSided = "two"
show LeftSided = "left"
show RightSided = "right"
sidedRelation :: Sided -> Expr -> Expr -> Relation Expr
sidedRelation LeftSided lhs rhs = makeType LessThan lhs rhs
sidedRelation RightSided lhs rhs = makeType GreaterThan lhs rhs
sidedRelation TwoSided lhs rhs = makeType GreaterThan (abs lhs) rhs
data RejectionHypotheses
= RejectH0
| AcceptH0
| DontRejectH0
| RejectH1
| AcceptH1
| DontRejectH1
deriving (Eq, Enum, Bounded)
instance Show RejectionHypotheses where
show RejectH0 = "rejecth0"
show AcceptH0 = "accepth0"
show DontRejectH0 = "dontrejecth0"
show RejectH1 = "rejecth1"
show AcceptH1 = "accepth1"
show DontRejectH1 = "dontrejecth1"
data ComponentId
= DataSet
| SampleSize -- n
| SampleMean -- M
| SampleVariance -- s^2
| SampleSdev -- s
| ObservedFrequencies -- fo (two-dimensional matrix), for chi squared
| ObservedRowTotals
| ObservedColumnTotals
| ObservedTotal
| ExpectedFrequencies -- fe (two-dimensional matrix), for chi squared
| PopulationMean -- mu
| PopulationSdev -- sigma
| StandardError -- sigmaM or SEM
| SignificanceLevel -- alpha
| NullHypothesis
| AlternativeHypothesis
| PooledVariance -- S_p
| Df -- df
| DfBetween -- for Anova
| DfWithin -- for Anova
| Groups
| Sidedness
| TestChoice
| Critical
| PValue
| RejectionCritical
| ConclusionPValue
| ConclusionCritical
| ConclusionHypotheses
| TestFormula
| TestValue
| Proportion -- P
| Correlation -- r
| One ComponentId -- Belonging to the first sample (in case of multiple samples)
| Two ComponentId -- Belonging to the second sample
| Other String
deriving (Eq, Show)
instance Ord ComponentId where
compare = compare `on` getCId
instance IsTerm ComponentId where
toTerm = toTerm . getCId
fromTerm t = fromCId <$> fromTerm t
getCId :: ComponentId -> String
getCId (Other nm) = nm
getCId (One nm) = getCId nm ++ "1"
getCId (Two nm) = getCId nm ++ "2"
getCId cId = case lookup cId cIdTable of
Just nm -> nm
_ -> error "Missing name for ComponentId"
fromCId :: String -> ComponentId
fromCId s
| suffix == "1" = One (fromCId $ init s)
| suffix == "2" = Two (fromCId $ init s)
| otherwise =
case [ x | (x, y) <- cIdTable, y == s ] of
[x] -> x
_ -> Other s
where
suffix = take 1 (reverse s)
cIdTable :: [(ComponentId, String)]
cIdTable =
[ (DataSet, "data")
, (SampleSize, "n")
, (SampleMean, "samplemean") -- was: "mean"
, (SampleVariance, "samplevariance") -- was: "variance"
, (SampleSdev, "samplesd") -- was: "sd"
, (ObservedFrequencies, "fo")
, (ObservedRowTotals, "forowtotal")
, (ObservedColumnTotals, "focolumntotal")
, (ObservedTotal, "fototal")
, (ExpectedFrequencies, "fe")
, (PopulationMean, "populationmean") -- was: "pmean"
, (PopulationSdev, "populationsd") -- was "psd"
, (StandardError, "standarderror") -- was: "se"
, (SignificanceLevel, "alpha")
, (NullHypothesis, "h0")
, (AlternativeHypothesis, "ha")
, (PooledVariance, "pooledvariance") -- was "sp2"
, (Df, "df")
, (DfWithin, "dfwithin")
, (DfBetween, "dfbetween")
, (Groups, "groups")
, (Sidedness, "sided")
, (TestChoice, "test")
, (Critical, "critical")
, (PValue, "pvalue")
, (RejectionCritical, "rejectioncritical") -- was: "rejection"
, (ConclusionPValue, "conclusionpvalue")
, (ConclusionCritical, "conclusioncritical") -- was: "conclusion"
, (ConclusionHypotheses, "conclusionhypotheses")
, (TestFormula, "testformula")
, (TestValue, "testvalue")
, (Proportion, "p")
, (Correlation, "r")
]
----------------------------------------------------------
-- Component utilities
isData :: Monad m => Component -> m Data
isData (CData d) = return d
isData _ = failMsg "data"
isExpr :: Monad m => Component -> m Expr
isExpr (CExpr e) = return e
isExpr _ = failMsg "expr"
isRelation :: Monad m => Component -> m (Relation Expr)
isRelation (CRelation r) = return r
isRelation _ = failMsg "relation"
isTestType :: Monad m => Component -> m TestType
isTestType (CChoice (TestType tt)) = return tt
isTestType _ = failMsg "testtype"
isSided :: Monad m => Component -> m Sided
isSided (CChoice (Sided s)) = return s
isSided _ = failMsg "sided"
isRejectionHypotheses :: Monad m => Component -> m RejectionHypotheses
isRejectionHypotheses (CChoice (RejectionHypotheses h)) = return h
isRejectionHypotheses _ = failMsg "rejectionhypotheses"
isConclusion :: Monad m => Component -> m Bool
isConclusion (CChoice (Conclusion b)) = return b
isConclusion _ = failMsg "conclusion"
failMsg :: Monad m => String -> m a
failMsg s = fail ("component is not of type " ++ s)
----------------------------------------------------------
-- Component utilities
-- | Types that have only atomic constructors are easy and follow a pattern for
-- their implementation
instance IsTerm TestType where
toTerm TTestOne = symbol tTestOneSymbol
toTerm TTestTwo = symbol tTestTwoSymbol
toTerm TTestPaired = symbol tTestPairedSymbol
toTerm ZTest = symbol zTestSymbol
toTerm RPearson = symbol rPearsonSymbol
toTerm Anova = symbol anovaSymbol
toTerm ChiSquared = symbol chiSquaredSymbol
fromTerm (TCon s [])
| s == tTestOneSymbol = return TTestOne
| s == tTestTwoSymbol = return TTestTwo
| s == tTestPairedSymbol = return TTestPaired
| s == zTestSymbol = return ZTest
| s == rPearsonSymbol = return RPearson
| s == anovaSymbol = return Anova
| s == chiSquaredSymbol = return ChiSquared
fromTerm (TVar "rpearson") = return RPearson -- temporary fix!!!
fromTerm (TVar "anova") = return Anova -- temporary fix!!!
fromTerm (TVar "chisquared") = return ChiSquared -- temporary fix!!!
fromTerm t = fail $ "Invalid term: " ++ show t ++ " not TestType"
instance IsTerm Sided where
toTerm TwoSided = symbol twoSidedSymbol
toTerm LeftSided = symbol leftSidedSymbol
toTerm RightSided = symbol rightSidedSymbol
fromTerm (TCon s [])
| s == twoSidedSymbol = return TwoSided
| s == leftSidedSymbol = return LeftSided
| s == rightSidedSymbol = return RightSided
fromTerm t = fail $ "Invalid term: " ++ show t ++ " not Sided"
instance IsTerm RejectionHypotheses where
toTerm RejectH0 = symbol rejectH0Symbol
toTerm AcceptH0 = symbol acceptH0Symbol
toTerm DontRejectH0 = symbol dontRejectH0Symbol
toTerm RejectH1 = symbol rejectH1Symbol
toTerm AcceptH1 = symbol acceptH1Symbol
toTerm DontRejectH1 = symbol dontRejectH1Symbol
fromTerm (TCon s [])
| s == rejectH0Symbol = return RejectH0
| s == acceptH0Symbol = return AcceptH0
| s == dontRejectH0Symbol = return DontRejectH0
| s == rejectH1Symbol = return RejectH1
| s == acceptH1Symbol = return AcceptH1
| s == dontRejectH1Symbol = return DontRejectH1
fromTerm t = fail $ "Invalid term: " ++ show t ++ " not RejectionHypotheses"