packages feed

HLearn-classification-0.0.1: src/HLearn/DataContainers/DS_List.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module HLearn.DataContainers.DS_List
    where

import Control.Applicative
import Control.Monad
import Control.Monad.Random
import Data.List
import Debug.Trace
import Safe
import Test.QuickCheck

import qualified Data.Foldable as F
import qualified Data.Traversable as T

import HLearn.Algebra
import HLearn.DataContainers
import HLearn.DataContainers.CSVParser
-- import HLearn.Misc.LazyDecodeList
-- import HLearn.RandUtils

import qualified Control.ConstraintKinds as CK

-------------------------------------------------------------------------------
-- DS_List

data DS_List label dataType = DS_List
    { dsDesc :: DataDesc label
    , dsL :: [dataType]
    , dsLen :: Int
    }
    deriving (Read,Show)
    
    
instance (Eq label) => Semigroup (DS_List label dataType) where
    (<>) (DS_List dsDesc1 dsL1 dsLen1) (DS_List dsDesc2 dsL2 dsLen2) = 
        if (dsDesc1 /= dsDesc2)
           then error "DS_List.(<>): adding two elements of different dimensions"
           else DS_List dsDesc1 (dsL1++dsL2) (dsLen1+dsLen2)
           
-- instance (Eq label) => Monoid (DS_List label datatype) where
--     mappend = (<>)
--     mempty = DS_List undefined mempty 0

-- arbitraryDS_List :: DataDesc -> Gen (DS_List Int (LDPS Int))
-- arbitraryDS_List desc = do
--     dsLen <- choose (100,200)
--     dsL <- vector dsLen
--     return $ DS_List 
--         { dsDesc = desc
--         , dsL = dsL
--         , dsLen = dsLen
--         , dsLabelL = [0..numLabels desc]
--         }

instance CK.Partitionable (DS_List label) where
    partition k ds = [ ds {dsL = dsL'} | dsL' <- CK.partition k (dsL ds)]

instance F.Foldable (DS_List label) where
    foldr f model0 ds = foldr f model0 (dsL ds)

instance CK.Foldable (DS_List label) where
    foldr f model0 ds = F.foldr f model0 (dsL ds)
    foldl f model0 ds = F.foldl f model0 (dsL ds)
    foldl' f model0 ds = F.foldl' f model0 (dsL ds)
    foldl1 model0 ds = F.foldl1 model0 (dsL ds)
    foldr1 model0 ds = F.foldr1 model0 (dsL ds)

instance Functor (DS_List label) where
    fmap f ds = ds { dsL = fmap f $ dsL ds }

instance CK.Functor (DS_List label) where
    fmap f ds = ds { dsL = fmap f $ dsL ds }

instance T.Traversable (DS_List label) where
    traverse f ds = liftA (\dsL' -> ds { dsL = dsL' } ) $ T.traverse f (dsL ds)

instance (Ord label, Ord dataType, Show label, Show dataType) => DataSparse label (DS_List label) dataType where
    emptyds desc = DS_List
        { dsDesc = desc
        , dsL = []
        , dsLen = 0
        }

    getDataDesc ds = dsDesc ds
    getNumObs = length . dsL --dsLen
    getObsL ds = [0..(dsLen ds)-1]
    getDataL = dsL

--     randomize ds = do
--         dsL' <- shuffle $ dsL ds
--         return $ ds { dsL = dsL' }

{-    sample num wds = do
        dsL' <- sampleL num $ dsL wds
        return $ wds
            { dsL = dsL'
            , dsLen = length dsL'
            }-}
     
--     zip3dsL :: ds dataType -> [a] -> [b] -> ds (dataType,a,b)
    zip3dsL ds xs ys = ds { dsL = zipped, dsLen = length zipped }
        where zipped = zip3 (dsL ds) xs ys
     
    zipdsL ds xs = ds { dsL = zipped, dsLen = length zipped }
        where zipped = zip (dsL ds) xs
              
    filterds cond ds = ds { dsL = dsL', dsLen = length dsL' }
        where dsL' = filter cond $ dsL ds
    
    splitdtree dt ds = ( ds { dsL = dsL1, dsLen = length dsL1 }
                       , ds { dsL = dsL2, dsLen = length dsL2 }
                       )
        where 
            dsL1 = filter (<=dt) (dsL ds)
            dsL2 = filter (> dt) (dsL ds)
    
{-    randSplit factor ds = do
        (dsL1,dsL2) <- randSplitL factor $ dsL ds
        let ds1 = ds
                { dsL = dsL1
                , dsLen = trace "DS_List.randSplit.ds1 WARNING: using slow length function" $ length dsL2
                }
        let ds2 = ds
                { dsL = dsL2
                , dsLen = trace "DS_List.randSplit.ds2 WARNING: using slow length function" $ length dsL2
                }
        return (ds1,ds2)-}
        
    takeFirst len ds = ds 
        { dsL = newL
        , dsLen = length newL
        }
        where
            newL = take len $ dsL ds 

    dropFirst len ds = ds 
        { dsL = drop len $ dsL ds 
        , dsLen = positive $ (dsLen ds)-len
        }
        where
            positive x = if x<0
                            then 0
                            else x

instance (NFData label, NFData dataType) => NFData (DS_List dataType label) where
    rnf ds = seq (rnf (dsDesc ds)) $ rnf (dsL ds)

-- instance (Binary label, Binary dataType) => Binary (DS_List dataType label) where
-- --     put lds = put (ldsDesc lds) >> put (Stream $ map (\(l,dp) -> (l,Stream dp)) $ ldsL lds) {->> put (ldLen lds)-}
--     put ds = do
--         put $ dsDesc ds
--         put $ Stream $ dsL ds
--         put $ dsLen ds
--     get = liftM3 DS_List get (liftM unstream get) get

---------------------------------------
-- test LDS_List

test_numLabels = 97
test_lenList = 10000

inflds = DS_List
    { dsDesc = DataDesc test_numLabels [0..test_numLabels-1] 500000
    , dsL = [(mod i test_numLabels, [(j,Continuous $ fromIntegral j) | j<-[1..100]]) | i <- [1..test_lenList]]
    , dsLen = test_lenList
    }
    

-------------------------------------------------------------------------------
-- IO functions

dd2intdd :: (DataDesc label) -> (DataDesc Int)
dd2intdd desc = desc { labelL = [0..numLabels desc-1] }

ds2intds :: (Ord label, Show label) => (DS_List label (LDPS label)) -> (DS_List Int (LDPS Int))
ds2intds ds = DS_List
    { dsDesc = dd2intdd $ dsDesc ds
    , dsL = map (\(label,dp)->(getIndex label, dp)) $ dsL ds
    , dsLen = dsLen ds
    }
    where
        getIndex label = case (elemIndex label $ labelL $ getDataDesc ds) of
                              Nothing -> error "stringds2intds: something awful happend"
                              Just x -> x

dd2booldd :: (DataDesc label) -> (DataDesc Bool)
dd2booldd desc = desc { numLabels=2, labelL = [True,False] }

ds2boolds :: (Ord label, Show label) => [label] -> (DS_List label (LDPS label)) -> (DS_List Bool (LDPS Bool))
ds2boolds tL ds = DS_List
    { dsDesc = dd2booldd $ dsDesc ds
    , dsL = map (\(label,dp)->(newLabel label, dp)) $ dsL ds
    , dsLen = dsLen ds
    }
    where
        newLabel label = if label `elem` tL
                            then True
                            else False

instance DataLoaderCSV (DS_List String (LDPS String)) where
    loadDataCSV filedesc = do
        ret <- loadData filedesc
        return $
            case ret of
                Left msg -> error $ show msg
                Right x -> x

-- | Lazily loads a file into a TrainingData type for use with the classification algorithms
loadData :: DatafileDesc -> IO (Either ParseError (DS_List String (LDPS String)))
loadData filedesc = liftM (liftM $ csv2data filedesc) $ loadCSV $ datafilePath filedesc

{-    do
    csveither <- loadCSV $ datafileName filedesc
    return $ liftM csv2data csveither
    return $ do
        csv <- csveither
        return csv2data-}
--         let dsL = map (\x -> (last x, list2datapoints $ init x)) csv
--         let labelL = extractLabelL dsL []
--         
--         return $ DS_List 
--             { dsDesc = DataDesc 
--                 { numLabels = length labelL
--                 , numAttr = length $ snd $ head dsL
--                 } 
--             , dsL = dsL
--             , dsLen = length dsL 
--             , dsLabelL = labelL
--             }

csv2data :: DatafileDesc -> [[String]] -> (DS_List String (LDPS String))
csv2data filedesc csv = DS_List 
    { dsDesc = DataDesc 
        { numLabels = length labelL
        , labelL = labelL
        , numAttr = length $ snd $ head dsL
        } 
    , dsL = dsL
    , dsLen = length dsL 
    }
    where
        dsL = map csv2ldps csv
        csv2ldps row = case datafileLabelColumn filedesc of
            LastC -> (last row, list2datapoints (datafileMissingStr filedesc) $ init row)
            FirstC -> (head row, list2datapoints (datafileMissingStr filedesc) $ tail row)
            IndexC x -> error "csv2data: Index x not implemented"
        labelL = extractLabelL dsL []

list2datapoints :: Maybe String -> [String] -> [(Int,DataItem)]
list2datapoints missingStr xs = mapi (\i x -> (i,format x)) xs
    where 
        format str = 
            case readMay str of
                 Just x  -> Continuous x
                 Nothing -> if Just str==missingStr
                               then Missing
                               else Discrete str

mapi :: (Int -> a -> b) -> [a] -> [b]
mapi = go 0
    where
        go itr f []     = []
        go itr f (x:xs) = (f itr x):(go (itr+1) f xs)


extractLabelL :: (Eq label) => [(label,dp)] -> [label] -> [label]
extractLabelL []              labelL = labelL
extractLabelL ((label,dp):xs) labelL = 
    if label `elem` labelL
        then extractLabelL xs labelL
        else extractLabelL xs (label:labelL)

---------------

-- csv2data :: Maybe String -> Maybe [String -> DataItem] -> [[String]] -> TrainingData String
-- csv2data missingStr Nothing   csv = csv2dataAuto missingStr csv
-- csv2data missingStr (Just fs) csv = error "forced value types not implemented"
-- -- csv2data missingStr (Just fs) csv = csv2dataForce missingStr fs csv
-- 
-- csv2dataAuto :: Maybe String -> [[String]] -> TrainingData String
-- csv2dataAuto missingStr csv = map (\dp -> (last dp, map cell2sql $ init dp)) csv
--     where 
--         cell2sql x = 
--             if Just x==missingStr
--                then Missing
--                else case (reads x::[(Double,String)]) of
--                         []     -> toDataItem (x::String)
--                         (x:xs) -> toDataItem $ fst x

-- csv2dataForce :: Maybe String -> [String -> DataItem] -> [[String]] -> TrainingData String
-- csv2dataForce missingStr fs csv = 
--     [(last line,
--         [ if Just cell==missingStr
--              then Missing
--              else f cell
--         | (f,cell) <- zip fs $ init line
--         ])
--     | line <- csv
--     ]