packages feed

timeseries 0.1.0 → 0.2.0

raw patch · 15 files changed

+197/−111 lines, 15 filesdep +text-timedep −bytestring-time

Dependencies added: text-time

Dependencies removed: bytestring-time

Files

CHANGES.md view
@@ -1,3 +1,13 @@+# Version 0.2 (2016-12-15)++  * Renamed function to load CSV file from **load** to **loadCSV**+  * Added toList function to the series+  * Implemented CSV Writer+  * Time Series is instance of Functor+  * Time Series is instance of Foldable+  * Implemented mean+  + # Version 0.1 (2016-12-13)    * Implemented functions
README.md view
@@ -1,12 +1,11 @@-[![Build Status](https://travis-ci.org/klangner/time-series-lib.svg?branch=master)](https://travis-ci.org/klangner/time-series-lib)+[![Build Status](https://travis-ci.org/klangner/timeseries.svg?branch=master)](https://travis-ci.org/klangner/timeseries) [![Hackage](https://img.shields.io/hackage/v/timeseries.svg)](https://hackage.haskell.org/package/timeseries)  # Welcome to Haskell Time Series library  Implemented functionality:   * Base operations on time series (get value, slice, max etc.)-  * Reading from CSV file-+  * Reading and writing from CSV file   ## Installation@@ -24,13 +23,13 @@ and other improvements.  Please report bugs via the-[github issue tracker](http://github.com/klangner/time-series-lib/issues).+[github issue tracker](http://github.com/klangner/timeseries/issues). -Master [git repository](http://github.com/klangner/time-series-lib):+Master [git repository](http://github.com/klangner/timeseries): -* `git clone https://github.com/klangner/time-series-lib.git`+* `git clone https://github.com/klangner/timeseries.git`   # Redistributing -time-series-lib source code is distributed under the BSD3 License.+timeseries source code is distributed under the BSD3 License.
benchmark/Benchmarks.hs view
@@ -1,10 +1,16 @@  import Criterion.Main+import Control.DeepSeq import Data.Time (UTCTime) import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Data.Text.Time (parseISODateTime) import qualified Data.TimeSeries as TS  +instance NFData a => NFData (TS.Series a) where+    rnf xs = rnf (TS.toList xs)++ seriesSize :: Integer seriesSize = 10^6 @@ -14,15 +20,19 @@ lastIndex :: UTCTime lastIndex = index (seriesSize - 1) -bigSeries :: TS.Series+bigSeries :: TS.Series Double bigSeries = TS.tsSeries [1..seriesSize] [1..]   main :: IO () main = defaultMain-    [ bgroup "Big series"+    [ bgroup "Basic operations"         [ bench "size"  $ nf TS.size bigSeries         , bench "valueAt"  $ nf (\xs -> TS.valueAt xs lastIndex) bigSeries         , bench "slice"  $ nf (\xs -> TS.valueAt (TS.slice xs (index 2) lastIndex) (index 1)) bigSeries+        , bench "max"  $ nf maximum bigSeries+        , bench "fmap"  $ nf (fmap (+ 2)) bigSeries         ]+    , bgroup "IO"+        [ bench "load CSV" $ nfIO (TS.loadCSV TS.NoHeader parseISODateTime "../testdata/test-100K.csv") ]     ]
src/Data/TimeSeries.hs view
@@ -1,9 +1,13 @@ -- | TimeSeries library module Data.TimeSeries     ( module Data.TimeSeries.Series-    , module Data.TimeSeries.CSVReader+    , module Data.TimeSeries.Stats+    , module Data.TimeSeries.IO.CSVReader+    , module Data.TimeSeries.IO.CSVWriter     ) where   import Data.TimeSeries.Series-import Data.TimeSeries.CSVReader+import Data.TimeSeries.Stats+import Data.TimeSeries.IO.CSVReader+import Data.TimeSeries.IO.CSVWriter
− src/Data/TimeSeries/CSVReader.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.TimeSeries.CSVReader-    ( load-    )where---import qualified Data.ByteString.Lazy as BS-import qualified Data.Text as T-import           Data.Csv-import qualified Data.Vector as V-import           Data.Time (UTCTime)--import           Data.TimeSeries.Series ( Series-                                        , emptySeries-                                        , series-                                        )----- | Load data from CSV file and create Time Series from it--- As a first argument provide function to convert date from ByteString to UTCTime-load :: (T.Text -> UTCTime) -> FilePath -> IO Series-load ft filePath = do-    csvData <- BS.readFile filePath-    case decode HasHeader csvData of-        Left err -> do-            _ <- putStrLn err-            return emptySeries--        Right vs -> do-            let vs' = V.map (parseLine ft) vs-            return $ series (V.toList vs')---parseLine :: (T.Text -> UTCTime) -> (T.Text, Double) -> (UTCTime, Double)-parseLine ft (date, value) = (ft date, value)
+ src/Data/TimeSeries/IO/CSVReader.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}++module Data.TimeSeries.IO.CSVReader+    ( HasHeader(..)+    , loadCSV+    )where+++import qualified Data.ByteString.Lazy as BS+import qualified Data.Text as T+import qualified Data.Csv as CSV+import qualified Data.Vector as V+import           Data.Time (UTCTime)++import           Data.TimeSeries.Series ( Series+                                        , emptySeries+                                        , series+                                        )++data HasHeader = HasHeader | NoHeader++-- | Load data from CSV file and create Time Series from it+-- As a first argument provide function to convert date from ByteString to UTCTime+loadCSV :: HasHeader -> (T.Text -> UTCTime) -> FilePath -> IO (Series Double)+loadCSV hasHeader ft filePath = do+    csvData <- BS.readFile filePath+    case CSV.decode (toCSVHeader hasHeader) csvData of+        Left err -> do+            _ <- putStrLn err+            return emptySeries++        Right vs -> do+            let vs' = V.map (parseLine ft) vs+            return $ series (V.toList vs')+++toCSVHeader :: HasHeader -> CSV.HasHeader+toCSVHeader HasHeader = CSV.HasHeader+toCSVHeader _ = CSV.NoHeader++parseLine :: (T.Text -> UTCTime) -> (T.Text, Double) -> (UTCTime, Double)+parseLine ft (date, value) = (ft date, value)
+ src/Data/TimeSeries/IO/CSVWriter.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.TimeSeries.IO.CSVWriter( saveCSV ) where+++import Prelude hiding (writeFile)+import Data.Csv+import Data.ByteString.Lazy (writeFile)+import Data.Text.Time ( formatISODateTime )++import Data.TimeSeries.Series ( Series+                              , toList )+++-- | Load data from CSV file and create Time Series from it+-- As a first argument provide function to convert date from ByteString to UTCTime+saveCSV :: Series Double -> FilePath -> IO ()+saveCSV ts filePath = do+    let rs = map (\(x, y) -> (formatISODateTime x, y)) (toList ts)+    let rs' = encode rs+    writeFile filePath rs'
src/Data/TimeSeries/Series.hs view
@@ -11,15 +11,14 @@ module Data.TimeSeries.Series     ( DataPoint     , Series-    , Value     , emptySeries-    , max-    , min     , series     , size     , slice+    , toList     , tsSeries     , valueAt+    , values     ) where  import Prelude hiding (max, min)@@ -27,21 +26,36 @@ import Data.Time.Clock.POSIX (posixSecondsToUTCTime)  -type Value = Double-data DataPoint = DP !UTCTime !Value+-- | Data points is single index value od time series+data DataPoint a = DP !UTCTime a                  deriving (Show, Eq) -data Series = Series [DataPoint]+instance Functor DataPoint where+    fmap f (DP x y) = DP x (f y)++instance Foldable DataPoint where+    foldMap f (DP _ y) = f y++-- | Data structure for holding Series.+-- Implementation should be hidden so it can be changed in the future+data Series a = Series [DataPoint a]     deriving (Show, Eq) +instance Functor Series where+    fmap f (Series xs) = Series (map (fmap f) xs) +instance Foldable Series where+    foldMap f (Series xs) = foldMap (foldMap f) xs+    length = size++ -- | Create empty series-emptySeries :: Series+emptySeries :: Series a emptySeries = Series []  --- | Create empty series-series :: [(UTCTime, Double)] -> Series+-- | Create series+series :: [(UTCTime, a)] -> Series a series xs = Series $ map (\(x, y) -> DP x y) xs  @@ -49,17 +63,27 @@ -- -- >seriesFromSeconds [1, 2, 3] [41.3, 52.22, 3.0] == Series [DP 1970-01-01 00:00:01 UTC 2.3,DP 1970-01-01 00:00:02 UTC 4.5] ---tsSeries :: [Integer] -> [Value] -> Series+tsSeries :: [Integer] -> [a] -> Series a tsSeries ts vs = Series (zipWith DP idx vs)     where idx = map (posixSecondsToUTCTime . fromIntegral) ts  +-- | Convert Time Series to list+toList :: Series a -> [(UTCTime, a)]+toList (Series xs) = map (\(DP x y) -> (x, y)) xs+++-- | Get only series values+values :: Series a -> [a]+values ts = map (\(_, y) -> y) (toList ts)++ -- | Get series size. -- Complexity O(n) -- -- >size (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) == 3 ---size :: Series -> Int+size :: Series a -> Int size (Series xs) = length xs  @@ -69,10 +93,10 @@ -- >valueAt (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) 2 == Just 52.22 -- >valueAt (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) 5 == Nothing ---valueAt :: Series -> UTCTime -> Maybe Value+valueAt :: Series a -> UTCTime -> Maybe a valueAt (Series xs) ts = safeHead [y | DP x y <- xs, x == ts]     where safeHead [] = Nothing-          safeHead (a:_) = Just a+          safeHead (i:_) = Just i   -- | Return series subset@@ -81,27 +105,7 @@ -- >slice (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) 2 3 == Series [DP 2 52.22, DP 3 3.0] -- >slice (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) 5 23 == Series [] ---slice :: Series -> UTCTime -> UTCTime -> Series+slice :: Series a -> UTCTime -> UTCTime -> Series a slice (Series xs) start end = Series [DP x y | DP x y <- xs, x >= start && x <= end]  --- | Return maximum value in the series--- Complexity O(n)------ >max (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) == 52.22--- >max (Series []) == 0----max :: Series -> Value-max (Series xs) = foldl (\d (DP _ y) -> max' d y) 0.0 xs-    where max' a b = if a > b then a else b---- | Return maximum value in the series--- Complexity O(n)------ >min (Series [DP 1 41.3, DP 2 52.22, DP 3 3.0]) == 3.0--- >min (Series []) == 0----min :: Series -> Value-min (Series xs) = foldl (\d (DP _ y) -> min' d y) maxValue xs-    where min' a b = if a < b then a else b-          maxValue = fromIntegral (maxBound :: Int)
+ src/Data/TimeSeries/Stats.hs view
@@ -0,0 +1,14 @@++module Data.TimeSeries.Stats+    ( mean+    ) where++import Prelude hiding (max, min)+import Data.TimeSeries.Series (Series)+++-- | Return maximum value in the series+mean :: Fractional a => Series a -> a+mean xs = sum xs / fromIntegral (length xs)+-- mean xs = (fromIntegral (length xs))+
− test-src/Data/TimeSeries/CSVSpec.hs
@@ -1,17 +0,0 @@-module Data.TimeSeries.CSVSpec (spec) where--import Test.Hspec-import Data.ByteString.Time (parseISODateTime)--import qualified Data.TimeSeries as TS-import qualified Data.TimeSeries.CSVReader as CSV---spec :: Spec-spec = do--  describe "Reader" $ do--    it "co2 test data" $ do-        xs <- CSV.load parseISODateTime "testdata/co2.csv"-        TS.size xs `shouldBe` 192
+ test-src/Data/TimeSeries/IO/CSVSpec.hs view
@@ -0,0 +1,17 @@+module Data.TimeSeries.IO.CSVSpec (spec) where++import Test.Hspec+import Data.Text.Time (parseISODateTime)++import qualified Data.TimeSeries as TS+import qualified Data.TimeSeries.IO.CSVReader as CSV+++spec :: Spec+spec = do++  describe "Reader" $ do++    it "co2 test data" $ do+        xs <- CSV.loadCSV CSV.HasHeader parseISODateTime "testdata/co2.csv"+        TS.size xs `shouldBe` 192
test-src/Data/TimeSeries/SeriesSpec.hs view
@@ -1,7 +1,7 @@ module Data.TimeSeries.SeriesSpec (spec) where  import Test.Hspec-import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds)  import qualified Data.TimeSeries as TS @@ -16,6 +16,12 @@         let values = [1.0, 2.0, 3.0]         TS.size (TS.tsSeries idx values) `shouldBe` 3 +    it "toList" $ do+        let idx = [1, 2, 3]+        let values = [1.0, 2.0, 3.0]+        let xs = TS.toList (TS.tsSeries idx values)+        map (\(x, y) -> (utcTimeToPOSIXSeconds x, y)) xs `shouldBe` [(1, 1.0), (2, 2.0), (3, 3.0)]+     it "return data point value at given index" $ do         let idx = [10, 20, 30]         let values = [10.0, 12.0, 32.4]@@ -40,10 +46,16 @@         let end = posixSecondsToUTCTime $ fromIntegral 34         TS.slice xs start end `shouldBe` TS.emptySeries +    it "map over series" $ do+        let xs = TS.tsSeries [1..] [10.0, 1.2, 32.4, 0.65, 11.0]+        let ys = fmap (+ 2) xs+        (TS.values ys)`shouldBe` [12.0, 3.2, 34.4, 2.65, 13.0]+     it "maximum value" $ do-        let xs = TS.tsSeries [1..5] [10.0, 1.2, 32.4, 0.65, 11.0]-        TS.max xs `shouldBe` 32.4+        let xs = TS.tsSeries [1..] [10.0, 1.2, 32.4, 0.65, 11.0]+        maximum xs `shouldBe` 32.4      it "minimum value" $ do         let xs = TS.tsSeries [1..5] [10.0, 1.2, 32.4, 0.65, 11.0]-        TS.min xs `shouldBe` 0.65+        minimum xs `shouldBe` 0.65+
test-src/Spec.hs view
@@ -1,9 +1,11 @@ import Test.Hspec import qualified Data.TimeSeries.SeriesSpec-import qualified Data.TimeSeries.CSVSpec+import qualified Data.TimeSeries.StatsSpec+import qualified Data.TimeSeries.IO.CSVSpec   main :: IO () main = hspec $ do   describe "Series" Data.TimeSeries.SeriesSpec.spec-  describe "CSV" Data.TimeSeries.CSVSpec.spec+  describe "Statistcs" Data.TimeSeries.StatsSpec.spec+  describe "CSV" Data.TimeSeries.IO.CSVSpec.spec
+ testdata/test-100K.csv view

file too large to diff

timeseries.cabal view
@@ -1,5 +1,5 @@ name:           timeseries-version:        0.1.0+version:        0.2.0 synopsis:       Library for Time Series processing License:        BSD3 License-file:   LICENSE@@ -31,16 +31,18 @@   build-depends:                     base >= 4.7 && < 5,                     bytestring < 0.11,-                    bytestring-time < 1,                     cassava >= 0.4 && < 0.5,                     text < 2,+                    text-time < 1,                     time >= 1.5 && < 2,                     vector < 0.12   exposed-modules:                     Data.TimeSeries   other-modules:                     Data.TimeSeries.Series,-                    Data.TimeSeries.CSVReader+                    Data.TimeSeries.Stats,+                    Data.TimeSeries.IO.CSVReader+                    Data.TimeSeries.IO.CSVWriter  test-suite unit-tests   type:             exitcode-stdio-1.0@@ -52,17 +54,19 @@                     hspec >=2 && <3,                     QuickCheck >=2.6 && <3,                     bytestring < 0.11,-                    bytestring-time < 1,                     cassava >= 0.4 && < 0.5,                     text < 2,+                    text-time < 1,                     time >= 1.5 && < 2,                     vector < 0.12   other-modules:                     Data.TimeSeries,                     Data.TimeSeries.Series,+                    Data.TimeSeries.Stats,                     Data.TimeSeries.SeriesSpec-                    Data.TimeSeries.CSVReader-                    Data.TimeSeries.CSVSpec+                    Data.TimeSeries.IO.CSVReader+                    Data.TimeSeries.IO.CSVWriter+                    Data.TimeSeries.IO.CSVSpec   hs-source-dirs:                     src,                     test-src