Performance gains and added back mixtures
This commit is contained in:
parent
fff473b27c
commit
2c83c68d95
107
__tests__/Lodash__test.re
Normal file
107
__tests__/Lodash__test.re
Normal file
|
@ -0,0 +1,107 @@
|
|||
open Jest;
|
||||
open Expect;
|
||||
|
||||
let makeTest = (~only=false, str, item1, item2) =>
|
||||
only
|
||||
? Only.test(str, () =>
|
||||
expect(item1) |> toEqual(item2)
|
||||
)
|
||||
: test(str, () =>
|
||||
expect(item1) |> toEqual(item2)
|
||||
);
|
||||
|
||||
module FloatFloatMap = {
|
||||
module Id =
|
||||
Belt.Id.MakeComparable({
|
||||
type t = float;
|
||||
let cmp: (float, float) => int = Pervasives.compare;
|
||||
});
|
||||
|
||||
type t = Belt.MutableMap.t(Id.t, float, Id.identity);
|
||||
|
||||
let fromArray = (ar: array((float, float))) =>
|
||||
Belt.MutableMap.fromArray(ar, ~id=(module Id));
|
||||
let toArray = (t: t) => Belt.MutableMap.toArray(t);
|
||||
let empty = () => Belt.MutableMap.make(~id=(module Id));
|
||||
let increment = (el, t: t) =>
|
||||
Belt.MutableMap.update(
|
||||
t,
|
||||
el,
|
||||
fun
|
||||
| Some(n) => Some(n +. 1.0)
|
||||
| None => Some(1.0),
|
||||
);
|
||||
|
||||
let get = (el, t: t) => Belt.MutableMap.get(t, el);
|
||||
let fmap = (fn, t: t) => Belt.MutableMap.map(t, fn);
|
||||
};
|
||||
|
||||
let split = (sortedArray: array(float)) => {
|
||||
let continuous = [||];
|
||||
let discrete = FloatFloatMap.empty();
|
||||
Belt.Array.forEachWithIndex(
|
||||
sortedArray,
|
||||
(index, element) => {
|
||||
let maxIndex = (sortedArray |> Array.length) - 1;
|
||||
let possiblySimilarElements =
|
||||
(
|
||||
switch (index) {
|
||||
| 0 => [|index + 1|]
|
||||
| n when n == maxIndex => [|index - 1|]
|
||||
| _ => [|index - 1, index + 1|]
|
||||
}
|
||||
)
|
||||
|> Belt.Array.map(_, r => sortedArray[r]);
|
||||
let hasSimilarElement =
|
||||
Belt.Array.some(possiblySimilarElements, r => r == element);
|
||||
hasSimilarElement
|
||||
? FloatFloatMap.increment(element, discrete)
|
||||
: {
|
||||
let _ = Js.Array.push(element, continuous);
|
||||
();
|
||||
};
|
||||
();
|
||||
},
|
||||
);
|
||||
|
||||
(continuous, discrete);
|
||||
};
|
||||
|
||||
describe("Lodash", () => {
|
||||
describe("Lodash", () => {
|
||||
makeTest("min", Lodash.min([|1, 3, 4|]), 1);
|
||||
makeTest("max", Lodash.max([|1, 3, 4|]), 4);
|
||||
makeTest("uniq", Lodash.uniq([|1, 3, 4, 4|]), [|1, 3, 4|]);
|
||||
makeTest(
|
||||
"countBy",
|
||||
Lodash.countBy([|1, 3, 4, 4|], r => r),
|
||||
Js.Dict.fromArray([|("1", 1), ("3", 1), ("4", 2)|]),
|
||||
);
|
||||
makeTest(
|
||||
"split",
|
||||
split([|1.432, 1.33455, 2.0|]),
|
||||
([|1.432, 1.33455, 2.0|], FloatFloatMap.empty()),
|
||||
);
|
||||
makeTest(
|
||||
"split",
|
||||
split([|1.432, 1.33455, 2.0, 2.0, 2.0, 2.0|])
|
||||
|> (((c, disc)) => (c, disc |> FloatFloatMap.toArray)),
|
||||
([|1.432, 1.33455|], [|(2.0, 4.0)|]),
|
||||
);
|
||||
|
||||
let makeDuplicatedArray = count => {
|
||||
let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int);
|
||||
let sorted = arr |> Belt.SortArray.stableSortBy(_, compare);
|
||||
E.A.concatMany([|sorted, sorted, sorted, sorted|])
|
||||
|> Belt.SortArray.stableSortBy(_, compare);
|
||||
};
|
||||
|
||||
let (_, discrete) = split(makeDuplicatedArray(10));
|
||||
let toArr = discrete |> FloatFloatMap.toArray;
|
||||
makeTest("splitMedium", toArr |> Belt.Array.length, 10);
|
||||
|
||||
let (c, discrete) = split(makeDuplicatedArray(500));
|
||||
let toArr = discrete |> FloatFloatMap.toArray;
|
||||
makeTest("splitMedium", toArr |> Belt.Array.length, 500);
|
||||
})
|
||||
});
|
|
@ -151,7 +151,7 @@ let make = () => {
|
|||
~schema,
|
||||
~onSubmit=({state}) => {None},
|
||||
~initialState={
|
||||
guesstimatorString: "40 to 50",
|
||||
guesstimatorString: "mm(40 to 80, floor(50 to 80), [.5,.5])",
|
||||
domainType: "Complete",
|
||||
xPoint: "50.0",
|
||||
xPoint2: "60.0",
|
||||
|
@ -162,7 +162,7 @@ let make = () => {
|
|||
unit: "days",
|
||||
sampleCount: "10000",
|
||||
outputXYPoints: "500",
|
||||
truncateTo: "100",
|
||||
truncateTo: "0",
|
||||
kernelWidth: "5",
|
||||
},
|
||||
(),
|
||||
|
@ -239,12 +239,13 @@ let make = () => {
|
|||
&& !Js.Float.isNaN(outputXYPoints)
|
||||
&& !Js.Float.isNaN(truncateTo)
|
||||
&& sampleCount > 10.
|
||||
&& outputXYPoints > 10.
|
||||
&& truncateTo > 10. =>
|
||||
&& outputXYPoints > 10. =>
|
||||
Some({
|
||||
sampleCount: sampleCount |> int_of_float,
|
||||
outputXYPoints: outputXYPoints |> int_of_float,
|
||||
truncateTo: truncateTo |> int_of_float |> E.O.some,
|
||||
truncateTo:
|
||||
int_of_float(truncateTo) > 0
|
||||
? Some(int_of_float(truncateTo)) : None,
|
||||
kernelWidth: kernelWidth |> int_of_float,
|
||||
})
|
||||
| _ => None
|
||||
|
|
|
@ -215,13 +215,21 @@ module DistPlusChart = {
|
|||
|> T.toScaledContinuous
|
||||
|> E.O.fmap(Distributions.Continuous.getShape);
|
||||
let range = T.xTotalRange(distPlus);
|
||||
|
||||
// We subtract a bit from the range to make sure that it fits. Maybe this should be done in d3 instead.
|
||||
let minX =
|
||||
switch (T.minX(distPlus), range) {
|
||||
| (Some(min), Some(range)) => Some(min -. range *. 0.001)
|
||||
switch (
|
||||
distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.01),
|
||||
range,
|
||||
) {
|
||||
| (min, Some(range)) => Some(min -. range *. 0.001)
|
||||
| _ => None
|
||||
};
|
||||
|
||||
let maxX = T.maxX(distPlus);
|
||||
let maxX = {
|
||||
distPlus |> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.99);
|
||||
};
|
||||
|
||||
let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson;
|
||||
let toDiscreteProbabilityMass =
|
||||
distPlus |> Distributions.DistPlus.T.toDiscreteProbabilityMass;
|
||||
|
|
|
@ -347,11 +347,11 @@ module Mixed = {
|
|||
};
|
||||
|
||||
let integralXtoY = (~cache, f, t) => {
|
||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findX(f);
|
||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findY(f);
|
||||
};
|
||||
|
||||
let integralYtoX = (~cache, f, t) => {
|
||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findY(f);
|
||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findX(f);
|
||||
};
|
||||
|
||||
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
|
||||
|
|
|
@ -49,7 +49,6 @@ module T = {
|
|||
};
|
||||
|
||||
let findY = (x: float, t: t): float => {
|
||||
// todo: change getIndexBy to realize it's sorted
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(xs(t), x);
|
||||
let n =
|
||||
|
@ -75,37 +74,14 @@ module T = {
|
|||
n;
|
||||
};
|
||||
|
||||
let findYA = (x: float, t: t): float => {
|
||||
// todo: change getIndexBy to realize it's sorted
|
||||
let firstHigherIndex = Belt.Array.getIndexBy(xs(t), e => e >= x);
|
||||
switch (firstHigherIndex) {
|
||||
| None => maxY(t) |> E.O.default(0.0)
|
||||
| Some(0) => minY(t) |> E.O.default(0.0)
|
||||
| Some(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let needsInterpolation = xs(t)[lowerOrEqualIndex] != x;
|
||||
if (needsInterpolation) {
|
||||
Functions.interpolate(
|
||||
xs(t)[lowerOrEqualIndex],
|
||||
xs(t)[firstHigherIndex],
|
||||
ys(t)[lowerOrEqualIndex],
|
||||
ys(t)[firstHigherIndex],
|
||||
x,
|
||||
);
|
||||
} else {
|
||||
ys(t)[lowerOrEqualIndex];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let findX = (y: float, t: t): float => {
|
||||
let firstHigherIndex = Belt.Array.getIndexBy(ys(t), e => e >= y);
|
||||
let f: float =
|
||||
let firstHigherIndex =
|
||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(ys(t), y);
|
||||
let foundX =
|
||||
switch (firstHigherIndex) {
|
||||
| None => maxX(t) |> E.O.default(0.0)
|
||||
| Some(0) => minX(t) |> E.O.default(0.0)
|
||||
| Some(firstHigherIndex) =>
|
||||
| `overMax => maxX(t) |> E.O.default(0.0)
|
||||
| `underMin => minX(t) |> E.O.default(0.0)
|
||||
| `firstHigher(firstHigherIndex) =>
|
||||
let lowerOrEqualIndex =
|
||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||
let needsInterpolation = ys(t)[lowerOrEqualIndex] != y;
|
||||
|
@ -113,15 +89,15 @@ module T = {
|
|||
Functions.interpolate(
|
||||
ys(t)[lowerOrEqualIndex],
|
||||
ys(t)[firstHigherIndex],
|
||||
ys(t)[lowerOrEqualIndex],
|
||||
ys(t)[firstHigherIndex],
|
||||
xs(t)[lowerOrEqualIndex],
|
||||
xs(t)[firstHigherIndex],
|
||||
y,
|
||||
);
|
||||
} else {
|
||||
xs(t)[lowerOrEqualIndex];
|
||||
};
|
||||
};
|
||||
f;
|
||||
foundX;
|
||||
};
|
||||
|
||||
let convertWithAlternativeXs = (newXs: array(float), t: t): t => {
|
||||
|
@ -284,13 +260,17 @@ module T = {
|
|||
}
|
||||
);
|
||||
|
||||
let _transverseShape = (fn, p: t) => {
|
||||
let _transverseShape2 = (fn, p: t) => {
|
||||
Belt.Array.zip(p.xs, p.ys)
|
||||
|> _transverseB(fn)
|
||||
|> Belt.Array.unzip
|
||||
|> fromArray;
|
||||
};
|
||||
|
||||
let _transverseShape = (fn, p: t) => {
|
||||
fromArray((p.xs, _transverse2(fn, p.ys)));
|
||||
};
|
||||
|
||||
let filter = (fn, t: t) =>
|
||||
t |> zip |> E.A.filter(fn) |> Belt.Array.unzip |> fromArray;
|
||||
|
||||
|
|
|
@ -121,6 +121,63 @@ module KDE = {
|
|||
};
|
||||
};
|
||||
|
||||
module FloatFloatMap = {
|
||||
module Id =
|
||||
Belt.Id.MakeComparable({
|
||||
type t = float;
|
||||
let cmp: (float, float) => int = Pervasives.compare;
|
||||
});
|
||||
|
||||
type t = Belt.MutableMap.t(Id.t, float, Id.identity);
|
||||
|
||||
let fromArray = (ar: array((float, float))) =>
|
||||
Belt.MutableMap.fromArray(ar, ~id=(module Id));
|
||||
let toArray = (t: t) => Belt.MutableMap.toArray(t);
|
||||
let empty = () => Belt.MutableMap.make(~id=(module Id));
|
||||
let increment = (el, t: t) =>
|
||||
Belt.MutableMap.update(
|
||||
t,
|
||||
el,
|
||||
fun
|
||||
| Some(n) => Some(n +. 1.0)
|
||||
| None => Some(1.0),
|
||||
);
|
||||
|
||||
let get = (el, t: t) => Belt.MutableMap.get(t, el);
|
||||
let fmap = (fn, t: t) => Belt.MutableMap.map(t, fn);
|
||||
};
|
||||
|
||||
let split = (sortedArray: array(float)) => {
|
||||
let continuous = [||];
|
||||
let discrete = FloatFloatMap.empty();
|
||||
Belt.Array.forEachWithIndex(
|
||||
sortedArray,
|
||||
(index, element) => {
|
||||
let maxIndex = (sortedArray |> Array.length) - 1;
|
||||
let possiblySimilarElements =
|
||||
(
|
||||
switch (index) {
|
||||
| 0 => [|index + 1|]
|
||||
| n when n == maxIndex => [|index - 1|]
|
||||
| _ => [|index - 1, index + 1|]
|
||||
}
|
||||
)
|
||||
|> Belt.Array.map(_, r => sortedArray[r]);
|
||||
let hasSimilarElement =
|
||||
Belt.Array.some(possiblySimilarElements, r => r == element);
|
||||
hasSimilarElement
|
||||
? FloatFloatMap.increment(element, discrete)
|
||||
: {
|
||||
let _ = Js.Array.push(element, continuous);
|
||||
();
|
||||
};
|
||||
();
|
||||
},
|
||||
);
|
||||
|
||||
(continuous, discrete);
|
||||
};
|
||||
|
||||
let toMixed =
|
||||
(
|
||||
~string,
|
||||
|
@ -140,18 +197,12 @@ let toMixed =
|
|||
|
||||
let length = samples |> E.A.length;
|
||||
Array.fast_sort(compare, samples);
|
||||
// let items =
|
||||
// E.A.uniq(samples)
|
||||
// |> E.A.fmap(r => (r, samples |> E.A.filter(n => n == r) |> E.A.length));
|
||||
// let (discretePart, continuousPart) =
|
||||
// Belt.Array.partition(items, ((_, count)) => count > 1);
|
||||
let discretePart = [||];
|
||||
let continuousPart = samples;
|
||||
let (continuousPart, disc) = split(samples);
|
||||
let lengthFloat = float_of_int(length);
|
||||
let discrete: DistTypes.xyShape =
|
||||
discretePart
|
||||
|> E.A.fmap(((x, count)) =>
|
||||
(x, float_of_int(count) /. float_of_int(length))
|
||||
)
|
||||
disc
|
||||
|> FloatFloatMap.fmap(r => r /. lengthFloat)
|
||||
|> FloatFloatMap.toArray
|
||||
|> XYShape.T.fromZippedArray;
|
||||
let pdf: DistTypes.xyShape =
|
||||
continuousPart |> E.A.length > 20
|
||||
|
@ -171,6 +222,5 @@ let toMixed =
|
|||
| (None, Some(shape)) => Some(shape)
|
||||
| _ => None
|
||||
};
|
||||
timeMessage("Finished truncation");
|
||||
shape;
|
||||
};
|
5
src/utility/Lodash.re
Normal file
5
src/utility/Lodash.re
Normal file
|
@ -0,0 +1,5 @@
|
|||
[@bs.module "lodash"] external min: array('a) => 'a = "min";
|
||||
[@bs.module "lodash"] external max: array('a) => 'a = "max";
|
||||
[@bs.module "lodash"] external uniq: array('a) => array('a) = "uniq";
|
||||
[@bs.module "lodash"]
|
||||
external countBy: (array('a), 'a => 'b) => Js.Dict.t(int) = "countBy";
|
Loading…
Reference in New Issue
Block a user