dataframe-learn-2.0.0.0: src/DataFrame/DecisionTree/Model.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{- | sklearn-style standalone tree estimators returning inspectable records
(depth, leaf count, per-feature split usage). 'fit' trains a classifier (from a
'TreeConfig') or a regressor (from a 'RegTreeConfig'); 'predict' is the compiled
tree expression, and the record exposes the raw 'Tree' too. The bare
'DataFrame.DecisionTree.Fit.fitDecisionTree' remains for callers that only want
the classifier @Expr@.
-}
module DataFrame.DecisionTree.Model (
module DataFrame.Model,
DecisionTreeClassifier (..),
DecisionTreeRegressor (..),
) where
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import qualified Data.Vector as V
import DataFrame.DecisionTree.Cart (cartFeatures)
import DataFrame.DecisionTree.Fit (fitDecisionTree, treeToExpr)
import DataFrame.DecisionTree.Regression (RegTreeConfig, fitRegTreeOn)
import DataFrame.DecisionTree.Types (Tree (..), TreeConfig)
import DataFrame.Featurize.Internal (targetDoubles)
import DataFrame.Internal.Column (Columnable)
import DataFrame.Internal.Expression (Expr (..), getColumns)
import DataFrame.Model
-- | A fitted classification tree with structural diagnostics.
data DecisionTreeClassifier a = DecisionTreeClassifier
{ dtcExpr :: !(Expr a)
, dtcDepth :: !Int
, dtcNLeaves :: !Int
, dtcFeatureUsage :: !(M.Map T.Text Int)
}
deriving (Show)
-- | A fitted regression tree with structural diagnostics.
data DecisionTreeRegressor = DecisionTreeRegressor
{ dtrTree :: !(Tree Double)
, dtrExpr :: !(Expr Double)
, dtrDepth :: !Int
, dtrNLeaves :: !Int
, dtrFeatureUsage :: !(M.Map T.Text Int)
}
deriving (Show)
instance (Columnable a, Ord a) => Fit TreeConfig (Expr a) where
type ModelOf TreeConfig (Expr a) = (DecisionTreeClassifier a)
fit cfg target df =
DecisionTreeClassifier
e
(exprDepth e)
(exprLeaves e)
(usageCounts (exprUsage e))
where
e = fitDecisionTree cfg target df
instance Predict (DecisionTreeClassifier a) where
type Prediction (DecisionTreeClassifier a) = Expr a
predict = dtcExpr
instance Fit RegTreeConfig (Expr Double) where
type ModelOf RegTreeConfig (Expr Double) = DecisionTreeRegressor
fit cfg target df =
DecisionTreeRegressor
t
e
(exprDepth e)
(exprLeaves e)
(usageCounts (exprUsage e))
where
t = case target of
Col name ->
fitRegTreeOn
cfg
(V.fromList (cartFeatures name df))
(targetDoubles target df)
Nothing
_ ->
error
("fit @DecisionTreeRegressor: target must be a column, got " ++ show target)
e = treeToExpr t
instance Predict DecisionTreeRegressor where
type Prediction DecisionTreeRegressor = Expr Double
predict = dtrExpr
usageCounts :: [T.Text] -> M.Map T.Text Int
usageCounts = foldr (\c -> M.insertWith (+) c 1) M.empty
exprUsage :: Expr a -> [T.Text]
exprUsage (If c t e) = getColumns c ++ exprUsage t ++ exprUsage e
exprUsage _ = []
exprLeaves :: Expr a -> Int
exprLeaves (If _ t e) = exprLeaves t + exprLeaves e
exprLeaves _ = 1
exprDepth :: Expr a -> Int
exprDepth (If _ t e) = 1 + max (exprDepth t) (exprDepth e)
exprDepth _ = 0