packages feed

probability-0.2.9: src/Numeric/Probability/Example/Histogram.hs

{- |
We have a set of symbols.
We draw from this set n times with replacement.
We then sort the symbols by their drawn frequency.
What is the expected distribution of the histogram?
-}
module Numeric.Probability.Example.Histogram where

import qualified Numeric.Probability.Distribution as Dist
import Control.Monad (replicateM, guard)

import qualified Data.NonEmpty as NonEmpty
import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import Data.Foldable (for_)
import Data.IntMap (IntMap)
import Data.Map (Map)

import Text.Printf (printf)

{- $setup
>>> import qualified Combinatorics as Comb
-}


example :: (Ord a) => NonEmpty.T [] a -> Int -> Dist.T Rational (Map Int Int)
example set n = Dist.norm $ do
   let x = NonEmpty.head set
   xs <- replicateM (n-1) $ Dist.uniform $ NonEmpty.flatten set
   return $ histogram $ Map.elems $ histogram (x:xs)

flattenHistogram :: (Num a) => Map Int a -> [a]
flattenHistogram histo =
   Map.elems $ Map.unionWith (+) histo $ Map.fromList $
   decorate 0 [1 .. fst $ Map.findMax histo]

{- |
>>> exampleList ('a'!:['b'..'k']) 4
fromFreqs [([4],720 % 1331),([2,1],540 % 1331),([1,0,1],40 % 1331),([0,2],30 % 1331),([0,0,0,1],1 % 1331)]
-}
exampleList :: (Ord a) => NonEmpty.T [] a -> Int -> Dist.T Rational [Int]
exampleList set n = fmap flattenHistogram $ example set n

histogram :: (Ord a) => [a] -> Map a Int
histogram = Map.fromListWith (+) . decorate 1

decorate :: b -> [a] -> [(a,b)]
decorate label = map (flip (,) label)


{-
3: https://oeis.org/A038207
4: https://oeis.org/A027465
5: https://oeis.org/A038231
6: https://oeis.org/A038243
7: https://oeis.org/A038255
8: https://oeis.org/A027466
9: https://oeis.org/A038279

expected $ example [1..k] n
j -> binomial n j * (k-1)^(n-j)

This also counts the symbols with zero occurrences.

We might prove this using a recurrence.
-}
expected :: Dist.T Rational (Map Int Int) -> Map Int Rational
expected =
   foldl (Map.unionWith (+)) Map.empty .
   map (\(x,p) -> fmap ((p*) . fromIntegral) x) .
   Dist.decons

visualize :: Rational -> Map Int Rational -> IO ()
visualize scale m =
   for_ [1 .. fst $ Map.findMax m] $ \n ->
      let freq = Map.findWithDefault 0 n m in
      printf "%6.1f %s\n" (fromRational freq :: Double) $
         replicate (round (scale * freq)) '*'



{-
https://oeis.org/A000041

>>> map (\n -> length $ partitions n n) [0..20]
-}
partitions :: Int -> Int -> [IntMap Int]
partitions maxBin =
   let go total multi =
         case compare multi 1 of
            LT -> guard (total == 0) >> [IntMap.empty]
            EQ -> guard (total <= maxBin) >> [IntMap.singleton multi total]
            GT ->
               concat $
               zipWith
                  (\j amount ->
                     map (IntMap.singleton multi j <>) $
                     go (total-amount) (multi-1))
                  [0..maxBin]
                  [0,multi..total]
   in \total -> go total total

{-
*Numeric.Probability.Visualize NE GP Comb> GP.plotList [] $ zipWith (*) (map fromInteger $ Comb.binomialSeq 110) (iterate (/27) ((27/28)**110::Double))
*Numeric.Probability.Visualize NE GP Comb> let xs = zipWith (*) (map fromInteger $ Comb.binomialSeq 110) (iterate (/27) ((27/28)**110::Double))
*Numeric.Probability.Visualize NE GP Comb> sum xs
1.0000000000000022


let histo n k = map (% k^(n-1)) $ zipWith (*) (Comb.binomialSeq n) (reverse $ take (fromInteger $ n+1) $ iterate (*(k-1)) 1)

>>> sum $ histo 110 28
28 % 1
>>> sum $ zipWith (*) [0..] $ histo 110 28
110 % 1
-}