{-# language OverloadedStrings #-} {-# language FlexibleInstances #-} {-# language DeriveFunctor, DeriveFoldable, DeriveTraversable, GeneralizedNewtypeDeriving #-} {-# language ConstraintKinds #-} {-# OPTIONS_GHC -Wno-unused-top-binds #-} {-# OPTIONS_GHC -Wno-type-defaults #-} ----------------------------------------------------------------------------- -- | -- Module : Core.Data.Frame.List -- Description : List-based dataframe -- Copyright : (c) Marco Zocca (2018-2019) -- License : BSD-style -- Maintainer : ocramz fripost org -- Stability : experimental -- Portability : GHC -- -- A general-purpose, row-oriented data frame, encoded as a list of rows -- ----------------------------------------------------------------------------- module Core.Data.Frame.List ( -- * Frame Frame, -- ** Construction frameFromList, -- ** Access Core.Data.Frame.List.head, Core.Data.Frame.List.take, Core.Data.Frame.List.drop, Core.Data.Frame.List.zipWith, numRows, -- ** Filtering Core.Data.Frame.List.filter, -- *** 'D.Decode'-based filtering helpers filterA, -- ** groupWith, -- ** Scans (row-wise cumulative operations) Core.Data.Frame.List.scanl, Core.Data.Frame.List.scanr, -- ** Vector-related toVector, fromVector, -- *** Sorting ) where import qualified Control.Monad as CM (filterM) import Data.List (groupBy) import qualified Data.Vector as V -- | A 'Frame' is a list of rows. newtype Frame row = Frame { tableRows :: [row] } deriving (Show, Functor, Foldable, Traversable) head :: Frame row -> row head = Prelude.head . tableRows -- | Retain n rows take :: Int -> Frame row -> Frame row take n = Frame . Prelude.take n . tableRows -- | Drop n rows drop :: Int -> Frame row -> Frame row drop n = Frame . Prelude.drop n . tableRows zipWith :: (a -> b -> c) -> Frame a -> Frame b -> Frame c zipWith f x y = frameFromList $ Prelude.zipWith f (tableRows x) (tableRows y) frameFromList :: [row] -> Frame row frameFromList = Frame toList :: Frame row -> [row] toList = tableRows numRows :: Frame row -> Int numRows = length . tableRows filter :: (row -> Bool) -> Frame row -> Frame row filter p = Frame . Prelude.filter p . tableRows -- | This generalizes the list-based 'filter' function. filterA :: Applicative f => (row -> f Bool) -> Frame row -> f (Frame row) filterA fm t = frameFromList <$> CM.filterM fm (toList t) -- | Left-associative scan scanl :: (b -> a -> b) -> b -> Frame a -> Frame b scanl f z tt = Frame $ Prelude.scanl f z (tableRows tt) -- | Right-associative scan scanr :: (a -> b -> b) -> b -> Frame a -> Frame b scanr f z tt = Frame $ Prelude.scanr f z (tableRows tt) -- | 'groupWith' takes row comparison function and a list and returns a list of lists such that the concatenation of the result is equal to the argument. Moreover, each sublist in the result contains only elements that satisfy the comparison. groupWith :: (row -> row -> Bool) -> Frame row -> [Frame row] groupWith f t = Frame <$> groupBy f (tableRows t) -- | Produce a 'Vector' of rows toVector :: Frame row -> V.Vector row toVector = V.fromList . tableRows -- | Produce a Frame from a 'Vector' of rows fromVector :: V.Vector row -> Frame row fromVector = frameFromList . V.toList