Implement generic sparklines with tests

This commit is contained in:
Sam Nolan 2022-04-08 10:17:01 +10:00
parent 51f2fce2c4
commit 53f4e56529
4 changed files with 62 additions and 2 deletions

View File

@ -0,0 +1,35 @@
open Jest
open Expect
let env: DistributionOperation.env = {
sampleCount: 100,
xyPointLength: 100,
}
let normalDist: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
let triangularDist: GenericDist_Types.genericDist = Symbolic(#Triangular({low: 1.0, medium: 2.0, high: 3.0}))
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
let runTest = (name: string, dist : GenericDist_Types.genericDist, expected: string) => {
test(name, () => {
let result = GenericDist.toSparkline(~xyPointLength=100, ~sampleCount=100, ~buckets=20, dist)
switch result {
| Ok(sparkline) => expect(sparkline)->toEqual(expected)
| Error(err) => expect("Error")->toEqual(expected)
}
})
}
describe("sparkline of generic distribution", () => {
runTest("normal", normalDist, `▁▁▁▁▂▃▄▆▇██▇▆▄▃▂▁▁▁`)
runTest("uniform", uniformDist, `████████████████████`)
runTest("beta", uniformDist, `████████████████████`)
runTest("lognormal", lognormalDist, `█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`)
runTest("cauchy", cauchyDist, `▁▁▁▁▁▁▁▁▁██▁▁▁▁▁▁▁▁▁`)
runTest("triangular", triangularDist, `▁▂▃▄▄▅▆▇████▇▆▅▄▄▃▂▁`)
runTest("exponential", exponentialDist, `█▆▄▃▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`)
})

View File

@ -49,7 +49,7 @@ let toFloatOperation = (
}
}
//Todo: If it's a pointSet, but the xyPointLenght is different from what it has, it should change.
//Todo: If it's a pointSet, but the xyPointLength is different from what it has, it should change.
// This is tricky because the case of discrete distributions.
// Also, change the outputXYPoints/pointSetDistLength details
let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSetDist, error> => {
@ -75,6 +75,9 @@ let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSe
}
}
let toSparkline = (~xyPointLength: int, ~sampleCount: int, ~buckets: int = 20, t: t) : result<string, error> =>
toPointSet(~xyPointLength, ~sampleCount, t) -> E.R2.fmap(PointSetDist.toSparkline(buckets))
module Truncate = {
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>
switch (leftCutoff, rightCutoff, t) {

View File

@ -24,6 +24,12 @@ let toPointSet: (
~sampleCount: int,
t,
) => result<PointSetTypes.pointSetDist, error>
let toSparkline: (
~xyPointLength: int,
~sampleCount: int,
~buckets: int=?,
t,
) => result<string, error>
let truncate: (
t,
@ -59,4 +65,4 @@ let mixture: (
array<(t, float)>,
~scaleMultiplyFn: scaleMultiplyFn,
~pointwiseAddFn: pointwiseAddFn,
) => result<t, error>
) => result<t, error>

View File

@ -168,6 +168,22 @@ let pdf = (f: float, t: t) => {
let inv = T.Integral.yToX
let cdf = T.Integral.xToY
let diff = (arr: array<float>): array<float> =>
Belt.Array.zipBy(arr, Belt.Array.sliceToEnd(arr, 1), (left, right) => right -. left)
let rec rangeByFloat = (start : float, end: float, step: float) =>
start > end ?
[]
: Belt.Array.concat([start], rangeByFloat(start +. step, end, step))
@genType
let toSparkline = (buckets: int, t: t ): string => {
let size : float = T.maxX(t) -. T.minX(t)
let stepSize = size /. Belt.Int.toFloat(buckets)
let cdf = rangeByFloat(T.minX(t), T.maxX(t), stepSize) -> Belt.Array.map(val => cdf(val,t))
Sparklines.create(diff(cdf), ())
}
let doN = (n, fn) => {
let items = Belt.Array.make(n, 0.0)
for x in 0 to n - 1 {