Simple minX and maxX for distributions

This commit is contained in:
Ozzie Gooen 2020-02-20 12:54:45 +00:00
parent 73dbd64686
commit c3ca1a1a57
4 changed files with 126 additions and 44 deletions

View File

@ -16,15 +16,13 @@ let mixedDist =
) )
|> GenericDistribution.renderIfNeeded(~sampleCount=1000); |> GenericDistribution.renderIfNeeded(~sampleCount=1000);
// "mm(floor(uniform(30,35)), normal(50,20), [.25,.5])",
let timeDist = let timeDist =
GenericDistribution.make( GenericDistribution.make(
~generationSource= ~generationSource=GuesstimatorString("floor(normal(30,2))"),
GuesstimatorString(
"mm(floor(uniform(40, 50)), normal(50,10), [.5,.5])",
),
~probabilityType=Pdf, ~probabilityType=Pdf,
~domain=Complete, ~domain=Complete,
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}), ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `days}),
(), (),
) )
|> GenericDistribution.renderIfNeeded(~sampleCount=1000); |> GenericDistribution.renderIfNeeded(~sampleCount=1000);

View File

@ -63,38 +63,55 @@ let continuousComponent = (p: DistributionTypes.pointsType) =>
| Continuous(c) => Some(c) | Continuous(c) => Some(c)
}; };
module Cont = { let discreteScaleFactor = (p: DistributionTypes.pointsType) =>
[@react.component] switch (p) {
let make = (~continuous, ~onHover, ~timeScale) => { | Mixed(mixedShape) => Some(mixedShape.discreteProbabilityMassFraction)
let chart = | Discrete(_) => None
React.useMemo1( | Continuous(_) => None
() =>
<CdfChart__Plain
primaryDistribution=continuous
color={`hex("333")}
onHover
timeScale
/>,
[|continuous|],
);
chart;
}; };
};
module Shapee = { module Shapee = {
[@react.component] [@react.component]
let make = (~shape: DistributionTypes.pointsType, ~timeScale, ~onHover) => { let make = (~shape: DistributionTypes.pointsType, ~timeScale, ~onHover) => {
let continuous = continuousComponent(shape); let discreteScaleFactor = shape |> discreteScaleFactor;
let discrete = discreteComponent(shape); 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);
};
<div> <div>
{continuous {continuous
|> E.O.React.fmapOrNull(continuous => |> E.O.React.fmapOrNull(continuous =>
<Cont continuous onHover timeScale /> <CdfChart__Plain
)} primaryDistribution=continuous
{discrete minX
|> E.O.React.fmapOrNull(r => maxX
r |> Shape.Discrete.scaleYToTotal(0.3) |> Shape.Discrete.render ?discrete
color={`hex("333")}
onHover
timeScale
/>
)} )}
{discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)}
</div>; </div>;
}; };
}; };
@ -105,12 +122,19 @@ module GenericDist = {
let (x, setX) = React.useState(() => 0.); let (x, setX) = React.useState(() => 0.);
let timeScale = let timeScale =
genericDistribution.unit |> DistributionTypes.DistributionUnit.toJson; genericDistribution.unit |> DistributionTypes.DistributionUnit.toJson;
<div> let chart =
{genericDistribution React.useMemo1(
() => {
genericDistribution
|> DistributionTypes.shape |> DistributionTypes.shape
|> E.O.React.fmapOrNull(shape => { |> E.O.React.fmapOrNull(shape => {
<Shapee shape timeScale onHover={r => setX(_ => r)} /> <Shapee shape timeScale onHover={r => setX(_ => r)} />
})} })
},
[|genericDistribution|],
);
<div>
chart
<table className="table-auto"> <table className="table-auto">
<thead> <thead>
<tr> <tr>

View File

@ -20,10 +20,26 @@ let renderIfNeeded =
switch (t.generationSource) { switch (t.generationSource) {
| GuesstimatorString(s) => | GuesstimatorString(s) =>
let shape = Guesstimator.stringToMixedShape(~string=s, ~sampleCount, ()); let shape = Guesstimator.stringToMixedShape(~string=s, ~sampleCount, ());
shape let newShape =
|> E.O.fmap((shape: DistributionTypes.mixedShape) => 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( make(
~generationSource=Shape(Mixed(shape)), ~generationSource=Shape(shape),
~probabilityType=Cdf, ~probabilityType=Cdf,
~domain=t.domain, ~domain=t.domain,
~unit=t.unit, ~unit=t.unit,
@ -52,7 +68,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => {
switch (t) { switch (t) {
| Mixed({continuous, discrete, discreteProbabilityMassFraction}) => | Mixed({continuous, discrete, discreteProbabilityMassFraction}) =>
continuous continuous
|> Shape.Continuous.normalizePdf |> Shape.Continuous.scalePdf(~scaleTo=1.0)
|> E.O.fmap(r => |> E.O.fmap(r =>
Mixed({ Mixed({
continuous: r, continuous: r,
@ -63,7 +79,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => {
| Discrete(d) => Some(Discrete(d |> Shape.Discrete.scaleYToTotal(1.0))) | Discrete(d) => Some(Discrete(d |> Shape.Discrete.scaleYToTotal(1.0)))
| Continuous(continuousShape) => | Continuous(continuousShape) =>
continuousShape continuousShape
|> Shape.Continuous.normalizePdf |> Shape.Continuous.scalePdf(~scaleTo=1.0)
|> E.O.fmap(r => Continuous(r)) |> E.O.fmap(r => Continuous(r))
}; };
}; };

View File

@ -17,6 +17,10 @@ module XYShape = {
{"xs": t.xs, "ys": t.ys}; {"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 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)}; let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)};
@ -101,6 +105,8 @@ module XYShape = {
}; };
module Continuous = { module Continuous = {
let minX = XYShape.minX;
let maxX = XYShape.maxX;
let fromArrays = XYShape.fromArrays; let fromArrays = XYShape.fromArrays;
let toJs = XYShape.toJs; let toJs = XYShape.toJs;
let toPdf = XYShape.Range.derivative; let toPdf = XYShape.Range.derivative;
@ -114,11 +120,16 @@ module Continuous = {
let normalizeCdf = (continuousShape: continuousShape) => let normalizeCdf = (continuousShape: continuousShape) =>
continuousShape |> XYShape.scaleCdfTo(~scaleTo=1.0); continuousShape |> XYShape.scaleCdfTo(~scaleTo=1.0);
let normalizePdf = (continuousShape: continuousShape) => let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) =>
continuousShape |> toCdf |> E.O.fmap(normalizeCdf) |> E.O.bind(_, toPdf); continuousShape
|> toCdf
|> E.O.fmap(XYShape.scaleCdfTo(~scaleTo))
|> E.O.bind(_, toPdf);
}; };
module Discrete = { module Discrete = {
let minX = XYShape.minX;
let maxX = XYShape.maxX;
type t = discreteShape; type t = discreteShape;
let fromArrays = XYShape.fromArrays; let fromArrays = XYShape.fromArrays;
let toJs = XYShape.toJs; 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, // 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. // 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) => { let findIntegralY = (f, t: t) => {
t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); 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 = { module Mixed = {
let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => { let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => {
continuous, continuous,
@ -166,6 +194,12 @@ module Mixed = {
discreteProbabilityMassFraction, 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 = let mixedMultiply =
( (
t: DistributionTypes.mixedShape, t: DistributionTypes.mixedShape,
@ -197,10 +231,6 @@ module Mixed = {
| _ => None | _ => None
}; };
}; };
//Do the math to add these distributions together
// let integral =
// (x: float, t: DistributionTypes.mixedShape): option(XYShape.t) => {
// };
}; };
module Any = { module Any = {
@ -223,6 +253,20 @@ module Any = {
Continuous.findIntegralY(x, continuousShape) 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. // TODO: This is wrong. The discrete component should be made continuous when integrating.
let pdfToCdf = (t: t) => let pdfToCdf = (t: t) =>
switch (t) { switch (t) {