diff --git a/showcase/entries/Continuous.re b/showcase/entries/Continuous.re
index 94d683ee..c9d57e92 100644
--- a/showcase/entries/Continuous.re
+++ b/showcase/entries/Continuous.re
@@ -16,15 +16,13 @@ let mixedDist =
)
|> GenericDistribution.renderIfNeeded(~sampleCount=1000);
+// "mm(floor(uniform(30,35)), normal(50,20), [.25,.5])",
let timeDist =
GenericDistribution.make(
- ~generationSource=
- GuesstimatorString(
- "mm(floor(uniform(40, 50)), normal(50,10), [.5,.5])",
- ),
+ ~generationSource=GuesstimatorString("floor(normal(30,2))"),
~probabilityType=Pdf,
~domain=Complete,
- ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
+ ~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `days}),
(),
)
|> GenericDistribution.renderIfNeeded(~sampleCount=1000);
diff --git a/src/components/charts/GenericDistributionChart.re b/src/components/charts/GenericDistributionChart.re
index fad95085..7a4ca384 100644
--- a/src/components/charts/GenericDistributionChart.re
+++ b/src/components/charts/GenericDistributionChart.re
@@ -63,38 +63,55 @@ let continuousComponent = (p: DistributionTypes.pointsType) =>
| Continuous(c) => Some(c)
};
-module Cont = {
- [@react.component]
- let make = (~continuous, ~onHover, ~timeScale) => {
- let chart =
- React.useMemo1(
- () =>
- ,
- [|continuous|],
- );
- chart;
+let discreteScaleFactor = (p: DistributionTypes.pointsType) =>
+ switch (p) {
+ | Mixed(mixedShape) => Some(mixedShape.discreteProbabilityMassFraction)
+ | Discrete(_) => None
+ | Continuous(_) => None
};
-};
module Shapee = {
[@react.component]
let make = (~shape: DistributionTypes.pointsType, ~timeScale, ~onHover) => {
- let continuous = continuousComponent(shape);
- let discrete = discreteComponent(shape);
+ let discreteScaleFactor = shape |> discreteScaleFactor;
+ let continuous =
+ continuousComponent(shape)
+ |> E.O.bind(
+ _,
+ Shape.Continuous.scalePdf(
+ ~scaleTo=
+ discreteScaleFactor
+ |> E.O.fmap(r => 1. -. r)
+ |> E.O.default(1.0),
+ ),
+ );
+ let discrete =
+ discreteComponent(shape)
+ |> E.O.fmap(
+ Shape.Discrete.scaleYToTotal(
+ discreteScaleFactor |> E.O.default(1.0),
+ ),
+ );
+ let minX = {
+ Shape.Any.minX(shape);
+ };
+ let maxX = {
+ Shape.Any.maxX(shape);
+ };
{continuous
|> E.O.React.fmapOrNull(continuous =>
-
- )}
- {discrete
- |> E.O.React.fmapOrNull(r =>
- r |> Shape.Discrete.scaleYToTotal(0.3) |> Shape.Discrete.render
+
)}
+ {discrete |> E.O.React.fmapOrNull(Shape.Discrete.render)}
;
};
};
@@ -105,12 +122,19 @@ module GenericDist = {
let (x, setX) = React.useState(() => 0.);
let timeScale =
genericDistribution.unit |> DistributionTypes.DistributionUnit.toJson;
+ let chart =
+ React.useMemo1(
+ () => {
+ genericDistribution
+ |> DistributionTypes.shape
+ |> E.O.React.fmapOrNull(shape => {
+ setX(_ => r)} />
+ })
+ },
+ [|genericDistribution|],
+ );
- {genericDistribution
- |> DistributionTypes.shape
- |> E.O.React.fmapOrNull(shape => {
-
setX(_ => r)} />
- })}
+ chart
diff --git a/src/core/GenericDistribution.re b/src/core/GenericDistribution.re
index 787e7c3e..f59afda7 100644
--- a/src/core/GenericDistribution.re
+++ b/src/core/GenericDistribution.re
@@ -20,10 +20,26 @@ let renderIfNeeded =
switch (t.generationSource) {
| GuesstimatorString(s) =>
let shape = Guesstimator.stringToMixedShape(~string=s, ~sampleCount, ());
- shape
- |> E.O.fmap((shape: DistributionTypes.mixedShape) =>
+ let newShape =
+ switch (shape) {
+ | Some({
+ continuous: {xs: [||], ys: [||]},
+ discrete: {xs: [||], ys: [||]},
+ }) =>
+ None
+ | Some({continuous, discrete: {xs: [||], ys: [||]}}) =>
+ Some(Continuous(continuous))
+ | Some({continuous: {xs: [||], ys: [||]}, discrete}) =>
+ Some(Discrete(discrete))
+ | Some(shape) => Some(Mixed(shape))
+ | _ => None
+ };
+
+ Js.log(newShape);
+ newShape
+ |> E.O.fmap((shape: DistributionTypes.pointsType) =>
make(
- ~generationSource=Shape(Mixed(shape)),
+ ~generationSource=Shape(shape),
~probabilityType=Cdf,
~domain=t.domain,
~unit=t.unit,
@@ -52,7 +68,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => {
switch (t) {
| Mixed({continuous, discrete, discreteProbabilityMassFraction}) =>
continuous
- |> Shape.Continuous.normalizePdf
+ |> Shape.Continuous.scalePdf(~scaleTo=1.0)
|> E.O.fmap(r =>
Mixed({
continuous: r,
@@ -63,7 +79,7 @@ let normalizePdf = (t: DistributionTypes.pointsType) => {
| Discrete(d) => Some(Discrete(d |> Shape.Discrete.scaleYToTotal(1.0)))
| Continuous(continuousShape) =>
continuousShape
- |> Shape.Continuous.normalizePdf
+ |> Shape.Continuous.scalePdf(~scaleTo=1.0)
|> E.O.fmap(r => Continuous(r))
};
};
diff --git a/src/core/Shape.re b/src/core/Shape.re
index d44b8975..b3537574 100644
--- a/src/core/Shape.re
+++ b/src/core/Shape.re
@@ -17,6 +17,10 @@ module XYShape = {
{"xs": t.xs, "ys": t.ys};
};
+ let minX = (t: t) => t.xs |> E.A.get(_, 0);
+ // TODO: Check if this actually gets the last element, I'm not sure it does.
+ let maxX = (t: t) => t.xs |> (r => E.A.get(r, E.A.length(r) - 1));
+
let zip = t => Belt.Array.zip(t.xs, t.ys);
let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)};
@@ -101,6 +105,8 @@ module XYShape = {
};
module Continuous = {
+ let minX = XYShape.minX;
+ let maxX = XYShape.maxX;
let fromArrays = XYShape.fromArrays;
let toJs = XYShape.toJs;
let toPdf = XYShape.Range.derivative;
@@ -114,11 +120,16 @@ module Continuous = {
let normalizeCdf = (continuousShape: continuousShape) =>
continuousShape |> XYShape.scaleCdfTo(~scaleTo=1.0);
- let normalizePdf = (continuousShape: continuousShape) =>
- continuousShape |> toCdf |> E.O.fmap(normalizeCdf) |> E.O.bind(_, toPdf);
+ let scalePdf = (~scaleTo=1.0, continuousShape: continuousShape) =>
+ continuousShape
+ |> toCdf
+ |> E.O.fmap(XYShape.scaleCdfTo(~scaleTo))
+ |> E.O.bind(_, toPdf);
};
module Discrete = {
+ let minX = XYShape.minX;
+ let maxX = XYShape.maxX;
type t = discreteShape;
let fromArrays = XYShape.fromArrays;
let toJs = XYShape.toJs;
@@ -147,6 +158,7 @@ module Discrete = {
// TODO: This has a clear bug where it returns the Y value of the first item,
// even if X is less than the X of the first item.
+ // It has a second bug that it assumes things are triangular, instead of interpolating via steps.
let findIntegralY = (f, t: t) => {
t |> XYShape.accumulateYs |> CdfLibrary.Distribution.findY(f);
};
@@ -159,6 +171,22 @@ module Discrete = {
};
};
+let min = (f1: option(float), f2: option(float)) =>
+ switch (f1, f2) {
+ | (Some(f1), Some(f2)) => Some(f1 < f2 ? f1 : f2)
+ | (Some(f1), None) => Some(f1)
+ | (None, Some(f2)) => Some(f2)
+ | (None, None) => None
+ };
+
+let max = (f1: option(float), f2: option(float)) =>
+ switch (f1, f2) {
+ | (Some(f1), Some(f2)) => Some(f1 > f2 ? f1 : f2)
+ | (Some(f1), None) => Some(f1)
+ | (None, Some(f2)) => Some(f2)
+ | (None, None) => None
+ };
+
module Mixed = {
let make = (~continuous, ~discrete, ~discreteProbabilityMassFraction) => {
continuous,
@@ -166,6 +194,12 @@ module Mixed = {
discreteProbabilityMassFraction,
};
+ let minX = (t: DistributionTypes.mixedShape) =>
+ min(t.continuous |> Continuous.minX, t.discrete |> Discrete.minX);
+
+ let maxX = (t: DistributionTypes.mixedShape) =>
+ min(t.continuous |> Continuous.maxX, t.discrete |> Discrete.maxX);
+
let mixedMultiply =
(
t: DistributionTypes.mixedShape,
@@ -197,10 +231,6 @@ module Mixed = {
| _ => None
};
};
- //Do the math to add these distributions together
- // let integral =
- // (x: float, t: DistributionTypes.mixedShape): option(XYShape.t) => {
- // };
};
module Any = {
@@ -223,6 +253,20 @@ module Any = {
Continuous.findIntegralY(x, continuousShape)
};
+ let minX = (t: t) =>
+ switch (t) {
+ | Mixed(m) => Mixed.minX(m)
+ | Discrete(discreteShape) => Discrete.minX(discreteShape)
+ | Continuous(continuousShape) => Continuous.minX(continuousShape)
+ };
+
+ let maxX = (t: t) =>
+ switch (t) {
+ | Mixed(m) => Mixed.maxX(m)
+ | Discrete(discreteShape) => Discrete.maxX(discreteShape)
+ | Continuous(continuousShape) => Continuous.maxX(continuousShape)
+ };
+
// TODO: This is wrong. The discrete component should be made continuous when integrating.
let pdfToCdf = (t: t) =>
switch (t) {