Improves sparklines by integrating them more deeply to the codebase

This commit is contained in:
Ozzie Gooen 2022-04-08 08:44:04 -04:00
parent b8d07bd8a4
commit 0fa954ae63
8 changed files with 47 additions and 24 deletions

View File

@ -5,7 +5,7 @@ let {normalDist, uniformDist, betaDist, lognormalDist, cauchyDist, triangularDis
let runTest = (name: string, dist : GenericDist_Types.genericDist, expected: string) => { let runTest = (name: string, dist : GenericDist_Types.genericDist, expected: string) => {
test(name, () => { test(name, () => {
let result = GenericDist.toSparkline(~sampleCount=100, ~buckets=20, dist) let result = GenericDist.toSparkline(dist, ~sampleCount=100, ~buckets=20, ())
switch result { switch result {
| Ok(sparkline) => expect(sparkline)->toEqual(expected) | Ok(sparkline) => expect(sparkline)->toEqual(expected)
| Error(err) => expect("Error")->toEqual(expected) | Error(err) => expect("Error")->toEqual(expected)
@ -14,11 +14,11 @@ let runTest = (name: string, dist : GenericDist_Types.genericDist, expected: str
} }
describe("sparkline of generic distribution", () => { describe("sparkline of generic distribution", () => {
runTest("normal", normalDist, `▁▃▄▅▆▇████████▇▆▅▄▃▁`) runTest("normal", normalDist, `▁▁▁▁▁▂▄▆▇██▇▆▄▂▁▁▁▁▁`)
runTest("uniform", uniformDist, `▁██▁`) runTest("uniform", uniformDist, `████████████████████`)
runTest("beta", betaDist, `▁▅▇█████████▇▇▆▅▄▃▂▁`) runTest("beta", betaDist, `▁▄▇████▇▆▅▄▃▃▂▁▁▁▁▁▁`)
runTest("lognormal", lognormalDist, `▁███▇▇▆▆▅▄▄▃▃▂▂▁▁▁`) runTest("lognormal", lognormalDist, `▁█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`)
runTest("cauchy", cauchyDist, `▁▁▁▂▄▅▆▇████▇▆▅▄▂▁▁▁`) runTest("cauchy", cauchyDist, `▁▁▁▁▁▁▁▁▁██▁▁▁▁▁▁▁▁▁`)
runTest("triangular", triangularDist, `▁▃▄▅▆████▆▅▄▃▁`) runTest("triangular", triangularDist, `▁▁▂▃▄▅▆▇████▇▆▅▄▃▂▁▁`)
runTest("exponential", exponentialDist, `███▇▇▆▆▆▅▅▄▄▃▃▃▂▂▁▁▁`) runTest("exponential", exponentialDist, `█▅▄▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`)
}) })

View File

@ -114,6 +114,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
->E.R2.fmap(r => Float(r)) ->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToString => dist->GenericDist.toString->String | ToString => dist->GenericDist.toString->String
| ToSparkline(buckets) =>
GenericDist.toSparkline(dist, ~sampleCount, ~buckets, ())
->E.R2.fmap(r => String(r))
->OutputLocal.fromResult
| ToDist(Inspect) => { | ToDist(Inspect) => {
Js.log2("Console log requested: ", dist) Js.log2("Console log requested: ", dist)
Dist(dist) Dist(dist)
@ -127,7 +131,7 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult
| ToDist(ToPointSet) => | ToDist(ToPointSet) =>
dist dist
->GenericDist.toPointSet(~xyPointLength, ~sampleCount) ->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
->E.R2.fmap(r => Dist(PointSet(r))) ->E.R2.fmap(r => Dist(PointSet(r)))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) | ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented)

View File

@ -52,10 +52,16 @@ let toFloatOperation = (
//Todo: If it's a pointSet, but the xyPointLength 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. // This is tricky because the case of discrete distributions.
// Also, change the outputXYPoints/pointSetDistLength details // Also, change the outputXYPoints/pointSetDistLength details
let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSetDist, error> => { let toPointSet = (
t,
~xyPointLength,
~sampleCount,
~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight,
unit,
): result<PointSetTypes.pointSetDist, error> => {
switch (t: t) { switch (t: t) {
| PointSet(pointSet) => Ok(pointSet) | PointSet(pointSet) => Ok(pointSet)
| Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) | Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(~xSelection, xyPointLength, r))
| SampleSet(r) => { | SampleSet(r) => {
let response = SampleSet.toPointSetDist( let response = SampleSet.toPointSetDist(
~samples=r, ~samples=r,
@ -75,10 +81,10 @@ let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSe
} }
} }
let toSparkline = (~sampleCount: int, ~buckets: int = 20, t: t) : result<string, error> => let toSparkline = (t: t, ~sampleCount: int, ~buckets: int=20, unit): result<string, error> =>
toPointSet(~xyPointLength=buckets, ~sampleCount, t) t
-> E.R.bind(x => x -> PointSetDist.T.toContinuous -> E.O2.toResult(GenericDist_Types.Other("Could not convert to continuous"))) ->toPointSet(~xSelection=#Linear, ~xyPointLength=buckets, ~sampleCount, ())
-> E.R2.fmap(c => Sparklines.create(Continuous.getShape(c).ys, ())) ->E.R.bind(r => r->PointSetDist.toSparkline->E.R2.errMap(r => Error(GenericDist_Types.Other(r))))
module Truncate = { module Truncate = {
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> => let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>

View File

@ -20,22 +20,20 @@ let toFloatOperation: (
) => result<float, error> ) => result<float, error>
let toPointSet: ( let toPointSet: (
t,
~xyPointLength: int, ~xyPointLength: int,
~sampleCount: int, ~sampleCount: int,
t, ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
unit,
) => result<PointSetTypes.pointSetDist, error> ) => result<PointSetTypes.pointSetDist, error>
let toSparkline: ( let toSparkline: (t, ~sampleCount: int, ~buckets: int=?, unit) => result<string, error>
~sampleCount: int,
~buckets: int=?,
t,
) => result<string, error>
let truncate: ( let truncate: (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~leftCutoff: option<float>=?, ~leftCutoff: option<float>=?,
~rightCutoff: option<float>=?, ~rightCutoff: option<float>=?,
unit unit,
) => result<t, error> ) => result<t, error>
let algebraicCombination: ( let algebraicCombination: (

View File

@ -41,6 +41,8 @@ module Operation = {
| #Sample | #Sample
] ]
type pointsetXSelection = [#Linear | #ByWeight]
type toDist = type toDist =
| Normalize | Normalize
| ToPointSet | ToPointSet
@ -55,6 +57,7 @@ module Operation = {
| ToDist(toDist) | ToDist(toDist)
| ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
| ToString | ToString
| ToSparkline(int)
type singleParamaterFunction = type singleParamaterFunction =
| FromDist(fromDist) | FromDist(fromDist)
@ -78,6 +81,7 @@ module Operation = {
| ToDist(Truncate(_, _)) => `truncate` | ToDist(Truncate(_, _)) => `truncate`
| ToDist(Inspect) => `inspect` | ToDist(Inspect) => `inspect`
| ToString => `toString` | ToString => `toString`
| ToSparkline(n) => `toSparkline(${E.I.toString(n)})`
| ToDistCombination(Algebraic, _, _) => `algebraic` | ToDistCombination(Algebraic, _, _) => `algebraic`
| ToDistCombination(Pointwise, _, _) => `pointwise` | ToDistCombination(Pointwise, _, _) => `pointwise`
} }

View File

@ -202,3 +202,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Sample => sample(s) | #Sample => sample(s)
| #Mean => T.mean(s) | #Mean => T.mean(s)
} }
let toSparkline = (t: t) =>
T.toContinuous(t)
->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())

View File

@ -346,11 +346,11 @@ module T = {
| _ => #NoSolution | _ => #NoSolution
} }
let toPointSetDist = (sampleCount, d: symbolicDist): PointSetTypes.pointSetDist => let toPointSetDist = (~xSelection=#ByWeight, sampleCount, d: symbolicDist): PointSetTypes.pointSetDist =>
switch d { switch d {
| #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]})) | #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]}))
| _ => | _ =>
let xs = interpolateXs(~xSelection=#ByWeight, d, sampleCount) let xs = interpolateXs(~xSelection, d, sampleCount)
let ys = xs |> E.A.fmap(x => pdf(x, d)) let ys = xs |> E.A.fmap(x => pdf(x, d))
Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys})) Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys}))
} }

View File

@ -180,6 +180,12 @@ module R = {
module R2 = { module R2 = {
let fmap = (a,b) => R.fmap(b,a) let fmap = (a,b) => R.fmap(b,a)
let bind = (a, b) => R.bind(b, a) let bind = (a, b) => R.bind(b, a)
//Converts result type to change error type only
let errMap = (a, map) => switch(a){
| Ok(r) => Ok(r)
| Error(e) => map(e)
}
} }
let safe_fn_of_string = (fn, s: string): option<'a> => let safe_fn_of_string = (fn, s: string): option<'a> =>