Simple restructuring

This commit is contained in:
Ozzie Gooen 2020-02-23 18:34:34 +00:00
parent f844c9cc72
commit ad0212db39
14 changed files with 137 additions and 52 deletions

View File

@ -12,7 +12,7 @@ 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 => <DistPlusChart distPlus />)} {timeDist |> 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

@ -10,7 +10,7 @@ module DistPlusChart = {
let minX = T.minX(distPlus); let minX = T.minX(distPlus);
let maxX = T.maxX(distPlus); let maxX = T.maxX(distPlus);
let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson;
<DistributionChart <DistributionPlot
minX minX
maxX maxX
?discrete ?discrete
@ -31,12 +31,11 @@ module IntegralChart = {
let continuous = let continuous =
integral integral
|> T.toContinuous |> T.toContinuous
|> E.O.fmap(Distributions.Continuous.toLinear)
|> E.O.fmap(Distributions.Continuous.getShape); |> E.O.fmap(Distributions.Continuous.getShape);
let minX = T.minX(integral); let minX = T.minX(integral);
let maxX = T.maxX(integral); let maxX = T.maxX(integral);
let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson;
<DistributionChart <DistributionPlot
minX minX
maxX maxX
?continuous ?continuous

View File

@ -1,6 +1,6 @@
module RawChart = { module RawPlot = {
[@bs.module "./cdfChartReact.js"] [@bs.module "./distPlotReact.js"]
external cdfChart: ReasonReact.reactClass = "default"; external plot: ReasonReact.reactClass = "default";
type primaryDistribution = type primaryDistribution =
option({ option({
@ -36,7 +36,7 @@ module RawChart = {
~children=[||], ~children=[||],
) => ) =>
ReasonReact.wrapJsForReason( ReasonReact.wrapJsForReason(
~reactClass=cdfChart, ~reactClass=plot,
~props= ~props=
makeProps( makeProps(
~height?, ~height?,
@ -102,7 +102,7 @@ let make =
~timeScale=?, ~timeScale=?,
) => { ) => {
<div className={Styles.graph(color)}> <div className={Styles.graph(color)}>
<RawChart <RawPlot
?maxX ?maxX
?minX ?minX
?scale ?scale

View File

@ -1,6 +1,6 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useSize } from 'react-use'; import { useSize } from 'react-use';
import { CdfChartD3 } from './cdfChartD3'; import { CdfChartD3 } from './distPlotD3';
/** /**
* @param min * @param min

View File

@ -8,7 +8,7 @@ let make =
unit, unit,
}; };
let toDistPlus = (~sampleCount, t: distPlusIngredients) => { let toDistPlus = (~sampleCount, t: distPlusIngredients): option(distPlus) => {
let shape = let shape =
Guesstimator.stringToMixedShape( Guesstimator.stringToMixedShape(
~string=t.guesstimatorString, ~string=t.guesstimatorString,

View File

@ -74,7 +74,7 @@ module Continuous = {
}; };
let oShapeMap = let oShapeMap =
(fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) => (fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) =>
fn(xyShape) |> E.O.fmap(xyShape => make(xyShape, interpolation)); fn(xyShape) |> E.O.fmap(make(_, interpolation));
let toLinear = (t: t): t => let toLinear = (t: t): t =>
switch (t) { switch (t) {
@ -90,7 +90,8 @@ module Continuous = {
type t = DistTypes.continuousShape; type t = DistTypes.continuousShape;
type integral = DistTypes.continuousShape; type integral = DistTypes.continuousShape;
let shapeFn = (fn, t: t) => t |> xyShape |> fn; let shapeFn = (fn, t: t) => t |> xyShape |> fn;
// TODO: Obviously fix this, it's terrible // TODO: Obviously fix this, it's terrible. Use interpolation method here.
// TODO: Steps could be 1 value, interpolation needs at least 2.
let integral = (~cache, t) => let integral = (~cache, t) =>
cache cache
|> E.O.default( |> E.O.default(
@ -108,7 +109,7 @@ module Continuous = {
let pointwiseFmap = (fn, t: t) => let pointwiseFmap = (fn, t: t) =>
t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape; t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape;
let toShape = (t: t): DistTypes.shape => Continuous(t); let toShape = (t: t): DistTypes.shape => Continuous(t);
// TODO: When Roman's PR comes in, fix this bit. // TODO: When Roman's PR comes in, fix this bit. This depends on interpolation, obviously.
let xToY = (f, t) => let xToY = (f, t) =>
shapeFn(CdfLibrary.Distribution.findY(f), t) shapeFn(CdfLibrary.Distribution.findY(f), t)
|> DistTypes.MixedPoint.makeContinuous; |> DistTypes.MixedPoint.makeContinuous;
@ -126,6 +127,8 @@ module Discrete = {
Dist({ Dist({
type t = DistTypes.discreteShape; type t = DistTypes.discreteShape;
type integral = DistTypes.continuousShape; type integral = DistTypes.continuousShape;
// todo: test this. Remove "stepstoContinuos-move elsewhere"
// todo: Make sure this works fine with one value. This is important for step functionality.
let integral = (~cache, t) => let integral = (~cache, t) =>
cache cache
|> E.O.default( |> E.O.default(
@ -138,6 +141,7 @@ module Discrete = {
); );
}, },
); );
// todo: Fix this with last element
let integralSum = (~cache, t) => t |> XYShape.ySum; let integralSum = (~cache, t) => t |> XYShape.ySum;
let minX = XYShape.minX; let minX = XYShape.minX;
let maxX = XYShape.maxX; let maxX = XYShape.maxX;
@ -145,17 +149,20 @@ module Discrete = {
let toShape = (t: t): DistTypes.shape => Discrete(t); let toShape = (t: t): DistTypes.shape => Discrete(t);
let toContinuous = _ => None; let toContinuous = _ => None;
let toDiscrete = t => Some(t); let toDiscrete = t => Some(t);
let toScaledContinuous = t => None; let toScaledContinuous = _ => None;
let toScaledDiscrete = t => Some(t); let toScaledDiscrete = t => Some(t);
// todo: Fix this with code that work find recent value and use that instead.
let xToY = (f, t) => let xToY = (f, t) =>
CdfLibrary.Distribution.findY(f, t) CdfLibrary.Distribution.findY(f, t)
|> DistTypes.MixedPoint.makeDiscrete; |> DistTypes.MixedPoint.makeDiscrete;
// todo: This should use cache and/or same code as above. FindingY is more complex, should use interpolationType.
let integralXtoY = (~cache, f, t) => let integralXtoY = (~cache, f, t) =>
t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f); t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f);
}); });
}; };
module Mixed = { module Mixed = {
type t = DistTypes.mixedShape;
let make = let make =
(~continuous, ~discrete, ~discreteProbabilityMassFraction) (~continuous, ~discrete, ~discreteProbabilityMassFraction)
: DistTypes.mixedShape => { : DistTypes.mixedShape => {
@ -171,7 +178,8 @@ module Mixed = {
discrete: {xs: [||], ys: [||]}, discrete: {xs: [||], ys: [||]},
} => } =>
None None
| {discrete: {xs: [|_|], ys: [|_|]}} => None | {continuous, discrete: {xs: [|_|], ys: [|_|]}} =>
Some(Continuous(continuous))
| {continuous, discrete: {xs: [||], ys: [||]}} => | {continuous, discrete: {xs: [||], ys: [||]}} =>
Some(Continuous(continuous)) Some(Continuous(continuous))
| {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} => | {continuous: {xyShape: {xs: [||], ys: [||]}}, discrete} =>
@ -180,6 +188,7 @@ module Mixed = {
}; };
}; };
// todo: Put into scaling module
let scaleDiscreteFn = let scaleDiscreteFn =
({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) => ({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) =>
f *. discreteProbabilityMassFraction; f *. discreteProbabilityMassFraction;
@ -188,6 +197,13 @@ module Mixed = {
({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) => ({discreteProbabilityMassFraction}: DistTypes.mixedShape, f) =>
f *. (1.0 -. discreteProbabilityMassFraction); f *. (1.0 -. discreteProbabilityMassFraction);
let scaleContinuous = ({discreteProbabilityMassFraction}: t, continuous) =>
continuous
|> Continuous.T.scaleBy(~scale=1.0 -. discreteProbabilityMassFraction);
let scaleDiscrete = ({discreteProbabilityMassFraction}: t, disrete) =>
disrete |> Discrete.T.scaleBy(~scale=discreteProbabilityMassFraction);
module T = module T =
Dist({ Dist({
type t = DistTypes.mixedShape; type t = DistTypes.mixedShape;
@ -211,14 +227,6 @@ module Mixed = {
DistTypes.MixedPoint.add(c, d); DistTypes.MixedPoint.add(c, d);
}; };
let scaleContinuous =
({discreteProbabilityMassFraction}: t, continuous) =>
continuous
|> Continuous.T.scaleBy(~scale=1.0 -. discreteProbabilityMassFraction);
let scaleDiscrete = ({discreteProbabilityMassFraction}: t, disrete) =>
disrete |> Discrete.T.scaleBy(~scale=discreteProbabilityMassFraction);
let toScaledContinuous = ({continuous} as t: t) => let toScaledContinuous = ({continuous} as t: t) =>
Some(scaleContinuous(t, continuous)); Some(scaleContinuous(t, continuous));
@ -241,10 +249,17 @@ module Mixed = {
let dist = let dist =
discrete discrete
|> Discrete.T.Integral.get(~cache=None) |> Discrete.T.Integral.get(~cache=None)
|> Continuous.toLinear
|> Continuous.T.scaleBy( |> Continuous.T.scaleBy(
~scale=discreteProbabilityMassFraction, ~scale=discreteProbabilityMassFraction,
); );
dist; Continuous.make(
XYShape.combine(
Continuous.getShape(cont),
Continuous.getShape(dist),
),
`Linear,
);
}, },
); );
}; };
@ -268,6 +283,7 @@ module Mixed = {
scaleDiscreteFn(t, discrete) +. scaleContinuousFn(t, cont); scaleDiscreteFn(t, discrete) +. scaleContinuousFn(t, cont);
}; };
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
let pointwiseFmap = let pointwiseFmap =
(fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => { (fn, {discrete, continuous, discreteProbabilityMassFraction}: t): t => {
{ {
@ -285,6 +301,8 @@ module Shape = {
type t = DistTypes.shape; type t = DistTypes.shape;
type integral = DistTypes.continuousShape; type integral = DistTypes.continuousShape;
// todo: change order of arguments so t goes last.
// todo: Think of other name here?
let mapToAll = (t: t, (fn1, fn2, fn3)) => let mapToAll = (t: t, (fn1, fn2, fn3)) =>
switch (t) { switch (t) {
| Mixed(m) => fn1(m) | Mixed(m) => fn1(m)
@ -388,6 +406,9 @@ module Shape = {
module DistPlus = { module DistPlus = {
open DistTypes; open DistTypes;
type t = DistTypes.distPlus;
let make = let make =
( (
~shape, ~shape,
@ -396,10 +417,11 @@ module DistPlus = {
~unit=UnspecifiedDistribution, ~unit=UnspecifiedDistribution,
(), (),
) )
: distPlus => { : t => {
let integral = Shape.T.Integral.get(~cache=None, shape); let integral = Shape.T.Integral.get(~cache=None, shape);
{shape, domain, integralCache: integral, unit, guesstimatorString}; {shape, domain, integralCache: integral, unit, guesstimatorString};
}; };
let update = let update =
( (
~shape=?, ~shape=?,
@ -407,7 +429,7 @@ module DistPlus = {
~domain=?, ~domain=?,
~unit=?, ~unit=?,
~guesstimatorString=?, ~guesstimatorString=?,
t: distPlus, t: t,
) => { ) => {
shape: E.O.default(t.shape, shape), shape: E.O.default(t.shape, shape),
integralCache: E.O.default(t.integralCache, integralCache), integralCache: E.O.default(t.integralCache, integralCache),
@ -416,29 +438,32 @@ module DistPlus = {
guesstimatorString: E.O.default(t.guesstimatorString, guesstimatorString), guesstimatorString: E.O.default(t.guesstimatorString, guesstimatorString),
}; };
let domainIncludedProbabilityMass = (t: t) =>
Domain.includedProbabilityMass(t.domain);
let domainIncludedProbabilityMassAdjustment = (t: t, f) =>
f *. Domain.includedProbabilityMass(t.domain);
let toShape = ({shape, _}: t) => shape;
let shapeFn = (fn, {shape}: t) => fn(shape);
module T = module T =
Dist({ Dist({
type t = DistTypes.distPlus; type t = DistTypes.distPlus;
type integral = DistTypes.distPlus; type integral = DistTypes.distPlus;
let toShape = ({shape, _}: t) => shape; let toShape = toShape;
let shapeFn = (fn, t: t) => t |> toShape |> fn;
let toContinuous = shapeFn(Shape.T.toContinuous); let toContinuous = shapeFn(Shape.T.toContinuous);
let toDiscrete = shapeFn(Shape.T.toDiscrete); let toDiscrete = shapeFn(Shape.T.toDiscrete);
// todo: Adjust for total mass. // todo: Adjust for total mass.
let domainIncludedProbabilityMass = (t: t) =>
Domain.includedProbabilityMass(t.domain);
let domainIncludedProbabilityMassAdjustment = (t: t, f) =>
f *. Domain.includedProbabilityMass(t.domain);
let toScaledContinuous = (t: t) => { let toScaledContinuous = (t: t) => {
t t
|> toShape |> toShape
|> Shape.T.toScaledContinuous |> Shape.T.toScaledContinuous
|> E.O.fmap( |> E.O.fmap(
Continuous.T.pointwiseFmap(r => Continuous.T.pointwiseFmap(
r *. domainIncludedProbabilityMass(t) domainIncludedProbabilityMassAdjustment(t),
), ),
); );
}; };
@ -475,9 +500,41 @@ module DistPlus = {
let integralSum = (~cache as _, t: t) => let integralSum = (~cache as _, t: t) =>
Shape.T.Integral.sum(~cache=Some(t.integralCache), toShape(t)); Shape.T.Integral.sum(~cache=Some(t.integralCache), toShape(t));
// TODO: Fix this below, obviously. Adjust for limit. // TODO: Fix this below, obviously. Adjust for limits
let integralXtoY = (~cache as _, f, t: t) => { let integralXtoY = (~cache as _, f, t: t) => {
Shape.T.Integral.xToY(~cache=Some(t.integralCache), f, toShape(t)); Shape.T.Integral.xToY(~cache=Some(t.integralCache), f, toShape(t));
}; };
}); });
};
module DistPlusTime = {
open DistTypes;
open DistPlus;
type t = DistTypes.distPlus;
let unitToJson = ({unit}: t) => unit |> DistTypes.DistributionUnit.toJson;
let timeVector = ({unit}: t) =>
switch (unit) {
| TimeDistribution(timeVector) => Some(timeVector)
| UnspecifiedDistribution => None
};
let timeInVectorToX = (f: TimeTypes.timeInVector, t: t) => {
let timeVector = t |> timeVector;
timeVector |> E.O.fmap(TimeTypes.RelativeTimePoint.toXValue(_, f));
};
let xToY = (f: TimeTypes.timeInVector, t: t) => {
timeInVectorToX(f, t) |> E.O.fmap(DistPlus.T.xToY(_, t));
};
module Integral = {
include DistPlus.T.Integral;
let xToY = (~cache as _, f: TimeTypes.timeInVector, t: t) => {
timeInVectorToX(f, t)
|> E.O.fmap(x => DistPlus.T.Integral.xToY(~cache=None, x, t));
};
};
}; };

View File

@ -50,11 +50,11 @@ module TimePoint = {
MomentRe.diff(timeVector.zero, moment, timeVector.unit); MomentRe.diff(timeVector.zero, moment, timeVector.unit);
}; };
module RelativeTimePoint = { type timeInVector =
type timeInVector = | Time(MomentRe.Moment.t)
| Time(MomentRe.Moment.t) | XValue(float);
| XValue(float);
module RelativeTimePoint = {
let toTime = (timeVector: timeVector, timeInVector: timeInVector) => let toTime = (timeVector: timeVector, timeInVector: timeInVector) =>
switch (timeInVector) { switch (timeInVector) {
| Time(r) => r | Time(r) => r

View File

@ -23,6 +23,22 @@ let fromArray = ((xs, ys)): t => {xs, ys};
let fromArrays = (xs, ys): t => {xs, ys}; let fromArrays = (xs, ys): t => {xs, ys};
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 compare = (a: float, b: float) => a > b ? 1 : (-1);
let comparePoints = ((x1: float, y1: float), (x2: float, y2: float)) =>
switch (x1 == x2, y1 == y2) {
| (false, _) => compare(x1, x2)
| (true, false) => compare(y1, y2)
| (true, true) => (-1)
};
let combine = (t1: t, t2: t) => {
let totalLength = E.A.length(t1.xs) + E.A.length(t2.xs);
let array = Belt.Array.concat(zip(t1), zip(t2));
Array.sort(comparePoints, array);
array |> Belt.Array.unzip |> fromArray;
};
let intersperce = (t1: t, t2: t) => { let intersperce = (t1: t, t2: t) => {
let items: ref(array((float, float))) = ref([||]); let items: ref(array((float, float))) = ref([||]);
let t1 = zip(t1); let t1 = zip(t1);

View File

@ -22,7 +22,7 @@ let propValue = (t: Prop.Value.t) => {
DistPlusIngredients.toDistPlus(~sampleCount=1000, r); DistPlusIngredients.toDistPlus(~sampleCount=1000, r);
switch (newDistribution) { switch (newDistribution) {
| Some(distribution) => | Some(distribution) =>
<div> <DistPlusChart distPlus=distribution /> </div> <div> <DistPlusPlot distPlus=distribution /> </div>
| None => "Something went wrong" |> ReasonReact.string | None => "Something went wrong" |> ReasonReact.string
}; };
| FloatCdf(_) => <div /> | FloatCdf(_) => <div />

View File

@ -109,14 +109,20 @@ module Model = {
// TODO: Fixe number that integral is calculated for // TODO: Fixe number that integral is calculated for
let getGlobalCatastropheChance = dateTime => { let getGlobalCatastropheChance = dateTime => {
let model = GlobalCatastrophe.Model.make(dateTime); GlobalCatastrophe.makeI(MomentRe.momentNow())
switch (model) { |> DistPlusIngredients.toDistPlus(~sampleCount=1000)
| Prop.Value.DistPlusIngredients(distPlusIngredients) => |> E.O.bind(
distPlusIngredients _,
|> DistPlusIngredients.toDistPlus(~sampleCount=1000) t => {
|> E.O.fmap(Distributions.DistPlus.T.Integral.xToY(~cache=None, 18.0)) let foo =
| _ => None Distributions.DistPlusTime.Integral.xToY(
}; ~cache=None,
Time(dateTime),
t,
);
Some(0.5);
},
);
}; };
let make = let make =

View File

@ -1,5 +1,12 @@
let guesstimatorString = "20 to 80"; let guesstimatorString = "floor(10 to 20)";
let makeI = (currentDateTime: MomentRe.Moment.t) => {
DistPlusIngredients.make(
~guesstimatorString,
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
(),
);
};
module Model = { module Model = {
let make = (currentDateTime: MomentRe.Moment.t) => { let make = (currentDateTime: MomentRe.Moment.t) => {
let distPlusIngredients = let distPlusIngredients =