packages feed

dataframe-1.0.0.0: app/Synthesis.hs

{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}

import qualified Data.Text as T
import qualified DataFrame as D
import qualified DataFrame.Functions as F

import Data.Char
import DataFrame.DecisionTree
import DataFrame.Operators hiding (name)

import System.Random

$(F.declareColumnsFromCsvFile "./data/titanic/train.csv")

main :: IO ()
main = do
    train <- D.readCsv "./data/titanic/train.csv"
    test <- D.readCsv "./data/titanic/test.csv"

    -- Apply the same transformations to training and test.
    let combined =
            (train <> test)
                |> D.deriveMany
                    [ "Ticket" .= F.lift (T.filter isAlpha) ticket
                    , "Name" .= F.match "\\s*([A-Za-z]+)\\." name
                    , "Cabin" .= F.whenPresent (T.take 1) cabin
                    ]
                |> D.renameMany
                    [ (F.name name, "title")
                    , (F.name cabin, "cabin_prefix")
                    , (F.name pclass, "passenger_class")
                    , (F.name sibsp, "number_of_siblings_and_spouses")
                    , (F.name parch, "number_of_parents_and_children")
                    ]
    print combined

    let (train', validation) =
            D.take
                (D.nRows train)
                combined
                |> D.filterJust (F.name survived)
                |> D.randomSplit (mkStdGen 4232) 0.7
        -- Split the test out again.
        test' =
            D.drop
                (D.nRows train)
                combined

        model =
            fitDecisionTree
                ( defaultTreeConfig
                    { maxTreeDepth = 5
                    , minSamplesSplit = 5
                    , minLeafSize = 3
                    , taoIterations = 100
                    , synthConfig =
                        defaultSynthConfig
                            { complexityPenalty = 0.1
                            , maxExprDepth = 3
                            , disallowedCombinations =
                                [ (F.name age, F.name fare)
                                , ("passenger_class", "number_of_siblings_and_spouses")
                                , ("passenger_class", "number_of_parents_and_children")
                                ]
                            }
                    }
                )
                survived -- Label to predict
                ( train'
                    |> D.exclude [F.name passengerid]
                )

    print model

    putStrLn "Training accuracy: "
    print $
        computeAccuracy
            (train' |> D.derive (F.name prediction) model)

    putStrLn "Validation accuracy: "
    print $
        computeAccuracy
            ( validation
                |> D.derive (F.name prediction) model
            )

    let predictions = D.derive (F.name survived) model test'
    D.writeCsv
        "./predictions.csv"
        (predictions |> D.select [F.name passengerid, F.name survived])

prediction :: D.Expr Int
prediction = F.col @Int "prediction"

computeAccuracy :: D.DataFrame -> Double
computeAccuracy df =
    let
        tp =
            fromIntegral $
                D.nRows
                    ( D.filterWhere
                        (survived .== F.lit (1 :: Int) .&& prediction .== F.lit (1 :: Int))
                        df
                    )
        tn =
            fromIntegral $
                D.nRows
                    ( D.filterWhere
                        (survived .== F.lit (0 :: Int) .&& prediction .== F.lit (0 :: Int))
                        df
                    )
        fp =
            fromIntegral $
                D.nRows
                    ( D.filterWhere
                        (survived .== F.lit (0 :: Int) .&& prediction .== F.lit (1 :: Int))
                        df
                    )
        fn =
            fromIntegral $
                D.nRows
                    ( D.filterWhere
                        (survived .== F.lit (1 :: Int) .&& prediction .== F.lit (0 :: Int))
                        df
                    )
     in
        (tp + tn) / (tp + tn + fp + fn)