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);
// "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);

View File

@ -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(
() =>
<CdfChart__Plain
primaryDistribution=continuous
color={`hex("333")}
onHover
timeScale
/>,
[|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);
};
<div>
{continuous
|> E.O.React.fmapOrNull(continuous =>
<Cont continuous onHover timeScale />
)}
{discrete
|> E.O.React.fmapOrNull(r =>
r |> Shape.Discrete.scaleYToTotal(0.3) |> Shape.Discrete.render
<CdfChart__Plain
primaryDistribution=continuous
minX
maxX
?discrete
color={`hex("333")}
onHover
timeScale
/>
)}
{discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)}
</div>;
};
};
@ -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 => {
<Shapee shape timeScale onHover={r => setX(_ => r)} />
})
},
[|genericDistribution|],
);
<div>
{genericDistribution
|> DistributionTypes.shape
|> E.O.React.fmapOrNull(shape => {
<Shapee shape timeScale onHover={r => setX(_ => r)} />
})}
chart
<table className="table-auto">
<thead>
<tr>

View File

@ -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))
};
};

View File

@ -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) {