hanalyze-0.2.0.0: test/Hanalyze/Model/Formula/RFormulaSpec.hs
{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Model.Formula.RFormulaSpec (spec) where
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck
import Hanalyze.Model.Formula
import Hanalyze.Model.Formula.Frame
import Hanalyze.Model.Formula.Design
import Hanalyze.Model.Formula.RFormula
import Hanalyze.Model.Formula.Nonlinear
import Hanalyze.Model.Formula.Mixed
import Hanalyze.Model.GLMM
import Hanalyze.Model.GLM (Family (..), LinkFn (..))
import Hanalyze.Stat.Distribution (Transform)
import Data.List (sort, nub)
import Control.Monad (forM, forM_)
import System.IO.Temp (withSystemTempFile)
import System.IO (hPutStr, hClose)
import Hanalyze.Model.HBM.Ast (Expr (..), Lit (..), DoStmt (..), Err)
import Data.IORef (newIORef, readIORef, modifyIORef')
import qualified Data.Text as T
import qualified DataFrame.Internal.Column as DX
import qualified DataFrame.Internal.DataFrame as DX
import qualified Hanalyze.Model.Core as Core
import qualified Hanalyze.MCMC.Core as Core
import SpecHelper
spec :: Spec
spec = do
describe "Hanalyze.Model.Formula.RFormula (A18 R front-end)" $ do
let gs = ["A","A","B","B","C","C","A","A","B","B","C","C"] :: [T.Text]
ts = ["P","Q","P","Q","P","Q","P","Q","P","Q","P","Q"] :: [T.Text]
ys = [10,20,30,40,50,60,12,22,34,44,52,62] :: [Double]
xs = [1,2,3,4,5,6,1,2,3,4,5,6] :: [Double]
df = DX.fromNamedColumns
[ ("y", DX.fromList ys), ("g", DX.fromList gs)
, ("t", DX.fromList ts), ("x", DX.fromList xs) ]
yhat s = case parseModel s >>= \f -> fitLMF f df of
Right (fr, _) -> Right (Core.fittedList fr)
Left e -> Left e
equiv rsyn usyn = case (yhat rsyn, yhat usyn) of
(Right a, Right b) -> and (zipWith (\p q -> abs (p - q) < 1e-9) a b)
_ -> False
describe "dispatch: ~ を含めば R・無ければ独自" $ do
it "y ~ x → 応答 y / データ変数 x" $
case parseModel "y ~ x" of
Right (Formula r dv _) -> (r, dv) `shouldBe` ("y", ["x"])
Left e -> expectationFailure e
it "独自構文 (= 区切り) も同 dispatch で通る" $
parseModel "y x = b0 + b1*x" `shouldSatisfy` isRightE
describe "クロス front-end 等価 (R 構文 vs 独自構文 → 同 ŷ)" $ do
it "linear: y~x ≡ y x = b0+b1*x" $
equiv "y ~ x" "y x = b0 + b1*x" `shouldBe` True
it "factor×factor: y~C(g)*C(t) ≡ 飽和" $
equiv "y ~ C(g)*C(t)" "y g t = b0 + bg!g + bt!t + bgt!g!t" `shouldBe` True
it "factor×連続: y~C(g)+C(g):x ≡ 水準別傾き" $
equiv "y ~ C(g) + C(g):x" "y g x = b0 + bg!g + bs!g*x" `shouldBe` True
it "I(x**2): y~x+I(x**2) ≡ b0+b1*x+b2*x^2" $
equiv "y ~ x + I(x**2)" "y x = b0 + b1*x + b2*x^2" `shouldBe` True
it "poly: y~poly(x,2) ≡ b0+bp!poly(x,2)" $
equiv "y ~ poly(x,2)" "y x = b0 + bp ! poly(x,2)" `shouldBe` True
-- round-trip 用の任意 Formula 生成 (識別子は固定プール・Lit は非負整数)。