First quick stab at mixed cdf integral
This commit is contained in:
parent
db023c0bef
commit
4d1cc7094f
|
@ -9,31 +9,11 @@ let shape: DistributionTypes.xyShape = {
|
|||
open Shape;
|
||||
|
||||
describe("Shape", () =>
|
||||
describe("XYShape", () => {
|
||||
test("#ySum", () =>
|
||||
describe("XYShape", () =>
|
||||
test("#ySum", ()
|
||||
=>
|
||||
expect(XYShape.ySum(shape)) |> toEqual(19.0)
|
||||
);
|
||||
test("#volume", () => {
|
||||
let shape: DistributionTypes.xyShape = {
|
||||
xs: [|1., 5., 10.|],
|
||||
ys: [|1., 2., 2.|],
|
||||
};
|
||||
expect(XYShape.volume(shape)) |> toEqual(Some(7.0));
|
||||
});
|
||||
test("#integral", () => {
|
||||
let expected: DistributionTypes.xyShape = {
|
||||
xs: [|1., 4., 8.|],
|
||||
ys: [|8., 17., 19.|],
|
||||
};
|
||||
expect(XYShape.volum2(shape)) |> toEqual(Some(expected));
|
||||
});
|
||||
test("#derivative", () => {
|
||||
let expected: DistributionTypes.xyShape = {
|
||||
xs: [|1., 4., 8.|],
|
||||
ys: [|8., 1., 1.|],
|
||||
};
|
||||
expect(XYShape.derivative(shape)) |> toEqual(expected);
|
||||
});
|
||||
)
|
||||
// test("#both", () => {
|
||||
// let expected: DistributionTypes.xyShape = {
|
||||
// xs: [|1., 4., 8.|],
|
||||
|
@ -42,5 +22,5 @@ describe("Shape", () =>
|
|||
// expect(shape |> XYShape.derivative |> XYShape.integral)
|
||||
// |> toEqual(shape);
|
||||
// });
|
||||
})
|
||||
)
|
||||
);
|
|
@ -8,7 +8,9 @@ let data: DistributionTypes.xyShape = {
|
|||
let mixedDist =
|
||||
GenericDistribution.make(
|
||||
~generationSource=
|
||||
GuesstimatorString("mm(uniform(10,12), normal(5,1), [.5,.5])"),
|
||||
GuesstimatorString(
|
||||
"mm(floor(uniform(40, 50)), normal(50,10), [.5,.5])",
|
||||
),
|
||||
~probabilityType=Pdf,
|
||||
~domain=Complete,
|
||||
~unit=Unspecified,
|
||||
|
@ -42,14 +44,14 @@ let distributions = () =>
|
|||
<h2> {"Basic Mixed Distribution" |> ReasonReact.string} </h2>
|
||||
<GenericDistributionChart dist=mixedDist />
|
||||
</div>
|
||||
<div>
|
||||
<h2> {"Time Distribution" |> ReasonReact.string} </h2>
|
||||
<GenericDistributionChart dist=timeDist />
|
||||
</div>
|
||||
<div>
|
||||
<h2> {"Domain Limited Distribution" |> ReasonReact.string} </h2>
|
||||
<GenericDistributionChart dist=domainLimitedDist />
|
||||
</div>
|
||||
</div>;
|
||||
// <div>
|
||||
// <h2> {"Time Distribution" |> ReasonReact.string} </h2>
|
||||
// <GenericDistributionChart dist=timeDist />
|
||||
// </div>
|
||||
// <div>
|
||||
// <h2> {"Domain Limited Distribution" |> ReasonReact.string} </h2>
|
||||
// <GenericDistributionChart dist=domainLimitedDist />
|
||||
// </div>
|
||||
|
||||
let entry = EntryTypes.(entry(~title="Pdf", ~render=distributions));
|
|
@ -35,7 +35,11 @@ module Continuous = {
|
|||
|> ReasonReact.string}
|
||||
</th>
|
||||
<th className="px-4 py-2 border ">
|
||||
{Shape.Continuous.findY(x, Shape.XYShape.integral(data))
|
||||
{Shape.Continuous.findY(
|
||||
x,
|
||||
Shape.XYShape.Range.integrateWithTriangles(data)
|
||||
|> E.O.toExt(""),
|
||||
)
|
||||
|> E.Float.with2DigitsPrecision
|
||||
|> ReasonReact.string}
|
||||
</th>
|
||||
|
@ -62,7 +66,34 @@ let make = (~dist) => {
|
|||
}) =>
|
||||
<div>
|
||||
<Continuous data=n />
|
||||
<Continuous data={n |> Shape.XYShape.integral} />
|
||||
<Continuous
|
||||
data={
|
||||
n
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|> E.O.toExt("")
|
||||
|> Shape.XYShape.scaleCdfTo
|
||||
}
|
||||
/>
|
||||
<Continuous
|
||||
data={
|
||||
n
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|> E.O.toExt("")
|
||||
|> Shape.XYShape.Range.derivative
|
||||
|> E.O.toExt("")
|
||||
}
|
||||
/>
|
||||
<Continuous
|
||||
data={
|
||||
n
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|> E.O.toExt("")
|
||||
|> Shape.XYShape.Range.derivative
|
||||
|> E.O.toExt("")
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|> E.O.toExt("")
|
||||
}
|
||||
/>
|
||||
{d |> Shape.Discrete.scaleYToTotal(f) |> Shape.Discrete.render}
|
||||
</div>
|
||||
| _ => <div />
|
||||
|
|
|
@ -19,6 +19,14 @@ module XYShape = {
|
|||
|
||||
let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)};
|
||||
|
||||
let scaleCdfTo = (~scaleTo=1., t: t) =>
|
||||
switch (_lastElement(t.ys)) {
|
||||
| Some(n) =>
|
||||
let scaleBy = scaleTo /. n;
|
||||
fmap(t, r => r *. scaleBy);
|
||||
| None => t
|
||||
};
|
||||
|
||||
let yFold = (fn, t: t) => {
|
||||
E.A.fold_left(fn, 0., t.ys);
|
||||
};
|
||||
|
@ -28,75 +36,69 @@ module XYShape = {
|
|||
let fromArray = ((xs, ys)): t => {xs, ys};
|
||||
let fromArrays = (xs, ys): t => {xs, ys};
|
||||
|
||||
let transverse = (fn, p: t) => {
|
||||
let (xs, ys) =
|
||||
Belt.Array.zip(p.xs, p.ys)
|
||||
->Belt.Array.reduce([||], (items, (x, y)) =>
|
||||
let _transverse = fn =>
|
||||
Belt.Array.reduce(_, [||], (items, (x, y)) =>
|
||||
switch (_lastElement(items)) {
|
||||
| Some((_, yLast)) =>
|
||||
Js.log3(y, yLast, fn(y, yLast));
|
||||
Belt.Array.concat(items, [|(x, fn(y, yLast))|]);
|
||||
| Some((xLast, yLast)) =>
|
||||
Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
||||
| None => [|(x, y)|]
|
||||
}
|
||||
)
|
||||
|> Belt.Array.unzip;
|
||||
fromArrays(xs, ys);
|
||||
);
|
||||
|
||||
let _transverseShape = (fn, p: t) => {
|
||||
Belt.Array.zip(p.xs, p.ys)
|
||||
|> _transverse(fn)
|
||||
|> Belt.Array.unzip
|
||||
|> fromArray;
|
||||
};
|
||||
|
||||
let accumulateYs = _transverseShape((aCurrent, aLast) => aCurrent +. aLast);
|
||||
let subtractYs = _transverseShape((aCurrent, aLast) => aCurrent -. aLast);
|
||||
|
||||
module Range = {
|
||||
// ((lastX, lastY), (nextX, nextY))
|
||||
type zippedRange = ((float, float), (float, float));
|
||||
|
||||
let inRanges = (fn, t: t) => {
|
||||
let ranges: Belt.Result.t(array(zippedRange), string) =
|
||||
Belt.Array.zip(t.xs, t.ys) |> E.A.toRanges;
|
||||
ranges |> E.R.toOption |> E.O.fmap(fn);
|
||||
};
|
||||
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||
let toT = r => r |> Belt.Array.unzip |> fromArray;
|
||||
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
||||
|
||||
let sum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||
|
||||
let volume = {
|
||||
let assumeLastY = (((lastX, lastY), (nextX, _)): zippedRange) =>
|
||||
let rangeAreaAssumingSteps =
|
||||
(((lastX, lastY), (nextX, _)): zippedRange) =>
|
||||
(nextX -. lastX) *. lastY;
|
||||
|
||||
inRanges((inRanges: array(zippedRange)) =>
|
||||
Belt.Array.map(inRanges, assumeLastY) |> sum
|
||||
);
|
||||
let rangeAreaAssumingTriangles =
|
||||
(((lastX, lastY), (nextX, nextY)): zippedRange) =>
|
||||
(nextX -. lastX) *. (lastY +. nextY) /. 2.;
|
||||
|
||||
let delta_y_over_delta_x =
|
||||
(((lastX, lastY), (nextX, nextY)): zippedRange) =>
|
||||
(nextY -. lastY) /. (nextX -. lastX);
|
||||
|
||||
let inRanges = (mapper, reducer, t: t) => {
|
||||
Belt.Array.zip(t.xs, t.ys)
|
||||
|> E.A.toRanges
|
||||
|> E.R.toOption
|
||||
|> E.O.fmap(r => r |> Belt.Array.map(_, mapper) |> reducer);
|
||||
};
|
||||
|
||||
let volumeTriangle = {
|
||||
let assumeLastY = (((lastX, lastY), (nextX, nextY)): zippedRange) =>
|
||||
(nextX -. lastX) *. (lastY -. nextY) /. 2.;
|
||||
let mapYsBasedOnRanges = fn => inRanges(r => (nextX(r), fn(r)), toT);
|
||||
|
||||
inRanges((inRanges: array(zippedRange)) =>
|
||||
Belt.Array.map(inRanges, assumeLastY) |> sum
|
||||
);
|
||||
let toStepFn = z => mapYsBasedOnRanges(rangeAreaAssumingSteps, z);
|
||||
|
||||
let integrateWithSteps = z =>
|
||||
mapYsBasedOnRanges(rangeAreaAssumingSteps, z) |> E.O.fmap(accumulateYs);
|
||||
|
||||
let integrateWithTriangles = z =>
|
||||
mapYsBasedOnRanges(rangeAreaAssumingTriangles, z)
|
||||
|> E.O.fmap(accumulateYs);
|
||||
|
||||
let derivative = mapYsBasedOnRanges(delta_y_over_delta_x);
|
||||
};
|
||||
|
||||
let volum2 = {
|
||||
let assumeLastY = (((lastX, lastY), (nextX, _)): zippedRange) => (
|
||||
nextX,
|
||||
(nextX -. lastX) *. lastY,
|
||||
);
|
||||
|
||||
inRanges((inRanges: array(zippedRange)) =>
|
||||
Belt.Array.map(inRanges, assumeLastY) |> Belt.Array.unzip |> fromArray
|
||||
);
|
||||
let findY = CdfLibrary.Distribution.findY;
|
||||
let findX = CdfLibrary.Distribution.findX;
|
||||
};
|
||||
|
||||
let diff = {
|
||||
let assumeLastY = (((lastX, lastY), (nextX, _)): zippedRange) => (
|
||||
nextX,
|
||||
(lastY -. lastY) /. (nextX -. lastX),
|
||||
);
|
||||
|
||||
inRanges((inRanges: array(zippedRange)) =>
|
||||
Belt.Array.map(inRanges, assumeLastY) |> Belt.Array.unzip |> fromArray
|
||||
);
|
||||
};
|
||||
let getY = (t: t, x: float) => x;
|
||||
let findY = (t: t, x: float) => x;
|
||||
|
||||
let integral = transverse((aCurrent, aLast) => aCurrent +. aLast);
|
||||
let derivative = transverse((aCurrent, aLast) => aCurrent -. aLast);
|
||||
// let massWithin = (t: t, left: pointInRange, right: pointInRange) => {
|
||||
// switch (left, right) {
|
||||
// | (Unbounded, Unbounded) => t |> ySum
|
||||
|
@ -105,15 +107,15 @@ module XYShape = {
|
|||
// | (X(l), X(r)) => getY(integral(t), r) -. getY(integral(t), l)
|
||||
// };
|
||||
// };
|
||||
};
|
||||
|
||||
module Continuous = {
|
||||
let fromArrays = XYShape.fromArrays;
|
||||
let toJs = XYShape.toJs;
|
||||
let toPdf = CdfLibrary.Distribution.toPdf;
|
||||
let toCdf = CdfLibrary.Distribution.toCdf;
|
||||
let toPdf = XYShape.Range.derivative;
|
||||
let toCdf = XYShape.Range.integrateWithTriangles;
|
||||
let findX = CdfLibrary.Distribution.findX;
|
||||
let findY = CdfLibrary.Distribution.findY;
|
||||
let findIntegralY = (f, r) => r |> toCdf |> E.O.fmap(findY(f));
|
||||
};
|
||||
|
||||
module Discrete = {
|
||||
|
@ -145,6 +147,18 @@ module Discrete = {
|
|||
| Some((_, y)) => y
|
||||
| None => 0.
|
||||
};
|
||||
|
||||
let integrate = XYShape.accumulateYs;
|
||||
let derivative = XYShape.subtractYs;
|
||||
|
||||
let findIntegralY = (f, t: t) =>
|
||||
t
|
||||
|> XYShape.Range.toStepFn
|
||||
|> E.O.fmap(XYShape.accumulateYs)
|
||||
|> E.O.fmap(CdfLibrary.Distribution.findY(f));
|
||||
|
||||
let findX = (f, t: t) =>
|
||||
t |> XYShape.Range.toStepFn |> E.O.fmap(CdfLibrary.Distribution.findX(f));
|
||||
};
|
||||
|
||||
module Mixed = {
|
||||
|
@ -154,14 +168,36 @@ module Mixed = {
|
|||
discreteProbabilityMassFraction,
|
||||
};
|
||||
|
||||
let mixedMultiply =
|
||||
(
|
||||
t: DistributionTypes.mixedShape,
|
||||
continuousComponent,
|
||||
discreteComponent,
|
||||
) => {
|
||||
let diffFn = t.discreteProbabilityMassFraction;
|
||||
continuousComponent *. (1.0 -. diffFn) +. discreteComponent *. diffFn;
|
||||
};
|
||||
|
||||
type yPdfPoint = {
|
||||
continuous: float,
|
||||
discrete: float,
|
||||
continuous: option(float),
|
||||
discrete: option(float),
|
||||
discreteProbabilityMassFraction: float,
|
||||
};
|
||||
|
||||
let getY = (t: DistributionTypes.mixedShape, x: float): yPdfPoint => {
|
||||
continuous: Continuous.findY(x, t.continuous),
|
||||
discrete: Discrete.findY(x, t.discrete),
|
||||
continuous: Continuous.findY(x, t.continuous) |> E.O.some,
|
||||
discrete: Discrete.findY(x, t.discrete) |> E.O.some,
|
||||
discreteProbabilityMassFraction: t.discreteProbabilityMassFraction,
|
||||
};
|
||||
|
||||
let getYIntegral =
|
||||
(t: DistributionTypes.mixedShape, x: float): option(float) => {
|
||||
let c = t.continuous |> Continuous.findIntegralY(x);
|
||||
let d = Discrete.findIntegralY(x, t.discrete);
|
||||
switch (c, d) {
|
||||
| (Some(c), Some(d)) => Some(mixedMultiply(t, c, d))
|
||||
| _ => None
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -175,13 +211,6 @@ module Any = {
|
|||
| Continuous(continuousShape) =>
|
||||
`continuous(Continuous.findY(x, continuousShape))
|
||||
};
|
||||
|
||||
let massInRange = (t: t, left: pointInRange, right: pointInRange) =>
|
||||
switch (t) {
|
||||
| Mixed(m) => 3.0
|
||||
| Discrete(discreteShape) => 2.0
|
||||
| Continuous(continuousShape) => 3.0
|
||||
};
|
||||
};
|
||||
|
||||
module DomainMixed = {
|
||||
|
|
Loading…
Reference in New Issue
Block a user