From a2d83958509373d77481ded5014383d0749cc759 Mon Sep 17 00:00:00 2001 From: Jireh Tan Date: Sun, 4 Jan 2026 17:10:56 +0800 Subject: [PATCH 1/6] update cabal + add wrapper over MWC --- Statistics/Distribution/Poisson.hs | 12 ++++++++++++ statistics.cabal | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Statistics/Distribution/Poisson.hs b/Statistics/Distribution/Poisson.hs index ba1441dd..66324678 100644 --- a/Statistics/Distribution/Poisson.hs +++ b/Statistics/Distribution/Poisson.hs @@ -31,12 +31,18 @@ import Data.Aeson (FromJSON(..), ToJSON, Value(..), (.:)) import Data.Binary (Binary(..)) import Data.Data (Data, Typeable) import GHC.Generics (Generic) + +import qualified System.Random.MWC.Distributions as MWC + import Numeric.SpecFunctions (incompleteGamma,logFactorial) import Numeric.MathFunctions.Constants (m_neg_inf) + import qualified Statistics.Distribution as D import qualified Statistics.Distribution.Poisson.Internal as I import Statistics.Internal +import Control.Monad (liftM) + @@ -93,6 +99,12 @@ instance D.Entropy PoissonDistribution where instance D.MaybeEntropy PoissonDistribution where maybeEntropy = Just . D.entropy +instance D.DiscreteGen PoissonDistribution where + genDiscreteVar (PD lambda) = MWC.poisson lambda + +instance D.ContGen PoissonDistribution where + genContVar (PD lambda) gen = fromIntegral <$> MWC.poisson lambda gen + -- | Create Poisson distribution. poisson :: Double -> PoissonDistribution poisson l = maybe (error $ errMsg l) id $ poissonE l diff --git a/statistics.cabal b/statistics.cabal index 4bb5c9f9..088e19a4 100644 --- a/statistics.cabal +++ b/statistics.cabal @@ -128,7 +128,7 @@ library build-depends: base >= 4.9 && < 5 -- , math-functions >= 0.3.4.1 - , mwc-random >= 0.15.0.0 + , mwc-random >= 0.15.3.0 , random >= 1.2 -- , aeson >= 0.6.0.0 @@ -219,7 +219,7 @@ common bench-stanza build-depends: base < 5 , vector >= 0.12.3 , statistics - , mwc-random + , mwc-random , tasty >=1.3.1 benchmark statistics-bench From f9a37f151741e242673fec3d91528001c44c0ad0 Mon Sep 17 00:00:00 2001 From: Jireh Tan Date: Sun, 4 Jan 2026 17:12:33 +0800 Subject: [PATCH 2/6] extra spaces removed --- statistics.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statistics.cabal b/statistics.cabal index 088e19a4..90be9d6f 100644 --- a/statistics.cabal +++ b/statistics.cabal @@ -219,7 +219,7 @@ common bench-stanza build-depends: base < 5 , vector >= 0.12.3 , statistics - , mwc-random + , mwc-random , tasty >=1.3.1 benchmark statistics-bench From 07a96b8ccee2ce6abc078d2049f74290dfbe3dfd Mon Sep 17 00:00:00 2001 From: Jireh Tan Date: Sun, 4 Jan 2026 17:13:10 +0800 Subject: [PATCH 3/6] even more extra spaces removed --- statistics.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statistics.cabal b/statistics.cabal index 90be9d6f..3b83547a 100644 --- a/statistics.cabal +++ b/statistics.cabal @@ -219,7 +219,7 @@ common bench-stanza build-depends: base < 5 , vector >= 0.12.3 , statistics - , mwc-random + , mwc-random , tasty >=1.3.1 benchmark statistics-bench From 6062033427918249edfc6bbec8f2c55264dfeede Mon Sep 17 00:00:00 2001 From: Jireh Tan Date: Fri, 9 Jan 2026 08:44:42 +0800 Subject: [PATCH 4/6] remove import --- Statistics/Distribution/Poisson.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Statistics/Distribution/Poisson.hs b/Statistics/Distribution/Poisson.hs index 66324678..1794df68 100644 --- a/Statistics/Distribution/Poisson.hs +++ b/Statistics/Distribution/Poisson.hs @@ -41,9 +41,6 @@ import Numeric.MathFunctions.Constants (m_neg_inf) import qualified Statistics.Distribution as D import qualified Statistics.Distribution.Poisson.Internal as I import Statistics.Internal -import Control.Monad (liftM) - - newtype PoissonDistribution = PD { From 4855194f5778230b890d211b95fe51644972c7a6 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Fri, 9 Jan 2026 15:15:24 +0300 Subject: [PATCH 5/6] Add since annotations ad bump version --- Statistics/Distribution/Poisson.hs | 2 ++ changelog.md | 5 +++++ statistics.cabal | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Statistics/Distribution/Poisson.hs b/Statistics/Distribution/Poisson.hs index 1794df68..389843ac 100644 --- a/Statistics/Distribution/Poisson.hs +++ b/Statistics/Distribution/Poisson.hs @@ -96,9 +96,11 @@ instance D.Entropy PoissonDistribution where instance D.MaybeEntropy PoissonDistribution where maybeEntropy = Just . D.entropy +-- | @since 0.16.5.0 instance D.DiscreteGen PoissonDistribution where genDiscreteVar (PD lambda) = MWC.poisson lambda +-- | @since 0.16.5.0 instance D.ContGen PoissonDistribution where genContVar (PD lambda) gen = fromIntegral <$> MWC.poisson lambda gen diff --git a/changelog.md b/changelog.md index bff0b21b..97729ede 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +## Changes in 0.16.5.0 [2026.01.09] + + * `ContGen` and `DiscreteGen` instances for `Poisson` distributions are added. + + ## Changes in 0.16.4.0 [2025.10.23] * Bartlett's test (`Statistics.Test.Bartlett`) and Levene's test diff --git a/statistics.cabal b/statistics.cabal index 3b83547a..65322a67 100644 --- a/statistics.cabal +++ b/statistics.cabal @@ -2,7 +2,7 @@ cabal-version: 3.0 build-type: Simple name: statistics -version: 0.16.4.0 +version: 0.16.5.0 synopsis: A library of statistical types, data, and functions description: This library provides a number of common functions and types useful From db0c522ea382c7835026c5f6c2c9bec0f8bc3cc8 Mon Sep 17 00:00:00 2001 From: Jireh Tan Date: Wed, 21 Jan 2026 12:21:05 +0800 Subject: [PATCH 6/6] fix bug where probability is out of range --- Statistics/Test/KruskalWallis.hs | 74 +++++++++++++++++++------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/Statistics/Test/KruskalWallis.hs b/Statistics/Test/KruskalWallis.hs index 3a917547..e15a04d8 100644 --- a/Statistics/Test/KruskalWallis.hs +++ b/Statistics/Test/KruskalWallis.hs @@ -34,17 +34,22 @@ import qualified Statistics.Sample.Internal as Sample(sum) -- -- The samples and values need not to be ordered but the values in the result -- are ordered. Assigned ranks (ties are given their average rank). -kruskalWallisRank :: (U.Unbox a, Ord a) => [U.Vector a] -> [U.Vector Double] -kruskalWallisRank samples = groupByTags - . sortBy (comparing fst) - . U.zip tags - $ rank (==) joinSample +kruskalWallisRank :: (U.Unbox a, Ord a) => [U.Vector a] -> Maybe [U.Vector Double] +kruskalWallisRank samples = + if firstRank == lastRank + then Nothing + else Just + . groupByTags + . sortBy (comparing fst) + . U.zip tags + $ rank (==) joinSample where (tags,joinSample) = U.unzip . sortBy (comparing snd) $ foldMap (uncurry tagSample) $ zip [(1::Int)..] samples tagSample t = U.map (\x -> (t,x)) - + firstRank = U.head joinSample + lastRank = U.last joinSample groupByTags xs | U.null xs = [] | otherwise = sort (U.map snd ys) : groupByTags zs @@ -56,21 +61,24 @@ kruskalWallisRank samples = groupByTags -- -- In textbooks the output value is usually represented by 'K' or 'H'. This -- function already does the ranking. -kruskalWallis :: (U.Unbox a, Ord a) => [U.Vector a] -> Double -kruskalWallis samples = (nTot - 1) * numerator / denominator - where - -- Total number of elements in all samples - nTot = fromIntegral $ sumWith rsamples U.length - -- Average rank of all samples - avgRank = (nTot + 1) / 2 - -- - numerator = sumWith rsamples $ \sample -> - let n = fromIntegral $ U.length sample - in n * square (mean sample - avgRank) - denominator = sumWith rsamples $ \sample -> - Sample.sum $ U.map (\r -> square (r - avgRank)) sample +kruskalWallis :: (U.Unbox a, Ord a) => [U.Vector a] -> Maybe Double +kruskalWallis samples = case kruskalWallisRank samples of + Nothing -> Nothing + Just rsamples -> + Just $ (nTot - 1) * numerator / denominator + where + -- Total number of elements in all samples + nTot = fromIntegral $ sumWith rsamples U.length + -- Average rank of all samples + avgRank = (nTot + 1) / 2 + -- + numerator = sumWith rsamples $ \sample -> + let n = fromIntegral $ U.length sample + in n * square (mean sample - avgRank) + denominator = sumWith rsamples $ \sample -> + Sample.sum $ U.map (\r -> square (r - avgRank)) sample + - rsamples = kruskalWallisRank samples -- | Perform Kruskal-Wallis Test for the given samples and required @@ -80,18 +88,22 @@ kruskalWallis samples = (nTot - 1) * numerator / denominator -- It uses /Chi-Squared/ distribution for approximation as long as the sizes are -- larger than 5. Otherwise the test returns 'Nothing'. kruskalWallisTest :: (Ord a, U.Unbox a) => [U.Vector a] -> Maybe (Test ()) +-- If there are no samples, return Nothing since there's nothing to test. kruskalWallisTest [] = Nothing -kruskalWallisTest samples - -- We use chi-squared approximation here - | all (>4) ns = Just Test { testSignificance = mkPValue $ complCumulative d k - , testStatistics = k - , testDistribution = () - } - | otherwise = Nothing - where - k = kruskalWallis samples - ns = map U.length samples - d = chiSquared (length ns - 1) +-- If there is only one sample, return Nothing since there's nothing to test against. +kruskalWallisTest [_] = Nothing +kruskalWallisTest samples = case kruskalWallis samples of + Nothing -> Nothing + Just k -> + -- We use chi-squared approximation here + if all (>4) ns then Just Test { testSignificance = mkPValue $ complCumulative d k + , testStatistics = k + , testDistribution = () + } + else Nothing + where + ns = map U.length samples + d = chiSquared (length ns - 1) -- * Helper functions