Frames-map-reduce-0.1.0.0: examples/AddRowsByLabel.hs
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Frames.MapReduce as FMR
import qualified Frames.Folds as FF
import qualified Frames as F
import qualified Data.Vinyl as V
import qualified Data.List as L
import qualified Data.Text as T
import qualified Control.Foldl as FL
import Data.Monoid ( Sum )
import System.Random ( newStdGen
, randomRs
)
-- Create types for the cols
type Label = "label" F.:-> T.Text
type Y = "y" F.:-> Double
type X = "x" F.:-> Double
type AllCols = [Label,Y,X]
-- filter, leaving only rows with labels 'A', 'B' or 'C'
unpack = FMR.unpackFilterOnField @Label (`elem` ["A", "B", "C"])
-- group the rest of the cols by Label
assign = FMR.splitOnKeys @'[Label]
-- sum the data columns and then re-attach the key
reduce = FMR.foldAndAddKey $ (FF.foldAllConstrained @Num @'[Y, X]) FL.sum
-- put it all together: filter, group by label, sum the data cols and re-attach the key.
-- Then turn the resulting list of Frames (each with only one Record in this case)
-- into one Frame via (<>).
mrFold = FMR.concatFold $ FMR.mapReduceFold unpack assign reduce
main :: IO ()
main = do
f <- createFrame 1000
let result = FMR.fold mrFold f
putStrLn $ (L.intercalate "\n" $ fmap show $ FL.fold FL.list result)
{- Output
{label :-> "A", y :-> 1293.6893073755323, x :-> 1386.4314446405742}
{label :-> "B", y :-> 1940.9402110282622, x :-> 2244.645291592506}
{label :-> "C", y :-> 2009.8541388288395, x :-> 2128.7190606123568}
-}
--- create the Frame
createFrame :: Int -> IO (F.FrameRec AllCols)
createFrame n = do
g <- newStdGen
let randLabels = L.take n $ randomRs ('A', 'Z') g
randDbls = L.take (2 * n) $ randomRs (0.0, 100.0) g
oneRow m =
T.singleton (randLabels !! m)
F.&: (randDbls !! m)
F.&: (randDbls !! (n + m))
F.&: V.RNil
return $ F.toFrame $ fmap oneRow [0 .. (n - 1)]