packages feed

integreat-0.2.2.1: src/Load.hs

{- Load
Gregory W. Schwartz

Collections the functions pertaining to the loading and unification of data
sets.
-}

{-# LANGUAGE BangPatterns #-}

module Load
    ( datasToEntities
    , entitiesToLevels
    , unifyAllLevels
    , filterEntities
    , filterEntitiesStdDev
    , getIDVec
    , getIDMap
    , defVertexSimMap
    , vertexCsvToLevels
    , standardizeLevel
    , getPremadeNetworks
    ) where

-- Standard
import Data.List
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
import qualified Data.IntMap.Strict as IMap
import qualified Data.Foldable as F
import Data.Function (on)

-- Cabal
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Storable as VS
import qualified Data.Text as T
import Data.Graph.Inductive
import qualified Control.Lens as L
import Numeric.LinearAlgebra
import Statistics.Sample

-- Local
import Types
import Utility

-- | Convert data entries to entities.
datasToEntities :: [DataEntry] -> [Entity]
datasToEntities = fmap dataEntryToEntity

-- | Convert a vertex entry to an entity.
dataEntryToEntity :: DataEntry -> Entity
dataEntryToEntity x = Entity { _entityID    = ID $ vertex x
                             , _dataSetName = DataSetName $ dataReplicate x
                             , _levelName   = LevelName $ dataLevel x
                             , _entityValue = intensity x
                             }

-- | Convert a collection of entities to levels.
entitiesToLevels :: [Entity] -> [(LevelName, Level)]
entitiesToLevels = fmap (L.over L._2 Level)
                 . Map.toAscList
                 . Map.unionsWith (Map.unionWith Map.union)
                 . fmap (\ !x -> Map.singleton
                                    (_levelName x)
                                    ( Map.singleton
                                        (_entityID x)
                                        (Map.singleton (_dataSetName x) x)
                                    )
                        )

-- | Combine all levels together to get a unique list of IDs.
unifyAllLevels :: [Level] -> UnifiedData
unifyAllLevels = UnifiedData . Map.unionsWith Map.union . fmap unLevel

-- | Filter entities that appear in less than the specified amount.
filterEntities :: NumSamples -> [Entity] -> [Entity]
filterEntities (NumSamples numSamples) = mconcat
                                       . filter ((>= numSamples) . length)
                                       . groupBy ((==) `on` _entityID)
                                       . sortBy (compare `on` _entityID)

-- | Filter entities that appear in less than the specified amount.
filterEntitiesStdDev :: StdDevThreshold
                     -> [(LevelName, Level)]
                     -> [(LevelName, Level)]
filterEntitiesStdDev (StdDevThreshold threshold) levels =
    fmap filterGood levels
  where
    filterGood   = L.over
                    L._2
                    ( Level
                    . Map.filterWithKey (\k _ -> Set.member k goodEntities)
                    . unLevel
                    )
    goodEntities = Set.unions
                 . fmap (Set.fromList . Map.keys . Map.filter p . unLevel . snd)
                 $ levels
    p            =
        (>= threshold) . stdDev . VU.fromList . fmap _entityValue . Map.elems

-- | Get a vector of all IDs for easy indexing.
getIDVec :: UnifiedData -> IDVec
getIDVec =
    IDVec . (\x -> V.fromList . Map.keys $ x) . unUnifiedData

-- | Get a map of all IDs for conversion.
getIDMap :: UnifiedData -> IDMap
getIDMap = IDMap
         . Map.fromList
         . V.toList
         . V.imap (\i v -> (v, i))
         . unIDVec
         . getIDVec

-- | The default vertex similarity values using just a diagonal of 1 matrix
-- (same entities are 1, different are 0).
defVertexSimMap :: Size -> [LevelName] -> VertexSimMap
defVertexSimMap (Size size) xs = VertexSimMap
                               . Map.fromListWith Map.union
                               . fmap ( \((!x, !y), !z)
                                     -> (x, Map.singleton y z)
                                      )
                               . flip zip (repeat defSimVals)
                               $ (,) <$> xs <*> xs
  where
    defSimVals = VertexSimValues
               . zipWith3
                    (\ !x !y !z -> ((x, y), z))
                    [0..(size - 1)]
                    [0..(size - 1)]
               $ repeat (1 :: Double)

-- | Convert a list of vertex entries to a vertex similarity map by joining
-- together all like comparisons and converting to those values into
-- matrices.
vertexCsvToLevels :: IDMap -> [VertexEntry] -> VertexSimMap
vertexCsvToLevels (IDMap idMap) =
    VertexSimMap
        . (Map.map . Map.map) ( VertexSimValues
                              . F.toList
                              )
        . Map.unionsWith (Map.unionWith (Seq.><))
        . fmap toLevelSimMap
  where
    toLevelSimMap x = Map.singleton
                        (LevelName $ vertexLevel1 x)
                        ( Map.singleton (LevelName $ vertexLevel2 x)
                                        ( Seq.singleton ( ( idLookup $ vertex1 x
                                                          , idLookup $ vertex2 x
                                                          )
                                                        , similarity x
                                                        )
                                        )
                        )
    idLookup x =
        lookupWithError ("ID: " ++ T.unpack x ++ " not found.") (ID x) idMap

-- | Convert a level's IDs to be integers based on the universal labeling.
standardizeLevel :: IDMap -> Level -> StandardLevel
standardizeLevel (IDMap idMap) (Level level) =
    StandardLevel
        . Map.map (flip standardizeDataSets standardDataSets)
        . Map.mapKeys (\ !x -> (x, lookupWithError (keyNotFound x) x idMap))
        $ level
  where
    standardDataSets = getStandardDataSets . Level $ level
    keyNotFound k    = "ID: " ++ show k ++ " not found."

-- | Unify the ordering of the data for data set entries.
getStandardDataSets :: Level -> StandardDataSets
getStandardDataSets = StandardDataSets
                    . Seq.fromList
                    . Set.toList
                    . Set.fromList
                    . concatMap Map.keys
                    . Map.elems
                    . unLevel

-- | Unify the ordering of the data for data set entries.
standardizeDataSets :: Map.Map DataSetName Entity
                    -> StandardDataSets
                    -> (Seq.Seq (Maybe Entity))
standardizeDataSets m = fmap (flip Map.lookup m) . unStandardDataSets

-- | Get a vector of all IDs from pre-made networks.
getPremadeIDVec :: [String] -> IDVec
getPremadeIDVec = IDVec . V.fromList . fmap (ID . T.pack)

-- | Get a map of all IDs for conversion. Assumes the IDs are already ordered
-- correctly.
getPremadeIDMap :: [String] -> IDMap
getPremadeIDMap xs =
    IDMap . Map.fromList . flip zip (fmap read xs) . fmap (ID . T.pack) $ xs

-- | Get the graph from a list of edges. Assumes the IDs integers,
-- starting at 0.
getPremadeGr :: IDMap -> [(String, String, Double)] -> LevelGr
getPremadeGr (IDMap idMap) =
    LevelGr
        . undir
        . mkGraph (zip [0..Map.size idMap] [0..Map.size idMap])
        . fmap (\(!x, !y, !z) -> (read x, read y, z))

-- | Get the edge matrix from a list of edges. Assumes the IDs integers,
-- starting at 0.
getPremadeEdgeSimMatrix :: IDMap -> [(String, String, Double)] -> EdgeSimMatrix
getPremadeEdgeSimMatrix (IDMap idMap) =
    EdgeSimMatrix
        . IMap.unionsWith IMap.union
        . fmap (\ (!x, !y, !z) -> IMap.singleton
                                    (read x)
                                    (IMap.singleton (read y) z)
               )

-- | Get the pre-made level networks for use with integration. We assume that
-- the IDs are integers, starting at 0, with everything in order.
getPremadeNetworks
    :: (Maybe [String], [([String], [(String, String, Double)])])
    -> (Maybe (Set.Set ID), Maybe UnifiedData, IDMap, IDVec, VertexSimMap, EdgeSimMap, GrMap)
getPremadeNetworks (!changedVerticesString, !allNetworks) =
    (changedVertices, Nothing, idMap, idVec, vertexSimMap, edgeSimMap, grMap)
  where
    changedVertices =
        fmap (Set.fromList . fmap (ID . T.pack)) $ changedVerticesString
    grMap        = GrMap
                 . Map.fromList
                 . zip levelNames
                 . fmap (getPremadeGr idMap)
                 . fmap snd
                 $ allNetworks
    edgeSimMap   = EdgeSimMap
                 . Map.fromList
                 . zip levelNames
                 . fmap (getPremadeEdgeSimMatrix idMap)
                 . fmap snd
                 $ allNetworks
    vertexSimMap =
        defVertexSimMap (Size . Map.size . unIDMap $ idMap) levelNames
    levelNames   = fmap (LevelName . T.pack . show) [0..length allNetworks - 1]
    idMap        = getPremadeIDMap allVertices
    idVec        = getPremadeIDVec allVertices
    allVertices  = concatMap fst allNetworks