From c3ca1a1a5769161c3bcdb40d7020b2c60add0051 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 20 Feb 2020 12:54:45 +0000 Subject: [PATCH] Simple minX and maxX for distributions --- showcase/entries/Continuous.re | 8 +- .../charts/GenericDistributionChart.re | 80 ++++++++++++------- src/core/GenericDistribution.re | 26 ++++-- src/core/Shape.re | 56 +++++++++++-- 4 files changed, 126 insertions(+), 44 deletions(-) diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re index 94d683ee..c9d57e92 100644 --- a/showcase/entries/Continuous.re +++ b/showcase/entries/Continuous.re @@ -16,15 +16,13 @@ let mixedDist = ) |> GenericDistribution.renderIfNeeded(~sampleCount=1000); +// "mm(floor(uniform(30,35)), normal(50,20), [.25,.5])", let timeDist = GenericDistribution.make( - ~generationSource= - GuesstimatorString( - "mm(floor(uniform(40, 50)), normal(50,10), [.5,.5])", - ), + ~generationSource=GuesstimatorString("floor(normal(30,2))"), ~probabilityType=Pdf, ~domain=Complete, - ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), + ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `days}), (), ) |> GenericDistribution.renderIfNeeded(~sampleCount=1000); diff --git a/src/components/charts/GenericDistributionChart.re b/src/components/charts/GenericDistributionChart.re index fad95085..7a4ca384 100644 --- a/src/components/charts/GenericDistributionChart.re +++ b/src/components/charts/GenericDistributionChart.re @@ -63,38 +63,55 @@ let continuousComponent = (p: DistributionTypes.pointsType) => | Continuous(c) => Some(c) }; -module Cont = { - [@react.component] - let make = (~continuous, ~onHover, ~timeScale) => { - let chart = - React.useMemo1( - () => - , - [|continuous|], - ); - chart; +let discreteScaleFactor = (p: DistributionTypes.pointsType) => + switch (p) { + | Mixed(mixedShape) => Some(mixedShape.discreteProbabilityMassFraction) + | Discrete(_) => None + | Continuous(_) => None }; -}; module Shapee = { [@react.component] let make = (~shape: DistributionTypes.pointsType, ~timeScale, ~onHover) => { - let continuous = continuousComponent(shape); - let discrete = discreteComponent(shape); + let discreteScaleFactor = shape |> discreteScaleFactor; + let continuous = + continuousComponent(shape) + |> E.O.bind( + _, + Shape.Continuous.scalePdf( + ~scaleTo= + discreteScaleFactor + |> E.O.fmap(r => 1. -. r) + |> E.O.default(1.0), + ), + ); + let discrete = + discreteComponent(shape) + |> E.O.fmap( + Shape.Discrete.scaleYToTotal( + discreteScaleFactor |> E.O.default(1.0), + ), + ); + let minX = { + Shape.Any.minX(shape); + }; + let maxX = { + Shape.Any.maxX(shape); + };
{continuous |> E.O.React.fmapOrNull(continuous => - - )} - {discrete - |> E.O.React.fmapOrNull(r => - r |> Shape.Discrete.scaleYToTotal(0.3) |> Shape.Discrete.render + )} + {discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)}
; }; }; @@ -105,12 +122,19 @@ module GenericDist = { let (x, setX) = React.useState(() => 0.); let timeScale = genericDistribution.unit |> DistributionTypes.DistributionUnit.toJson; + let chart = + React.useMemo1( + () => { + genericDistribution + |> DistributionTypes.shape + |> E.O.React.fmapOrNull(shape => { + setX(_ => r)} /> + }) + }, + [|genericDistribution|], + );
- {genericDistribution - |> DistributionTypes.shape - |> E.O.React.fmapOrNull(shape => { - setX(_ => r)} /> - })} + chart diff --git a/src/core/GenericDistribution.re b/src/core/GenericDistribution.re index 787e7c3e..f59afda7 100644 --- a/src/core/GenericDistribution.re +++ b/src/core/GenericDistribution.re @@ -20,10 +20,26 @@ let renderIfNeeded = switch (t.generationSource) { | GuesstimatorString(s) => let shape = Guesstimator.stringToMixedShape(~string=s, ~sampleCount, ()); - shape - |> E.O.fmap((shape: DistributionTypes.mixedShape) => + let newShape = + switch (shape) { + | Some({ + continuous: {xs: [||], ys: [||]}, + discrete: {xs: [||], ys: [||]}, + }) => + None + | Some({continuous, discrete: {xs: [||], ys: [||]}}) => + Some(Continuous(continuous)) + | Some({continuous: {xs: [||], ys: [||]}, discrete}) => + Some(Discrete(discrete)) + | Some(shape) => Some(Mixed(shape)) + | _ => None + }; + + Js.log(newShape); + newShape + |> E.O.fmap((shape: DistributionTypes.pointsType) => make( - ~generationSource=Shape(Mixed(shape)), + ~generationSource=Shape(shape), ~probabilityType=Cdf, ~domain=t.domain, ~unit=t.unit, @@ -52,7 +68,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => { switch (t) { | Mixed({continuous, discrete, discreteProbabilityMassFraction}) => continuous - |> Shape.Continuous.normalizePdf + |> Shape.Continuous.scalePdf(~scaleTo=1.0) |> E.O.fmap(r => Mixed({ continuous: r, @@ -63,7 +79,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => { | Discrete(d) => Some(Discrete(d |> Shape.Discrete.scaleYToTotal(1.0))) | Continuous(continuousShape) => continuousShape - |> Shape.Continuous.normalizePdf + |> Shape.Continuous.scalePdf(~scaleTo=1.0) |> E.O.fmap(r => Continuous(r)) }; }; diff --git a/src/core/Shape.re b/src/core/Shape.re index d44b8975..b3537574 100644 --- a/src/core/Shape.re +++ b/src/core/Shape.re @@ -17,6 +17,10 @@ module XYShape = { {"xs": t.xs, "ys": t.ys}; }; + let minX = (t: t) => t.xs |> E.A.get(_, 0); + // TODO: Check if this actually gets the last element, I'm not sure it does. + let maxX = (t: t) => t.xs |> (r => E.A.get(r, E.A.length(r) - 1)); + let zip = t => Belt.Array.zip(t.xs, t.ys); let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)}; @@ -101,6 +105,8 @@ module XYShape = { }; module Continuous = { + let minX = XYShape.minX; + let maxX = XYShape.maxX; let fromArrays = XYShape.fromArrays; let toJs = XYShape.toJs; let toPdf = XYShape.Range.derivative; @@ -114,11 +120,16 @@ module Continuous = { let normalizeCdf = (continuousShape: continuousShape) => continuousShape |> XYShape.scaleCdfTo(~scaleTo=1.0); - let normalizePdf = (continuousShape: continuousShape) => - continuousShape |> toCdf |> E.O.fmap(normalizeCdf) |> E.O.bind(_, toPdf); + let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) => + continuousShape + |> toCdf + |> E.O.fmap(XYShape.scaleCdfTo(~scaleTo)) + |> E.O.bind(_, toPdf); }; module Discrete = { + let minX = XYShape.minX; + let maxX = XYShape.maxX; type t = discreteShape; let fromArrays = XYShape.fromArrays; let toJs = XYShape.toJs; @@ -147,6 +158,7 @@ module Discrete = { // TODO: This has a clear bug where it returns the Y value of the first item, // even if X is less than the X of the first item. + // It has a second bug that it assumes things are triangular, instead of interpolating via steps. let findIntegralY = (f, t: t) => { t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); }; @@ -159,6 +171,22 @@ module Discrete = { }; }; +let min = (f1: option(float), f2: option(float)) => + switch (f1, f2) { + | (Some(f1), Some(f2)) => Some(f1 < f2 ? f1 : f2) + | (Some(f1), None) => Some(f1) + | (None, Some(f2)) => Some(f2) + | (None, None) => None + }; + +let max = (f1: option(float), f2: option(float)) => + switch (f1, f2) { + | (Some(f1), Some(f2)) => Some(f1 > f2 ? f1 : f2) + | (Some(f1), None) => Some(f1) + | (None, Some(f2)) => Some(f2) + | (None, None) => None + }; + module Mixed = { let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => { continuous, @@ -166,6 +194,12 @@ module Mixed = { discreteProbabilityMassFraction, }; + let minX = (t: DistributionTypes.mixedShape) => + min(t.continuous |> Continuous.minX, t.discrete |> Discrete.minX); + + let maxX = (t: DistributionTypes.mixedShape) => + min(t.continuous |> Continuous.maxX, t.discrete |> Discrete.maxX); + let mixedMultiply = ( t: DistributionTypes.mixedShape, @@ -197,10 +231,6 @@ module Mixed = { | _ => None }; }; - //Do the math to add these distributions together - // let integral = - // (x: float, t: DistributionTypes.mixedShape): option(XYShape.t) => { - // }; }; module Any = { @@ -223,6 +253,20 @@ module Any = { Continuous.findIntegralY(x, continuousShape) }; + let minX = (t: t) => + switch (t) { + | Mixed(m) => Mixed.minX(m) + | Discrete(discreteShape) => Discrete.minX(discreteShape) + | Continuous(continuousShape) => Continuous.minX(continuousShape) + }; + + let maxX = (t: t) => + switch (t) { + | Mixed(m) => Mixed.maxX(m) + | Discrete(discreteShape) => Discrete.maxX(discreteShape) + | Continuous(continuousShape) => Continuous.maxX(continuousShape) + }; + // TODO: This is wrong. The discrete component should be made continuous when integrating. let pdfToCdf = (t: t) => switch (t) {