Improved mixed CDF function

This commit is contained in:
Ozzie Gooen 2020-02-24 16:39:55 +00:00
parent d7f95df430
commit 3182067a48
5 changed files with 111 additions and 36 deletions

View File

@ -79,7 +79,10 @@ describe("Shape", () => {
make({xs: [|1., 4., 8.|], ys: [|0.1, 5., 1.0|]}, `Stepwise); make({xs: [|1., 4., 8.|], ys: [|0.1, 5., 1.0|]}, `Stepwise);
continuous |> toLinear |> getShape; continuous |> toLinear |> getShape;
}, },
{xs: [|1.0, 4.0, 4.0, 8.0, 8.0|], ys: [|0.1, 0.1, 5.0, 5.0, 1.0|]}, {
xs: [|1.00007, 4.0, 4.00007, 8.0, 8.00007|],
ys: [|0.1, 0.1, 5.0, 5.0, 1.0|],
},
); );
makeTest( makeTest(
"integralXToY", "integralXToY",
@ -251,13 +254,20 @@ describe("Shape", () => {
T.Integral.get(~cache=None, mixed), T.Integral.get(~cache=None, mixed),
Distributions.Continuous.make( Distributions.Continuous.make(
{ {
xs: [|1., 3., 4., 4., 7., 8., 8., 14.|], xs: [|1.00007, 3., 4., 4.00007, 7., 8., 8.00007, 14.|],
ys: [|0.15, 0.0, 0.15, 0.4, 0.13986013986013987, 0.4, 0.5, 0.5|], ys: [|
0.15,
0.15,
0.18496503496503497,
0.4349674825174825,
0.5398601398601399,
0.5913086913086913,
0.6913122927072927,
1.0,
|],
}, },
`Linear, `Linear,
), ),
); );
// makeTest("integralXToY", T.Integral.xToY(~cache=None, 6.0, mixed), 0.9);
// makeTest("integralSum", T.Integral.sum(~cache=None, mixed), 1.0);
}); });
}); });

View File

@ -12,7 +12,11 @@ let distributions = () =>
<div> <div>
<div> <div>
<h2> {"Basic Mixed Distribution" |> ReasonReact.string} </h2> <h2> {"Basic Mixed Distribution" |> ReasonReact.string} </h2>
{timeDist |> E.O.React.fmapOrNull(distPlus => <DistPlusPlot distPlus />)} {timeDist
|> E.O.fmap(
Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0),
)
|> E.O.React.fmapOrNull(distPlus => <DistPlusPlot distPlus />)}
<h2> {"Simple Continuous" |> ReasonReact.string} </h2> <h2> {"Simple Continuous" |> ReasonReact.string} </h2>
</div> </div>
</div>; </div>;

View File

@ -60,12 +60,6 @@ let make = (~distPlus: DistTypes.distPlus) => {
() => {<IntegralChart distPlus onHover={r => {setX(_ => r)}} />}, () => {<IntegralChart distPlus onHover={r => {setX(_ => r)}} />},
[|distPlus|], [|distPlus|],
); );
Js.log4(
"distPlus",
x,
distPlus,
distPlus |> Distributions.DistPlus.T.xToY(x),
);
<div> <div>
chart chart
chart2 chart2
@ -93,14 +87,14 @@ let make = (~distPlus: DistTypes.distPlus) => {
{distPlus {distPlus
|> Distributions.DistPlus.T.xToY(x) |> Distributions.DistPlus.T.xToY(x)
|> DistTypes.MixedPoint.toDiscreteValue |> DistTypes.MixedPoint.toDiscreteValue
|> E.Float.with2DigitsPrecision |> Js.Float.toPrecisionWithPrecision(_, ~digits=7)
|> ReasonReact.string} |> ReasonReact.string}
</th> </th>
<th className="px-4 py-2 border "> <th className="px-4 py-2 border ">
{distPlus {distPlus
|> Distributions.DistPlus.T.xToY(x) |> Distributions.DistPlus.T.xToY(x)
|> DistTypes.MixedPoint.toContinuousValue |> DistTypes.MixedPoint.toContinuousValue
|> E.Float.with2DigitsPrecision |> Js.Float.toPrecisionWithPrecision(_, ~digits=7)
|> ReasonReact.string} |> ReasonReact.string}
</th> </th>
<th className="px-4 py-2 border "> <th className="px-4 py-2 border ">

View File

@ -101,7 +101,7 @@ module Continuous = {
switch (interpolation) { switch (interpolation) {
| `Stepwise => | `Stepwise =>
xyShape xyShape
|> XYShape.XtoY.stepwise(f) |> XYShape.XtoY.stepwiseIncremental(f)
|> E.O.default(0.0) |> E.O.default(0.0)
|> DistTypes.MixedPoint.makeContinuous |> DistTypes.MixedPoint.makeContinuous
| `Linear => | `Linear =>
@ -109,6 +109,14 @@ module Continuous = {
|> XYShape.XtoY.linear(f) |> XYShape.XtoY.linear(f)
|> DistTypes.MixedPoint.makeContinuous |> DistTypes.MixedPoint.makeContinuous
}; };
// let combineWithFn = (t1: t, t2: t, fn: (float, float) => float) => {
// switch(t1, t2){
// | ({interpolation: `Stepwise}, {interpolation: `Stepwise}) => 3.0
// | ({interpolation: `Linear}, {interpolation: `Linear}) => 3.0
// }
// };
let integral = (~cache, t) => let integral = (~cache, t) =>
cache cache
|> E.O.default( |> E.O.default(
@ -148,7 +156,7 @@ module Discrete = {
let toScaledDiscrete = t => Some(t); let toScaledDiscrete = t => Some(t);
let xToY = (f, t) => { let xToY = (f, t) => {
XYShape.XtoY.ifAtX(f, t) XYShape.XtoY.stepwiseIfAtX(f, t)
|> E.O.default(0.0) |> E.O.default(0.0)
|> DistTypes.MixedPoint.makeDiscrete; |> DistTypes.MixedPoint.makeDiscrete;
}; };
@ -254,9 +262,11 @@ module Mixed = {
~scale=discreteProbabilityMassFraction, ~scale=discreteProbabilityMassFraction,
); );
Continuous.make( Continuous.make(
XYShape.combine( XYShape.Combine.combineLinear(
Continuous.getShape(cont), Continuous.getShape(cont),
Continuous.getShape(dist), Continuous.getShape(dist),
(a, b) =>
a +. b
), ),
`Linear, `Linear,
); );

View File

@ -5,15 +5,21 @@ type t = xyShape;
let toJs = (t: t) => { let toJs = (t: t) => {
{"xs": t.xs, "ys": t.ys}; {"xs": t.xs, "ys": t.ys};
}; };
let minX = (t: t) => t.xs |> E.A.first; let xs = (t: t) => t.xs;
let maxX = (t: t) => t.xs |> E.A.last; let minX = (t: t) => t |> xs |> E.A.first;
let first = (t: t) => let maxX = (t: t) => t |> xs |> E.A.last;
switch (t.xs |> E.A.first, t.ys |> E.A.first) { let xTotalRange = (t: t) =>
switch (minX(t), maxX(t)) {
| (Some(min), Some(max)) => Some(max -. min)
| _ => None
};
let first = ({xs, ys}: t) =>
switch (xs |> E.A.first, ys |> E.A.first) {
| (Some(x), Some(y)) => Some((x, y)) | (Some(x), Some(y)) => Some((x, y))
| _ => None | _ => None
}; };
let last = (t: t) => let last = ({xs, ys}: t) =>
switch (t.xs |> E.A.last, t.ys |> E.A.last) { switch (xs |> E.A.last, ys |> E.A.last) {
| (Some(x), Some(y)) => Some((x, y)) | (Some(x), Some(y)) => Some((x, y))
| _ => None | _ => None
}; };
@ -21,7 +27,7 @@ let last = (t: t) =>
let unsafeFirst = (t: t) => first(t) |> E.O.toExn("Unsafe operation"); let unsafeFirst = (t: t) => first(t) |> E.O.toExn("Unsafe operation");
let unsafeLast = (t: t) => last(t) |> E.O.toExn("Unsafe operation"); let unsafeLast = (t: t) => last(t) |> E.O.toExn("Unsafe operation");
let zip = t => Belt.Array.zip(t.xs, t.ys); let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys);
let getBy = (t: t, fn) => t |> zip |> Belt.Array.getBy(_, fn); let getBy = (t: t, fn) => t |> zip |> Belt.Array.getBy(_, fn);
let firstPairAtOrBeforeValue = (xValue, t: t) => { let firstPairAtOrBeforeValue = (xValue, t: t) => {
@ -38,20 +44,64 @@ let firstPairAtOrBeforeValue = (xValue, t: t) => {
}; };
module XtoY = { module XtoY = {
let ifAtX = (f, t: t) => let stepwiseIncremental = (f, t: t) =>
getBy(t, ((x, _)) => x == f) |> E.O.fmap(((_, y)) => y);
let stepwise = (f, t: t) =>
firstPairAtOrBeforeValue(f, t) |> E.O.fmap(((_, y)) => y); firstPairAtOrBeforeValue(f, t) |> E.O.fmap(((_, y)) => y);
let stepwiseIfAtX = (f, t: t) =>
getBy(t, ((x, _)) => x == f) |> E.O.fmap(((_, y)) => y);
// TODO: When Roman's PR comes in, fix this bit. This depends on interpolation, obviously. // TODO: When Roman's PR comes in, fix this bit. This depends on interpolation, obviously.
let linear = (f, t: t) => t |> CdfLibrary.Distribution.findY(f); let linear = (f, t: t) => t |> CdfLibrary.Distribution.findY(f);
}; };
let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)}; let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)};
let xMap = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
let fromArray = ((xs, ys)): t => {xs, ys}; let fromArray = ((xs, ys)): t => {xs, ys};
let fromArrays = (xs, ys): t => {xs, ys}; let fromArrays = (xs, ys): t => {xs, ys};
module Combine = {
let combineLinear = (t1: t, t2: t, fn: (float, float) => float) => {
let allXs = Belt.Array.concat(xs(t1), xs(t2));
allXs |> Array.sort(compare);
let allYs =
allXs
|> E.A.fmap(x => {
let y1 = XtoY.linear(x, t1);
let y2 = XtoY.linear(x, t2);
fn(y1, y2);
});
fromArrays(allXs, allYs);
};
let combineStepwise =
(t1: t, t2: t, fn: (option(float), option(float)) => float) => {
let allXs = Belt.Array.concat(xs(t1), xs(t2));
allXs |> Array.sort(compare);
let allYs =
allXs
|> E.A.fmap(x => {
let y1 = XtoY.stepwiseIncremental(x, t1);
let y2 = XtoY.stepwiseIncremental(x, t2);
fn(y1, y2);
});
fromArrays(allXs, allYs);
};
let combineIfAtX =
(t1: t, t2: t, fn: (option(float), option(float)) => float) => {
let allXs = Belt.Array.concat(xs(t1), xs(t2));
allXs |> Array.sort(compare);
let allYs =
allXs
|> E.A.fmap(x => {
let y1 = XtoY.stepwiseIfAtX(x, t1);
let y2 = XtoY.stepwiseIfAtX(x, t2);
fn(y1, y2);
});
fromArrays(allXs, allYs);
};
};
// todo: maybe not needed? // todo: maybe not needed?
// let comparePoint = (a: float, b: float) => a > b ? 1 : (-1); // let comparePoint = (a: float, b: float) => a > b ? 1 : (-1);
@ -155,12 +205,19 @@ module Range = {
let derivative = mapYsBasedOnRanges(delta_y_over_delta_x); let derivative = mapYsBasedOnRanges(delta_y_over_delta_x);
let stepsToContinuous = t => // TODO: It would be nicer if this the diff didn't change the first element, and also maybe if there were a more elegant way of doing this.
Belt.Array.zip(t.xs, t.ys) let stepsToContinuous = t => {
|> E.A.toRanges let diff = xTotalRange(t) |> E.O.fmap(r => r *. 0.00001);
|> E.R.toOption switch (diff, E.A.toRanges(Belt.Array.zip(t.xs, t.ys))) {
|> E.O.fmap(r => r |> Belt.Array.map(_, rangePointAssumingSteps)) | (Some(diff), Ok(items)) =>
|> E.O.fmap(Belt.Array.unzip) Some(
|> E.O.fmap(fromArray) items
|> E.O.fmap(intersperce(t)); |> Belt.Array.map(_, rangePointAssumingSteps)
|> Belt.Array.unzip
|> fromArray
|> intersperce(t |> xMap(e => e +. diff)),
)
| _ => None
};
};
}; };