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);
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(
"integralXToY",
@ -251,13 +254,20 @@ describe("Shape", () => {
T.Integral.get(~cache=None, mixed),
Distributions.Continuous.make(
{
xs: [|1., 3., 4., 4., 7., 8., 8., 14.|],
ys: [|0.15, 0.0, 0.15, 0.4, 0.13986013986013987, 0.4, 0.5, 0.5|],
xs: [|1.00007, 3., 4., 4.00007, 7., 8., 8.00007, 14.|],
ys: [|
0.15,
0.15,
0.18496503496503497,
0.4349674825174825,
0.5398601398601399,
0.5913086913086913,
0.6913122927072927,
1.0,
|],
},
`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>
<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>
</div>
</div>;

View File

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

View File

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

View File

@ -5,15 +5,21 @@ type t = xyShape;
let toJs = (t: t) => {
{"xs": t.xs, "ys": t.ys};
};
let minX = (t: t) => t.xs |> E.A.first;
let maxX = (t: t) => t.xs |> E.A.last;
let first = (t: t) =>
switch (t.xs |> E.A.first, t.ys |> E.A.first) {
let xs = (t: t) => t.xs;
let minX = (t: t) => t |> xs |> E.A.first;
let maxX = (t: t) => t |> xs |> E.A.last;
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))
| _ => None
};
let last = (t: t) =>
switch (t.xs |> E.A.last, t.ys |> E.A.last) {
let last = ({xs, ys}: t) =>
switch (xs |> E.A.last, ys |> E.A.last) {
| (Some(x), Some(y)) => Some((x, y))
| _ => None
};
@ -21,7 +27,7 @@ let last = (t: t) =>
let unsafeFirst = (t: t) => first(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 firstPairAtOrBeforeValue = (xValue, t: t) => {
@ -38,20 +44,64 @@ let firstPairAtOrBeforeValue = (xValue, t: t) => {
};
module XtoY = {
let ifAtX = (f, t: t) =>
getBy(t, ((x, _)) => x == f) |> E.O.fmap(((_, y)) => y);
let stepwise = (f, t: t) =>
let stepwiseIncremental = (f, t: t) =>
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.
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 xMap = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
let fromArray = ((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?
// 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 stepsToContinuous = t =>
Belt.Array.zip(t.xs, t.ys)
|> E.A.toRanges
|> E.R.toOption
|> E.O.fmap(r => r |> Belt.Array.map(_, rangePointAssumingSteps))
|> E.O.fmap(Belt.Array.unzip)
|> E.O.fmap(fromArray)
|> E.O.fmap(intersperce(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.
let stepsToContinuous = t => {
let diff = xTotalRange(t) |> E.O.fmap(r => r *. 0.00001);
switch (diff, E.A.toRanges(Belt.Array.zip(t.xs, t.ys))) {
| (Some(diff), Ok(items)) =>
Some(
items
|> Belt.Array.map(_, rangePointAssumingSteps)
|> Belt.Array.unzip
|> fromArray
|> intersperce(t |> xMap(e => e +. diff)),
)
| _ => None
};
};
};