packages feed

numhask-histogram (empty) → 0.0.0.1

raw patch · 6 files changed

+236/−0 lines, 6 filesdep +HUnitdep +QuickCheckdep +basesetup-changed

Dependencies added: HUnit, QuickCheck, base, containers, doctest, foldl, numhask, numhask-range, protolude, tasty, tasty-hunit, tasty-quickcheck, tdigest

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Tony Day (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Tony Day nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ numhask-histogram.cabal view
@@ -0,0 +1,60 @@+-- This file has been generated from package.yaml by hpack version 0.18.1.+--+-- see: https://github.com/sol/hpack++name:           numhask-histogram+version:        0.0.0.1+synopsis:       See readme.md+description:    See readme.md for description.+category:       project+homepage:       https://github.com/tonyday567/numhask-histogram#readme+bug-reports:    https://github.com/tonyday567/numhask-histogram/issues+author:         Tony Day+maintainer:     tonyday567@gmail.com+copyright:      Tony Day (c) 2017+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10++extra-source-files:+    stack.yaml++source-repository head+  type: git+  location: https://github.com/tonyday567/numhask-histogram++library+  hs-source-dirs:+      src+  ghc-options: -Wall+  build-depends:+      base >=4.7 && <5+    , protolude+    , containers+    , foldl+    , numhask+    , numhask-range+    , tdigest+  exposed-modules:+      NumHask.Histogram+  other-modules:+      Paths_numhask_histogram+  default-language: Haskell2010++test-suite test+  type: exitcode-stdio-1.0+  main-is: test.hs+  hs-source-dirs:+      test+  build-depends:+      base >=4.7 && <5+    , protolude+    , HUnit+    , QuickCheck+    , doctest+    , protolude+    , tasty+    , tasty-hunit+    , tasty-quickcheck+  default-language: Haskell2010
+ src/NumHask/Histogram.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# OPTIONS_GHC -Wall #-}++module NumHask.Histogram+  ( Histogram(..)+  , DealOvers(..)+  , fill+  , regular+  , makeRects+  , regularQuantiles+  , quantileFold+  , fromQuantiles+  , freq+  ) where++import Data.TDigest+import NumHask.Prelude+import NumHask.Range+import NumHask.Rect+import NumHask.Space+import qualified Data.Map as Map+import qualified Control.Foldl as L+import qualified Data.List++-- | a Histogram is a list of contiguous boundaries (a boundary being the lower edge of one bucket and the upper edge of another), and a count for each bucket+-- Overs and Unders are counted in key=0 and key=length cut+data Histogram = Histogram+   { cuts   :: [Double] -- bucket boundaries+   , values :: Map.Map Int Double -- bucket counts+   } deriving (Show, Eq)++-- | whether or not to ignore unders and overs+data DealOvers = IgnoreOvers | IncludeOvers Double++-- | fill a Histogram using pre-specified cuts+-- >>> fill [0,50,100] [1..100]+-- Histogram {cuts = [0.0,50.0,100.0], values = fromList [(1,50.0),(2,50.0)]}+fill :: (Functor f, Foldable f) => [Double] -> f Double -> Histogram+fill cs xs = Histogram cs (histMap cs xs)+  where+    histMap cs' xs' = L.fold count $+        (\x -> L.fold countBool (fmap (x >) cs')) <$> xs'+    count = L.premap (\x -> (x,1.0)) countW+    countBool = L.Fold (\x a -> x + if a then 1 else 0) 0 identity+    countW = L.Fold (\x (a,w) -> Map.insertWith (+) a w x) Map.empty identity++-- | make a histogram using n equally spaced cuts over the entire range of the data+-- >>> regular 4 [0..100]+-- Histogram {cuts = [0.0,25.0,50.0,75.0,100.0], values = fromList [(0,1.0),(1,25.0),(2,25.0),(3,25.0),(4,25.0)]}+--+regular :: Int -> [Double] -> Histogram+regular n xs = fill cs xs+  where+    cs = grid OuterPos (space xs :: Range Double) n++-- | transform a Histogram to Rects+-- >>> makeRects IgnoreOvers (regular 4 [0..100])+-- [Rect 0.0 25.0 0.0 0.25,Rect 25.0 50.0 0.0 0.25,Rect 50.0 75.0 0.0 0.25,Rect 75.0 100.0 0.0 0.25]+--+makeRects :: DealOvers -> Histogram -> [Rect Double]+makeRects o (Histogram cs counts) = Data.List.zipWith4 Rect x z y w'+  where+    y = repeat 0+    w = zipWith (/)+        ((\x' -> Map.findWithDefault 0 x' counts) <$> [f..l])+        (zipWith (-) z x)+    f = case o of+          IgnoreOvers -> 1+          IncludeOvers _ -> 0+    l = case o of+          IgnoreOvers -> length cs - 1+          IncludeOvers _ -> length cs+    w' = (/sum w) <$> w+    x = case o of+          IgnoreOvers -> cs+          IncludeOvers outw ->+            [Data.List.head cs - outw] <>+            cs <>+            [Data.List.last cs + outw]+    z = drop 1 x+++-- | approx regular n-quantiles+-- >>> regularQuantiles 4 [0..100]+-- [0.0,24.75,50.0,75.25,100.0]+--+regularQuantiles :: Double -> [Double] -> [Double]+regularQuantiles n = L.fold (quantileFold qs)+  where+    qs = ((1 / n) *) <$> [0 .. n]++-- | one-pass approximate quantiles fold+quantileFold :: [Double] -> L.Fold Double [Double]+quantileFold qs = L.Fold step begin done+  where+    step x a = Data.TDigest.insert a x+    begin = tdigest ([] :: [Double]) :: TDigest 25+    done x = fromMaybe nan . (`quantile` compress x) <$> qs++-- | take a specification of quantiles and make a Histogram+-- >>> fromQuantiles [0,0.25,0.5,0.75,1] (regularQuantiles 4 [0..100])+-- Histogram {cuts = [0.0,24.75,50.0,75.25,100.0], values = fromList [(1,0.25),(2,0.25),(3,0.25),(4,0.25)]}+--+fromQuantiles :: [Double] -> [Double] -> Histogram+fromQuantiles qs xs = Histogram xs (Map.fromList $ zip [1..] (diffq qs))+  where+    diffq [] = []+    diffq [_] = []+    diffq (x:xs') = L.fold (L.Fold step (x,[]) (reverse . snd)) xs'+    step (a0,xs') a = (a,(a-a0):xs')++-- | normalize a histogram so that sum values = one+-- >>> freq $ fill [0,50,100] [1..100]+-- Histogram {cuts = [0.0,50.0,100.0], values = fromList [(1,0.5),(2,0.5)]}+--+freq :: Histogram -> Histogram+freq (Histogram cs vs) = Histogram cs $ Map.map (* recip (sum vs)) vs
+ stack.yaml view
@@ -0,0 +1,7 @@+resolver: nightly-2017-12-29++packages:+- .++extra-deps: []+
+ test/test.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# OPTIONS_GHC -Wall #-}++module Main where++import Protolude+import Test.Tasty (TestTree, testGroup, defaultMain)+import Test.DocTest++main :: IO ()+main = do+    doctest ["src/NumHask/Histogram.hs"]+    defaultMain tests++tests :: TestTree+tests =+    testGroup ""+    [+    ]