diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re
index 2d0d2e4d..5ff0567b 100644
--- a/showcase/entries/Continuous.re
+++ b/showcase/entries/Continuous.re
@@ -15,12 +15,8 @@ let timeDist =
);
let setup = dist =>
- dist
- |> DistPlusIngredients.toDistPlus(
- ~sampleCount=2000,
- ~outputXYPoints=1000,
- ~truncateTo=Some(1000),
- )
+ RenderTypes.DistPlus.make(~distPlusIngredients=dist,())
+ |> DistPlusIngredients.toDistPlus
|> E.O.React.fmapOrNull(distPlus => );
let simpleExample = (name, guesstimatorString) =>
diff --git a/src/Samples.re b/src/Samples.re
index 360f8279..093335dc 100644
--- a/src/Samples.re
+++ b/src/Samples.re
@@ -22,6 +22,7 @@ module KDE = {
|> JS.jsToDist;
};
+ // Note: This was an experiment, but it didn't actually work that well.
let inGroups = (samples, outputXYPoints, kernelWidth, ~cuttoff=0.9, ()) => {
let partitionAt =
samples
@@ -88,33 +89,94 @@ module T = {
(continuous, discrete);
};
- let kde = (~samples, ~outputXYPoints) => {
- let width = Bandwidth.nrd0(samples);
+ let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => {
let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0);
let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints);
- let kernelWidth = int_of_float(Jstat.max([|(width /. xyPointWidth), 1.0 |]));
- KDE.normalSampling(samples, outputXYPoints, kernelWidth);
+ xWidth /. xyPointWidth;
+ };
+
+ let formatUnitWidth = w => Jstat.max([|w, 1.0|]) |> int_of_float;
+
+ let suggestedUnitWidth = (samples, outputXYPoints) => {
+ let suggestedXWidth = Bandwidth.nrd0(samples);
+ xWidthToUnitWidth(samples, outputXYPoints, suggestedXWidth);
+ };
+
+ let kde = (~samples, ~outputXYPoints, width) => {
+ KDE.normalSampling(samples, outputXYPoints, width);
};
// todo: Figure out some way of doing this without having to integrate so many times.
- let toShape = (~samples: t, ~outputXYPoints=3000, ~kernelWidth=10, ()) => {
+ let toShape =
+ (~samples: t, ~samplingInputs: RenderTypes.Sampling.Inputs.fInputs, ()) => {
Array.fast_sort(compare, samples);
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples);
- let length = samples |> E.A.length;
- let lengthFloat = float_of_int(length);
+ let length = samples |> E.A.length |> float_of_int;
let discrete: DistTypes.xyShape =
discretePart
- |> E.FloatFloatMap.fmap(r => r /. lengthFloat)
+ |> E.FloatFloatMap.fmap(r => r /. length)
|> E.FloatFloatMap.toArray
|> XYShape.T.fromZippedArray;
- let pdf: DistTypes.xyShape =
+
+ let pdf =
continuousPart |> E.A.length > 5
? {
- continuousPart |> kde(~samples=_, ~outputXYPoints)
+ let _suggestedXWidth = Bandwidth.nrd0(continuousPart);
+ let _suggestedUnitWidth =
+ suggestedUnitWidth(continuousPart, samplingInputs.outputXYPoints);
+ let usedWidth =
+ samplingInputs.kernelWidth |> E.O.default(_suggestedXWidth);
+ let usedUnitWidth =
+ xWidthToUnitWidth(
+ samples,
+ samplingInputs.outputXYPoints,
+ usedWidth,
+ );
+ let foo: RenderTypes.Sampling.samplingStats = {
+ sampleCount: samplingInputs.sampleCount,
+ outputXYPoints: samplingInputs.outputXYPoints,
+ bandwidthXSuggested: _suggestedXWidth,
+ bandwidthUnitSuggested: _suggestedUnitWidth,
+ bandwidthXImplemented: usedWidth,
+ bandwidthUnitImplemented: usedUnitWidth,
+ };
+ continuousPart
+ |> kde(
+ ~samples=_,
+ ~outputXYPoints=samplingInputs.outputXYPoints,
+ formatUnitWidth(usedUnitWidth),
+ )
+ |> Distributions.Continuous.make(`Linear)
+ |> (r => Some((r, foo)));
}
- : {xs: [||], ys: [||]};
- let continuous = pdf |> Distributions.Continuous.make(`Linear);
- let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
- shape;
+ : None;
+ let shape =
+ MixedShapeBuilder.buildSimple(
+ ~continuous=pdf |> E.O.fmap(fst),
+ ~discrete,
+ );
+ let samplesParse: RenderTypes.Sampling.outputs = {
+ continuousParseParams: pdf |> E.O.fmap(snd),
+ shape,
+ };
+ samplesParse;
+ };
+
+ let fromGuesstimatorString =
+ (
+ ~guesstimatorString,
+ ~samplingInputs=RenderTypes.Sampling.Inputs.empty,
+ (),
+ ) => {
+ let hasValidSamples =
+ Guesstimator.stringToSamples(guesstimatorString, 10) |> E.A.length > 0;
+ let samplingInputs = RenderTypes.Sampling.Inputs.toF(samplingInputs);
+ switch (hasValidSamples) {
+ | false => None
+ | true =>
+ let samples =
+ Guesstimator.stringToSamples(guesstimatorString, samplingInputs.sampleCount);
+ Some(toShape(~samples, ~samplingInputs, ()));
+ };
};
};
\ No newline at end of file
diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re
index bfcbcb51..0ab50e95 100644
--- a/src/components/DistBuilder.re
+++ b/src/components/DistBuilder.re
@@ -26,7 +26,7 @@ type options = {
sampleCount: int,
outputXYPoints: int,
truncateTo: option(int),
- kernelWidth: int,
+ kernelWidth: option(float),
};
module Form = ReForm.Make(FormConfig);
@@ -111,6 +111,13 @@ module Styles = {
]);
};
+type inputs = {
+ samplingInputs: RenderTypes.Sampling.inputs,
+ guesstimatorString: string,
+ length: int,
+ shouldTruncateSampledDistribution: int,
+};
+
module DemoDist = {
[@react.component]
let make = (~guesstimatorString, ~domain, ~unit, ~options) => {
@@ -119,14 +126,24 @@ module DemoDist = {
{switch (domain, unit, options) {
| (Some(domain), Some(unit), Some(options)) =>
- let distPlus =
- DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ())
- |> DistPlusIngredients.toDistPlus(
- ~sampleCount=options.sampleCount,
- ~outputXYPoints=options.outputXYPoints,
- ~truncateTo=options.truncateTo,
- ~kernelWidth=options.kernelWidth,
- );
+ let distPlusIngredients =
+ DistPlusIngredients.make(
+ ~guesstimatorString,
+ ~domain,
+ ~unit,
+ (),
+ );
+ let inputs =
+ RenderTypes.DistPlus.make(
+ ~samplingInputs={
+ sampleCount: Some(options.sampleCount),
+ outputXYPoints: Some(options.outputXYPoints),
+ kernelWidth: options.kernelWidth,
+ },
+ ~distPlusIngredients,
+ (),
+ );
+ let distPlus = DistPlusIngredients.toDistPlus(inputs);
switch (distPlus) {
| Some(distPlus) =>
| _ =>
@@ -160,9 +177,9 @@ let make = () => {
unitType: "UnspecifiedDistribution",
zero: MomentRe.momentNow(),
unit: "days",
- sampleCount: "10000",
- outputXYPoints: "500",
- truncateTo: "100",
+ sampleCount: "30000",
+ outputXYPoints: "10000",
+ truncateTo: "1000",
kernelWidth: "5",
},
(),
@@ -246,7 +263,7 @@ let make = () => {
truncateTo:
int_of_float(truncateTo) > 0
? Some(int_of_float(truncateTo)) : None,
- kernelWidth: kernelWidth |> int_of_float,
+ kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
})
| _ => None
};
diff --git a/src/distribution/DistPlusIngredients.re b/src/distribution/DistPlusIngredients.re
index 36a7e663..098128a5 100644
--- a/src/distribution/DistPlusIngredients.re
+++ b/src/distribution/DistPlusIngredients.re
@@ -15,41 +15,34 @@ let applyTruncation = (truncateTo, distPlus) =>
| _ => None
};
+ // ~samplingInputs=RenderTypes.Sampling.Inputs.empty,
+ // ~truncateTo: option(int),
+ // t: distPlusIngredients,
+//Make truncation optional
let toDistPlus =
(
- ~sampleCount=2000,
- ~outputXYPoints=1500,
- ~truncateTo=Some(300),
- ~kernelWidth=5,
- t: distPlusIngredients,
+ inputs:RenderTypes.DistPlus.inputs
)
: option(distPlus) => {
let toDist = shape =>
Distributions.DistPlus.make(
~shape,
- ~domain=t.domain,
- ~unit=t.unit,
- ~guesstimatorString=Some(t.guesstimatorString),
+ ~domain=inputs.distPlusIngredients.domain,
+ ~unit=inputs.distPlusIngredients.unit,
+ ~guesstimatorString=Some(inputs.distPlusIngredients.guesstimatorString),
(),
)
|> Distributions.DistPlus.T.scaleToIntegralSum(~intendedSum=1.0);
- let parsed1 = MathJsParser.fromString(t.guesstimatorString);
let shape =
- switch (parsed1) {
- | Ok(r) =>
- let shape = SymbolicDist.toShape(truncateTo |> E.O.default(10000), r);
- Some(shape |> toDist);
- | _ =>
- let fewSamples = Guesstimator.stringToSamples(t.guesstimatorString, 10);
- if (fewSamples |> E.A.length > 0) {
- let samples =
- Guesstimator.stringToSamples(t.guesstimatorString, sampleCount);
- let shape =
- Samples.T.toShape(~samples, ~outputXYPoints, ~kernelWidth, ());
- shape |> E.O.fmap(toDist) |> applyTruncation(truncateTo);
- } else {
- None;
- };
- };
- shape;
+ GuesstimatorToShape.run(
+ ~renderingInputs={
+ guesstimatorString: inputs.distPlusIngredients.guesstimatorString,
+ shapeLength: inputs.recommendedLength,
+ },
+ ~samplingInputs=inputs.samplingInputs,
+ )
+ |> GuesstimatorToShape.getShape;
+ //TODO: Apply truncation
+ let foo = shape |> E.O.fmap(toDist);
+ foo;
};
\ No newline at end of file
diff --git a/src/distribution/GuesstimatorToShape.re b/src/distribution/GuesstimatorToShape.re
new file mode 100644
index 00000000..360fb787
--- /dev/null
+++ b/src/distribution/GuesstimatorToShape.re
@@ -0,0 +1,32 @@
+let runSymbolic =
+ (renderingInputs: RenderTypes.primaryInputs) =>{
+ let graph = MathJsParser.fromString(renderingInputs.guesstimatorString);
+ graph |> E.R.fmap(g => RenderTypes.Symbolic.make(g, SymbolicDist.toShape(renderingInputs.shapeLength,g)))
+ }
+
+let getShape = (r: RenderTypes.Combined.outputs) =>
+ switch (r.symbolic, r.sampling) {
+ | (Some(Ok({shape})), _) => Some(shape)
+ | (_, Some({shape})) => shape
+ | _ => None
+ };
+
+let run =
+ (
+ ~renderingInputs: RenderTypes.primaryInputs,
+ ~samplingInputs: RenderTypes.Sampling.inputs,
+ )
+ : RenderTypes.Combined.outputs => {
+ let symbolic = runSymbolic(renderingInputs);
+ let sampling =
+ switch (symbolic) {
+ | Ok(r) => None
+ | Error(r) =>
+ Samples.T.fromGuesstimatorString(
+ ~guesstimatorString=renderingInputs.guesstimatorString,
+ ~samplingInputs,
+ (),
+ )
+ };
+ {symbolic: Some(symbolic), sampling};
+};
\ No newline at end of file
diff --git a/src/distribution/MixedShapeBuilder.re b/src/distribution/MixedShapeBuilder.re
index cde9251c..c8195568 100644
--- a/src/distribution/MixedShapeBuilder.re
+++ b/src/distribution/MixedShapeBuilder.re
@@ -8,7 +8,8 @@ type assumptions = {
discreteProbabilityMass: option(float),
};
-let buildSimple = (~continuous, ~discrete): option(DistTypes.shape) => {
+let buildSimple = (~continuous: option(DistTypes.continuousShape), ~discrete): option(DistTypes.shape) => {
+ let continuous = continuous |> E.O.default(Distributions.Continuous.make(`Linear, {xs: [||], ys: [||]}))
let cLength =
continuous
|> Distributions.Continuous.getShape
diff --git a/src/distribution/RenderTypes.re b/src/distribution/RenderTypes.re
new file mode 100644
index 00000000..8f4b9882
--- /dev/null
+++ b/src/distribution/RenderTypes.re
@@ -0,0 +1,86 @@
+type primaryInputs = {
+ guesstimatorString: string,
+ shapeLength: int,
+};
+
+module Sampling = {
+ type inputs = {
+ sampleCount: option(int),
+ outputXYPoints: option(int),
+ kernelWidth: option(float),
+ };
+ type samplingStats = {
+ sampleCount: int,
+ outputXYPoints: int,
+ bandwidthXSuggested: float,
+ bandwidthUnitSuggested: float,
+ bandwidthXImplemented: float,
+ bandwidthUnitImplemented: float,
+ };
+ type outputs = {
+ continuousParseParams: option(samplingStats),
+ shape: option(ProbExample.DistTypes.shape),
+ };
+ module Inputs = {
+ let defaultSampleCount = 5000;
+ let defaultOutputXYPoints = 10000;
+ let empty = {sampleCount: None, outputXYPoints: None, kernelWidth: None};
+
+ type fInputs = {
+ sampleCount: int,
+ outputXYPoints: int,
+ kernelWidth: option(float),
+ };
+ let toF = (i: inputs): fInputs => {
+ sampleCount: i.sampleCount |> E.O.default(defaultSampleCount),
+ outputXYPoints: i.outputXYPoints |> E.O.default(defaultOutputXYPoints),
+ kernelWidth: i.kernelWidth,
+ };
+ };
+};
+
+module Symbolic = {
+ type inputs = {length: int};
+ type outputs = {
+ graph: ProbExample.SymbolicDist.bigDist,
+ shape: ProbExample.DistTypes.shape,
+ };
+ let make = (graph, shape) => {graph, shape};
+};
+
+module Combined = {
+ type inputs = {
+ samplingInputs: Sampling.inputs,
+ symbolicInputs: Symbolic.inputs,
+ guesstimatorString: string,
+ };
+ type outputs = {
+ symbolic: option(Belt.Result.t(Symbolic.outputs, string)),
+ sampling: option(Sampling.outputs),
+ };
+};
+
+module DistPlus = {
+ let defaultRecommendedLength = 10000;
+ let defaultShouldTruncate = true;
+ type inputs = {
+ distPlusIngredients: DistTypes.distPlusIngredients,
+ samplingInputs: Sampling.inputs,
+ recommendedLength: int,
+ shouldTruncate: bool,
+ };
+ let make =
+ (
+ ~samplingInputs=Sampling.Inputs.empty,
+ ~recommendedLength=defaultRecommendedLength,
+ ~shouldTruncate=defaultShouldTruncate,
+ ~distPlusIngredients,
+ ()
+ )
+ : inputs => {
+ distPlusIngredients,
+ samplingInputs,
+ recommendedLength,
+ shouldTruncate,
+ };
+};
\ No newline at end of file
diff --git a/src/interface/FormBuilder.re b/src/interface/FormBuilder.re
index 94e7b4cb..7280155b 100644
--- a/src/interface/FormBuilder.re
+++ b/src/interface/FormBuilder.re
@@ -19,12 +19,8 @@ let propValue = (t: Prop.Value.t) => {
| ConditionalArray(r) => "Array" |> ReasonReact.string
| DistPlusIngredients((r: DistTypes.distPlusIngredients)) =>
let newDistribution =
- DistPlusIngredients.toDistPlus(
- ~sampleCount=1000,
- ~outputXYPoints=2000,
- ~truncateTo=Some(500),
- r,
- );
+ RenderTypes.DistPlus.make(~distPlusIngredients=r, ~recommendedLength=1000, ~shouldTruncate=true,())
+ |> DistPlusIngredients.toDistPlus
switch (newDistribution) {
| Some(distribution) =>
diff --git a/src/models/EAFunds.re b/src/models/EAFunds.re
index aeacb1b2..bd42d96e 100644
--- a/src/models/EAFunds.re
+++ b/src/models/EAFunds.re
@@ -110,6 +110,7 @@ module Model = {
// TODO: Fixe number that integral is calculated for
let getGlobalCatastropheChance = dateTime => {
GlobalCatastrophe.makeI(MomentRe.momentNow())
+ |> RenderTypes.DistPlus.make(~distPlusIngredients=_, ())
|> DistPlusIngredients.toDistPlus
|> E.O.bind(_, Distributions.DistPlusTime.Integral.xToY(Time(dateTime)));
};
diff --git a/src/symbolic/MathJsParser.re b/src/symbolic/MathJsParser.re
index 4f6225ff..c5bae5bd 100644
--- a/src/symbolic/MathJsParser.re
+++ b/src/symbolic/MathJsParser.re
@@ -245,6 +245,5 @@ let fromString = str => {
}
);
let value = E.R.bind(mathJsParse, MathAdtToDistDst.run);
- Js.log4("fromString", mathJsToJson, mathJsParse, value);
value;
};
\ No newline at end of file
diff --git a/src/symbolic/SymbolicDist.re b/src/symbolic/SymbolicDist.re
index a4c33baf..cec8e1d6 100644
--- a/src/symbolic/SymbolicDist.re
+++ b/src/symbolic/SymbolicDist.re
@@ -296,7 +296,7 @@ module PointwiseAddDistributionsWeighted = {
let normalized = normalizeWeights(dists);
let continuous = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Continuous) |> continuousShape(_, sampleCount);
let discrete = normalized |> E.A.filter(((r,_)) => GenericSimple.contType(r) == `Discrete) |> discreteShape(_, sampleCount);
- let shape = MixedShapeBuilder.buildSimple(~continuous, ~discrete);
+ let shape = MixedShapeBuilder.buildSimple(~continuous=Some(continuous), ~discrete);
shape |> E.O.toExt("")
};