packages feed

HQu-0.0.0.0: src/Q/Stats/TimeSeries.hs

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes        #-}

module Q.Stats.TimeSeries where
import qualified Data.ByteString.Lazy     as B
import           Data.Csv                 ((.:))
import qualified Data.Csv                 as Csv
import qualified Data.Map                 as M
import           Data.Maybe               (fromJust)
import qualified Data.Text                as T
import           Data.Time                (Day, LocalTime (LocalTime), midnight)
import           Data.Time.Format         ()
import           Data.Time.Format.ISO8601 (FormatExtension (BasicFormat),
                                           calendarFormat, formatParseM,
                                           formatShow, localTimeFormat,
                                           timeOfDayFormat)
import           Data.Vector              (Vector, toList)
import           GHC.Generics             (Generic)
-- A single data point with a time and value.
data DataPoint a b = DataPoint {
    dpT :: a  -- ^Time
  , dpV :: b  -- ^Value
  } deriving (Generic, Show, Eq, Ord)

{-|
Read a a csv row with 2 columns: `date,value` where `date` is
in shortened iso format. (with our without time)
-}
instance Csv.FromNamedRecord (DataPoint LocalTime Double) where
  parseNamedRecord m = DataPoint
      <$> fmap (fromJust . parseDateTime) (m .: "date")
      <*> (m .: "value")

{-|
Read a a csv row with 2 columns: `date,value` where `date` is
in year fractions.
-}
instance Csv.FromNamedRecord (DataPoint Double Double) where
  parseNamedRecord m = DataPoint
      <$> (m .: "date")
      <*> (m .: "value")


parseDateTime :: String -> Maybe LocalTime
parseDateTime iso_datetime =
  if length iso_datetime == 8 then
    parseDay iso_datetime
  else
    formatParseM localTimeFormat' iso_datetime

localTimeFormat' = localTimeFormat (calendarFormat BasicFormat) (timeOfDayFormat BasicFormat)
dayFormat' = calendarFormat BasicFormat

parseTime :: String -> Maybe LocalTime
parseTime = formatParseM localTimeFormat'

parseDay :: String -> Maybe LocalTime
parseDay iso_date = do
  day <- formatParseM dayFormat' iso_date
  return $ LocalTime day midnight

dayToString :: Day -> T.Text
dayToString = T.pack . formatShow dayFormat'

dateToString :: LocalTime -> String
dateToString = formatShow (localTimeFormat (calendarFormat BasicFormat) (timeOfDayFormat BasicFormat))

read :: forall a. (Csv.FromNamedRecord a) => FilePath -> IO [a]
read f = do
  s <- B.readFile f
  let records = Csv.decodeByName s
  case records of (Left s)               -> fail s
                  (Right (header, rows)) -> return $ toList rows



valuesOnly :: [DataPoint a b] -> [b]
valuesOnly = fmap dpV

toPair (DataPoint d v) = (d, v)