packages feed

plot-light 0.1.0.0 → 0.1.0.1

raw patch · 10 files changed

+323/−59 lines, 10 filesdep +attoparsec-timedep −vectordep ~basenew-component:exe:plot-light

Dependencies added: attoparsec-time

Dependencies removed: vector

Dependency ranges changed: base

Files

+ app/timeseries/Main.hs view
@@ -0,0 +1,15 @@+module Main where++import Graphics.Rendering.Plot.Light.IO.Text++import qualified Data.Attoparsec.Text as A+import qualified Data.Text.IO as T+++fname = "data/forex"++main = do+  d <- T.readFile fname+  let pd = A.parseOnly parseFxDataset d+  case pd of Left e -> error e+             Right datarows -> pure datarows
+ data/forex view
@@ -0,0 +1,174 @@+2017-06-26 21:00:00,1.0876,1.0880,1.0872,1.0874+2017-06-26 20:00:00,1.0873,1.0875,1.0872,1.0875+2017-06-26 19:00:00,1.0874,1.0875,1.0873,1.0874+2017-06-26 18:00:00,1.0870,1.0875,1.0870,1.0875+2017-06-26 17:00:00,1.0873,1.0873,1.0870,1.0870+2017-06-26 16:00:00,1.0869,1.0873,1.0867,1.0873+2017-06-26 15:00:00,1.0873,1.0876,1.0869,1.0870+2017-06-26 14:00:00,1.0875,1.0877,1.0871,1.0873+2017-06-26 13:00:00,1.0882,1.0883,1.0875,1.0875+2017-06-26 12:00:00,1.0882,1.0888,1.0880,1.0885+2017-06-26 11:00:00,1.0881,1.0882,1.0879,1.0881+2017-06-26 10:00:00,1.0881,1.0883,1.0879,1.0881+2017-06-26 09:00:00,1.0864,1.0879,1.0862,1.0879+2017-06-26 08:00:00,1.0858,1.0866,1.0858,1.0862+2017-06-26 07:00:00,1.0860,1.0862,1.0857,1.0860+2017-06-26 06:00:00,1.0855,1.0859,1.0854,1.0859+2017-06-26 05:00:00,1.0857,1.0859,1.0856,1.0856+2017-06-26 04:00:00,1.0858,1.0859,1.0857,1.0857+2017-06-26 03:00:00,1.0855,1.0859,1.0855,1.0858+2017-06-26 02:00:00,1.0856,1.0857,1.0855,1.0855+2017-06-26 01:00:00,1.0855,1.0858,1.0855,1.0856+2017-06-26 00:00:00,1.0856,1.0856,1.0855,1.0856+2017-06-25 23:00:00,1.0855,1.0856,1.0854,1.0855+2017-06-25 22:00:00,1.0855,1.0857,1.0851,1.0855+2017-06-25 21:00:00,1.0859,1.0863,1.0851,1.0855+2017-06-23 20:00:00,1.0855,1.0858,1.0852,1.0852+2017-06-23 19:00:00,1.0852,1.0855,1.0851,1.0855+2017-06-23 18:00:00,1.0854,1.0856,1.0852,1.0852+2017-06-23 17:00:00,1.0856,1.0856,1.0852,1.0854+2017-06-23 16:00:00,1.0853,1.0854,1.0851,1.0854+2017-06-23 15:00:00,1.0850,1.0853,1.0849,1.0853+2017-06-23 14:00:00,1.0852,1.0853,1.0847,1.0848+2017-06-23 13:00:00,1.0856,1.0859,1.0849,1.0849+2017-06-23 12:00:00,1.0856,1.0856,1.0851,1.0854+2017-06-23 11:00:00,1.0848,1.0856,1.0848,1.0856+2017-06-23 10:00:00,1.0854,1.0855,1.0845,1.0848+2017-06-23 09:00:00,1.0842,1.0852,1.0841,1.0852+2017-06-23 08:00:00,1.0844,1.0844,1.0837,1.0843+2017-06-23 07:00:00,1.0842,1.0848,1.0840,1.0845+2017-06-23 06:00:00,1.0838,1.0845,1.0838,1.0842+2017-06-23 05:00:00,1.0837,1.0839,1.0837,1.0838+2017-06-23 04:00:00,1.0837,1.0838,1.0836,1.0837+2017-06-23 03:00:00,1.0837,1.0837,1.0836,1.0836+2017-06-23 02:00:00,1.0838,1.0838,1.0835,1.0836+2017-06-23 01:00:00,1.0837,1.0839,1.0836,1.0837+2017-06-23 00:00:00,1.0837,1.0838,1.0835,1.0837+2017-06-22 23:00:00,1.0838,1.0838,1.0836,1.0837+2017-06-22 22:00:00,1.0839,1.0843,1.0837,1.0838+2017-06-22 21:00:00,1.0838,1.0856,1.0837,1.0838+2017-06-22 20:00:00,1.0838,1.0839,1.0837,1.0838+2017-06-22 19:00:00,1.0842,1.0842,1.0836,1.0837+2017-06-22 18:00:00,1.0841,1.0843,1.0840,1.0841+2017-06-22 17:00:00,1.0845,1.0847,1.0840,1.0841+2017-06-22 16:00:00,1.0847,1.0848,1.0843,1.0845+2017-06-22 15:00:00,1.0848,1.0851,1.0843,1.0845+2017-06-22 14:00:00,1.0852,1.0858,1.0844,1.0850+2017-06-22 13:00:00,1.0860,1.0864,1.0851,1.0851+2017-06-22 12:00:00,1.0873,1.0873,1.0862,1.0862+2017-06-22 11:00:00,1.0869,1.0873,1.0868,1.0871+2017-06-22 10:00:00,1.0871,1.0874,1.0869,1.0870+2017-06-22 09:00:00,1.0870,1.0871,1.0866,1.0870+2017-06-22 08:00:00,1.0861,1.0870,1.0860,1.0869+2017-06-22 07:00:00,1.0857,1.0862,1.0855,1.0861+2017-06-22 06:00:00,1.0863,1.0864,1.0857,1.0857+2017-06-22 05:00:00,1.0863,1.0864,1.0862,1.0863+2017-06-22 04:00:00,1.0863,1.0864,1.0861,1.0863+2017-06-22 03:00:00,1.0860,1.0863,1.0860,1.0863+2017-06-22 02:00:00,1.0862,1.0862,1.0859,1.0860+2017-06-22 01:00:00,1.0859,1.0863,1.0859,1.0863+2017-06-22 00:00:00,1.0861,1.0863,1.0860,1.0860+2017-06-21 23:00:00,1.0861,1.0862,1.0860,1.0861+2017-06-21 22:00:00,1.0861,1.0862,1.0860,1.0861+2017-06-21 21:00:00,1.0862,1.0866,1.0860,1.0861+2017-06-21 20:00:00,1.0861,1.0862,1.0859,1.0861+2017-06-21 19:00:00,1.0862,1.0862,1.0860,1.0860+2017-06-21 18:00:00,1.0860,1.0862,1.0860,1.0861+2017-06-21 17:00:00,1.0858,1.0861,1.0855,1.0859+2017-06-21 16:00:00,1.0860,1.0862,1.0858,1.0858+2017-06-21 15:00:00,1.0864,1.0864,1.0858,1.0858+2017-06-21 14:00:00,1.0859,1.0866,1.0858,1.0865+2017-06-21 13:00:00,1.0859,1.0861,1.0854,1.0856+2017-06-21 12:00:00,1.0860,1.0862,1.0858,1.0860+2017-06-21 11:00:00,1.0864,1.0865,1.0856,1.0860+2017-06-21 10:00:00,1.0855,1.0863,1.0855,1.0863+2017-06-21 09:00:00,1.0855,1.0858,1.0854,1.0854+2017-06-21 08:00:00,1.0848,1.0854,1.0846,1.0854+2017-06-21 07:00:00,1.0852,1.0853,1.0848,1.0850+2017-06-21 06:00:00,1.0852,1.0853,1.0849,1.0851+2017-06-21 05:00:00,1.0851,1.0852,1.0851,1.0851+2017-06-21 04:00:00,1.0851,1.0852,1.0850,1.0851+2017-06-21 03:00:00,1.0851,1.0853,1.0851,1.0852+2017-06-21 02:00:00,1.0855,1.0855,1.0849,1.0851+2017-06-21 01:00:00,1.0854,1.0855,1.0853,1.0855+2017-06-21 00:00:00,1.0856,1.0856,1.0850,1.0852+2017-06-20 23:00:00,1.0856,1.0857,1.0855,1.0856+2017-06-20 22:00:00,1.0866,1.0871,1.0856,1.0856+2017-06-20 21:00:00,1.0858,1.0870,1.0856,1.0861+2017-06-20 20:00:00,1.0855,1.0858,1.0855,1.0858+2017-06-20 19:00:00,1.0855,1.0857,1.0854,1.0855+2017-06-20 18:00:00,1.0857,1.0858,1.0855,1.0855+2017-06-20 17:00:00,1.0857,1.0859,1.0856,1.0858+2017-06-20 16:00:00,1.0858,1.0861,1.0857,1.0858+2017-06-20 15:00:00,1.0860,1.0862,1.0858,1.0860+2017-06-20 14:00:00,1.0859,1.0861,1.0857,1.0859+2017-06-20 13:00:00,1.0864,1.0865,1.0858,1.0859+2017-06-20 12:00:00,1.0854,1.0867,1.0854,1.0865+2017-06-20 11:00:00,1.0864,1.0864,1.0854,1.0855+2017-06-20 10:00:00,1.0861,1.0865,1.0860,1.0865+2017-06-20 09:00:00,1.0871,1.0875,1.0857,1.0864+2017-06-20 08:00:00,1.0874,1.0877,1.0868,1.0870+2017-06-20 07:00:00,1.0879,1.0880,1.0864,1.0872+2017-06-20 06:00:00,1.0878,1.0881,1.0877,1.0880+2017-06-20 05:00:00,1.0881,1.0882,1.0877,1.0877+2017-06-20 04:00:00,1.0879,1.0882,1.0878,1.0880+2017-06-20 03:00:00,1.0880,1.0881,1.0879,1.0879+2017-06-20 02:00:00,1.0880,1.0881,1.0878,1.0880+2017-06-20 01:00:00,1.0879,1.0880,1.0878,1.0880+2017-06-20 00:00:00,1.0880,1.0881,1.0878,1.0878+2017-06-19 23:00:00,1.0880,1.0880,1.0877,1.0880+2017-06-19 22:00:00,1.0882,1.0883,1.0879,1.0879+2017-06-19 21:00:00,1.0878,1.0890,1.0875,1.0882+2017-06-19 20:00:00,1.0878,1.0879,1.0877,1.0878+2017-06-19 19:00:00,1.0879,1.0879,1.0877,1.0879+2017-06-19 18:00:00,1.0879,1.0879,1.0876,1.0877+2017-06-19 17:00:00,1.0875,1.0879,1.0875,1.0878+2017-06-19 16:00:00,1.0877,1.0879,1.0875,1.0876+2017-06-19 15:00:00,1.0877,1.0879,1.0873,1.0877+2017-06-19 14:00:00,1.0883,1.0883,1.0877,1.0881+2017-06-19 13:00:00,1.0877,1.0885,1.0875,1.0885+2017-06-19 12:00:00,1.0869,1.0882,1.0868,1.0882+2017-06-19 11:00:00,1.0879,1.0880,1.0869,1.0870+2017-06-19 10:00:00,1.0876,1.0880,1.0873,1.0878+2017-06-19 09:00:00,1.0880,1.0881,1.0875,1.0876+2017-06-19 08:00:00,1.0883,1.0885,1.0877,1.0881+2017-06-19 07:00:00,1.0903,1.0903,1.0889,1.0890+2017-06-19 06:00:00,1.0905,1.0910,1.0904,1.0905+2017-06-19 05:00:00,1.0905,1.0905,1.0903,1.0904+2017-06-19 04:00:00,1.0902,1.0906,1.0902,1.0905+2017-06-19 03:00:00,1.0903,1.0903,1.0901,1.0902+2017-06-19 02:00:00,1.0904,1.0905,1.0902,1.0903+2017-06-19 01:00:00,1.0903,1.0905,1.0903,1.0904+2017-06-19 00:00:00,1.0906,1.0906,1.0902,1.0902+2017-06-18 23:00:00,1.0905,1.0905,1.0902,1.0905+2017-06-18 22:00:00,1.0903,1.0905,1.0901,1.0905+2017-06-18 21:00:00,1.0911,1.0911,1.0902,1.0907+2017-06-16 20:00:00,1.0900,1.0907,1.0898,1.0906+2017-06-16 19:00:00,1.0901,1.0902,1.0898,1.0900+2017-06-16 18:00:00,1.0904,1.0904,1.0900,1.0902+2017-06-16 17:00:00,1.0901,1.0902,1.0899,1.0901+2017-06-16 16:00:00,1.0902,1.0902,1.0898,1.0901+2017-06-16 15:00:00,1.0898,1.0902,1.0893,1.0901+2017-06-16 14:00:00,1.0890,1.0898,1.0886,1.0898+2017-06-16 13:00:00,1.0886,1.0888,1.0883,1.0884+2017-06-16 12:00:00,1.0885,1.0891,1.0884,1.0886+2017-06-16 11:00:00,1.0889,1.0890,1.0885,1.0885+2017-06-16 10:00:00,1.0881,1.0893,1.0881,1.0890+2017-06-16 09:00:00,1.0882,1.0883,1.0877,1.0882+2017-06-16 08:00:00,1.0876,1.0882,1.0876,1.0882+2017-06-16 07:00:00,1.0871,1.0876,1.0869,1.0875+2017-06-16 06:00:00,1.0870,1.0874,1.0867,1.0874+2017-06-16 05:00:00,1.0871,1.0872,1.0869,1.0870+2017-06-16 04:00:00,1.0871,1.0872,1.0870,1.0870+2017-06-16 03:00:00,1.0871,1.0872,1.0870,1.0871+2017-06-16 02:00:00,1.0872,1.0872,1.0869,1.0871+2017-06-16 01:00:00,1.0872,1.0873,1.0870,1.0872+2017-06-16 00:00:00,1.0874,1.0874,1.0871,1.0872+2017-06-15 23:00:00,1.0872,1.0873,1.0871,1.0872+2017-06-15 22:00:00,1.0875,1.0875,1.0871,1.0872+2017-06-15 21:00:00,1.0873,1.0876,1.0871,1.0875+2017-06-15 20:00:00,1.0874,1.0875,1.0871,1.0873+2017-06-15 19:00:00,1.0875,1.0875,1.0873,1.0873+2017-06-15 18:00:00,1.0874,1.0875,1.0873,1.0874+2017-06-15 17:00:00,1.0872,1.0874,1.0871,1.0872+2017-06-15 16:00:00,1.0873,1.0874,1.0871,1.0871
+ data/heatmap-bw view
@@ -0,0 +1,6 @@+6 5 4 3 1 0+3 2 2 0 0 1+0 0 0 0 1 0+0 0 0 0 2 3+0 0 1 2 4 3+0 1 2 3 4 5
plot-light.cabal view
@@ -1,5 +1,5 @@ name:                plot-light-version:             0.1.0.0+version:             0.1.0.1 synopsis:            A lightweight plotting library, exporting to SVG -- description: homepage:            https://github.com/ocramz/plot-light@@ -13,6 +13,9 @@ extra-source-files:  README.md cabal-version:       >=1.10 tested-with:         GHC == 8.0.2+data-dir:            data+data-files:          forex+                     heatmap-bw  library   default-language:    Haskell2010@@ -24,9 +27,12 @@                        Graphics.Rendering.Plot.Light.IO.Text                        Graphics.Rendering.Plot.Light.PlotTypes.Heatmap                        Graphics.Rendering.Plot.Light.PlotTypes.TimeSeries+  other-modules:       Data.TimeSeries.Forex+                       Graphics.Rendering.Plot.Light.Internal.Types   build-depends:       base >= 4.7 && < 5                      , time-                     , vector+                     , attoparsec-time                     +                     -- , vector                      , attoparsec                      , scientific                      , text@@ -34,13 +40,15 @@                      , palette                      , blaze-svg --- executable plot-light---   default-language:    Haskell2010---   ghc-options:         -threaded -rtsopts -with-rtsopts=-N---   hs-source-dirs:      app---   main-is:             Main.hs---   build-depends:       base---                      , plot-light+executable plot-light+  default-language:    Haskell2010+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  hs-source-dirs:      app/timeseries+  main-is:             Main.hs+  build-depends:       base+                     , plot-light+                     , attoparsec+                     , text  test-suite spec   default-language:    Haskell2010
+ src/Data/TimeSeries/Forex.hs view
@@ -0,0 +1,22 @@+module Data.TimeSeries.Forex where++import Data.Time++data FxRow a  = FxRow {+    rateOpen :: a+  , rateHigh :: a+  , rateLow :: a+  , rateClose :: a+               } deriving (Eq, Show)+++-- | An instant, defined by date (Day) and TimeOfDay+data Tick = Tick Day TimeOfDay+  deriving (Eq, Show, Ord)++-- | A point in a time series+data TsPoint a =+  Tsp {+    _tick :: Tick,+    _val :: a+    } deriving (Eq, Show)
src/Graphics/Rendering/Plot/Light.hs view
@@ -3,7 +3,6 @@  -- import Data.Foldable import qualified Data.Text as T-import qualified Data.Vector as V  import Text.Blaze.Svg import Text.Blaze.Svg11 ((!), mkPath, rotate, translate, l, m)
src/Graphics/Rendering/Plot/Light/IO/Text.hs view
@@ -1,29 +1,64 @@ module Graphics.Rendering.Plot.Light.IO.Text where - import Data.Text import qualified Data.Attoparsec.Text as A import qualified Data.Attoparsec.Internal.Types as AP (Parser) import Data.Scientific +import Control.Applicative ((<|>))+import Data.Time (Day, TimeOfDay)+import qualified Attoparsec.Time as AT +import Data.TimeSeries.Forex  --- row = many' decimal  space, comma :: A.Parser Char space = A.char ' ' comma = A.char ',' --- rowNums :: A.Parser Scientific+-- | Parse a row of numbers, separated by `sep` rowNums :: AP.Parser Text s -> AP.Parser Text [Scientific]-rowNums = A.sepBy A.scientific+rowNums sep = A.sepBy A.scientific sep  rowNumSpace :: AP.Parser Text [Scientific] rowNumSpace = rowNums space -- -- | parse a grid of numbers, separated by `sep` gridNum :: AP.Parser Text s -> AP.Parser Text [[Scientific]] gridNum sep = A.sepBy (rowNums sep) A.endOfLine++++++++++-- * Forex dataset++parseFxDataset :: AP.Parser Text [TsPoint (FxRow Scientific)]+parseFxDataset = A.sepBy parseFxRow A.endOfLine++parseFxRow :: AP.Parser Text (TsPoint (FxRow Scientific))+parseFxRow = do+  (d, t) <- parseDateTime+  _ <- comma+  open <- A.scientific+  _ <- comma+  hi <- A.scientific+  _ <- comma+  lo <- A.scientific+  _ <- comma+  close <- A.scientific+  pure $ Tsp (Tick d t) (FxRow open hi lo close)++parseDateTime :: AP.Parser Text (Day, TimeOfDay)+parseDateTime = do+  d <- AT.dayInISO8601+  _ <- space+  t <- AT.timeOfDayInISO8601+  return (d, t)++
src/Graphics/Rendering/Plot/Light/Internal.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} module Graphics.Rendering.Plot.Light.Internal where - import Control.Arrow ((&&&), (***))  -- import Data.Foldable@@ -11,34 +10,16 @@ import Text.Blaze.Svg import Text.Blaze.Svg11 -- ((!), mkPath, rotate, translate, l, m) import qualified Text.Blaze.Svg11 as S-import qualified Text.Blaze.Svg11.Attributes as A+import qualified Text.Blaze.Svg11.Attributes as S import Text.Blaze.Svg.Renderer.String (renderSvg)  import qualified Data.Colour as C import qualified Data.Colour.Names as C import qualified Data.Colour.SRGB as C ---data FigureData a d =-  FigData {-    _width :: a-  , _height :: a-  , _xmin :: a-  , _xmax :: a-  , _ymin :: a-  , _ymax :: a-  , _figData :: d-      }----- | A LabeledPoint carries the information of where a point should be plotted, what label should it carry (e.g. for labelling the axes) and its function value -data LabeledPoint c l a =-  LabeledPoint { _coord :: c, _label :: l, _value :: a }--data P1 a = P1 a-data P2 a = P2 a a+import GHC.Real +import Graphics.Rendering.Plot.Light.Internal.Types   -- axis f1 figd = undefined@@ -49,38 +30,46 @@ --     m = minimum d  --- | Given a point `x` in a range [x1min, x1max], map it by affine transformation onto the interval [x2min, x2max]-affine :: Fractional t => t -> t -> t -> t -> t -> t-affine x2min x2max x1min x1max x = (x - x1min)*d2/d1 + x2min where-  d1 = x1max - x1min-  d2 = x2max - x2min   + -- | Header for a Figure figure :: FigureData Int d -> Svg -> Svg figure fd =   S.docTypeSvg-  ! A.version "1.1"-  ! A.width (vi $ _width fd)-  ! A.height (vi $ _height fd)-  ! A.viewbox (vis [_xmin fd, _ymin fd, _xmax fd, _ymax fd])+  ! S.version "1.1"+  ! S.width (vi $ _width fd)+  ! S.height (vi $ _height fd)+  ! S.viewbox (vis [_xmin fd, _ymin fd, _xmax fd, _ymax fd])   -- | A filled rectangle, centered at (x0, y0) rectCentered   :: Double -> Double -> Double -> Double -> C.Colour Double -> Svg-rectCentered x0 y0 wid hei col = S.g ! A.transform (translate x0c y0c) $ -  S.rect ! A.width (vd wid) ! A.height (vd hei) ! A.fill (colourAttr col) where+rectCentered x0 y0 wid hei col = S.g ! S.transform (translate x0c y0c) $ +  S.rect ! S.width (vd wid) ! S.height (vd hei) ! S.fill (colourAttr col) where    x0c = x0 - (wid / 2)    y0c = y0 - (hei / 2)     -- centeredAt x0 y0 = S.g ! A.transform (translate x0 y0)   +line x1 x2 y1 y2 = S.line ! S.x1 (vd x1) ! S.x2 (vd x2) ! S.y1 (vd y1) ! S.y2 (vd y2) --- * Helpers  +-- * Helpers+++-- | Given a point `x` in a range [x1min, x1max], map it by affine transformation onto the interval [x2min, x2max]+affine :: Fractional t => t -> t -> t -> t -> t -> t+affine x2min x2max x1min x1max x = (x - x1min)*d2/d1 + x2min where+  d1 = x1max - x1min+  d2 = x2max - x2min++  ++ -- Render a Colour from `colour` into a `blaze` Attribute colourAttr :: C.Colour Double -> S.AttributeValue colourAttr = S.toValue . C.sRGB24show @@ -88,6 +77,7 @@ -- ** Conversion from primitive numerical types to AttributeValue vi :: Int -> S.AttributeValue vi = S.toValue+  -- | For use e.g. in `viewbox` vis :: [Int] -> S.AttributeValue
+ src/Graphics/Rendering/Plot/Light/Internal/Types.hs view
@@ -0,0 +1,20 @@+module Graphics.Rendering.Plot.Light.Internal.Types where+++data FigureData a d =+  FigData {+    _width :: a+  , _height :: a+  , _xmin :: a+  , _xmax :: a+  , _ymin :: a+  , _ymax :: a+  , _figData :: d+      }++-- | A LabeledPoint carries the information of where a point should be plotted, what label should it carry (e.g. for labelling the axes) and its function value +data LabeledPoint c l a =+  LabeledPoint { _coord :: c, _label :: l, _value :: a }++data P1 a = P1 a+data P2 a = P2 a a
src/Graphics/Rendering/Plot/Light/PlotTypes/TimeSeries.hs view
@@ -6,11 +6,11 @@ import qualified Data.Text as T  import Graphics.Rendering.Plot.Light.Internal+import Graphics.Rendering.Plot.Light.Internal.Types+import Data.TimeSeries.Forex --- | An instant, defined by date (Day) and TimeOfDay-data Tick = Tick Day TimeOfDay-  deriving (Eq, Show, Ord) + -- | Create a Tick from valid (year, month, day, hour, minute, second) mkTick :: Integer -> Int -> Int -> Int -> Int -> Pico -> Maybe Tick mkTick yy mm dd hr mi se = do@@ -23,12 +23,7 @@ fromTick :: Tick -> Rational fromTick (Tick d t) = fromIntegral (toModifiedJulianDay d) + timeOfDayToDayFraction t --- | A point in a time series-data TsPoint a =-  Tsp {-    _tick :: Tick,-    _val :: a-    } deriving (Eq, Show)+   -- | Transform the time coordinate of a timeseries point