Added native logScoring and related functionality to XYShape
This commit is contained in:
parent
f887af22ea
commit
a99415e5bc
43
__tests__/XYShape__Test.re
Normal file
43
__tests__/XYShape__Test.re
Normal file
|
@ -0,0 +1,43 @@
|
|||
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)
|
||||
);
|
||||
|
||||
let shape1: DistTypes.xyShape = {xs: [|1., 4., 8.|], ys: [|0.2, 0.4, 0.8|]};
|
||||
|
||||
let shape2: DistTypes.xyShape = {
|
||||
xs: [|1., 5., 10.|],
|
||||
ys: [|0.2, 0.5, 0.8|],
|
||||
};
|
||||
|
||||
let shape3: DistTypes.xyShape = {
|
||||
xs: [|1., 20., 50.|],
|
||||
ys: [|0.2, 0.5, 0.8|],
|
||||
};
|
||||
|
||||
describe("XYShapes", () => {
|
||||
describe("logScorePoint", () => {
|
||||
makeTest(
|
||||
"When identical",
|
||||
XYShape.logScorePoint(30, shape1, shape1),
|
||||
Some(0.0),
|
||||
);
|
||||
makeTest(
|
||||
"When similar",
|
||||
XYShape.logScorePoint(30, shape1, shape2),
|
||||
Some(1.658971191043856),
|
||||
);
|
||||
makeTest(
|
||||
"When very different",
|
||||
XYShape.logScorePoint(30, shape1, shape3),
|
||||
Some(210.3721280423322),
|
||||
);
|
||||
})
|
||||
});
|
|
@ -134,12 +134,12 @@ let make =
|
|||
?xScale
|
||||
?yScale
|
||||
?timeScale
|
||||
discrete={discrete |> E.O.fmap(XYShape.toJs)}
|
||||
discrete={discrete |> E.O.fmap(XYShape.T.toJs)}
|
||||
height
|
||||
marginBottom=50
|
||||
marginTop=0
|
||||
onHover
|
||||
continuous={continuous |> E.O.fmap(XYShape.toJs)}
|
||||
continuous={continuous |> E.O.fmap(XYShape.T.toJs)}
|
||||
showDistributionLines
|
||||
showDistributionYAxis
|
||||
showVerticalLine
|
||||
|
|
|
@ -82,7 +82,7 @@ module Continuous = {
|
|||
interpolation,
|
||||
};
|
||||
let lastY = (t: t) =>
|
||||
t |> xyShape |> XYShape.unsafeLast |> (((_, y)) => y);
|
||||
t |> xyShape |> XYShape.T.unsafeLast |> (((_, y)) => y);
|
||||
let oShapeMap =
|
||||
(fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) =>
|
||||
fn(xyShape) |> E.O.fmap(make(_, interpolation));
|
||||
|
@ -105,22 +105,22 @@ module Continuous = {
|
|||
Dist({
|
||||
type t = DistTypes.continuousShape;
|
||||
type integral = DistTypes.continuousShape;
|
||||
let minX = shapeFn(XYShape.minX);
|
||||
let maxX = shapeFn(XYShape.maxX);
|
||||
let minX = shapeFn(XYShape.T.minX);
|
||||
let maxX = shapeFn(XYShape.T.maxX);
|
||||
let toDiscreteProbabilityMass = _ => 0.0;
|
||||
let pointwiseFmap = (fn, t: t) =>
|
||||
t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape;
|
||||
t |> xyShape |> XYShape.T.pointwiseMap(fn) |> fromShape;
|
||||
let toShape = (t: t): DistTypes.shape => Continuous(t);
|
||||
let xToY = (f, {interpolation, xyShape}: t) =>
|
||||
switch (interpolation) {
|
||||
| `Stepwise =>
|
||||
xyShape
|
||||
|> XYShape.XtoY.stepwiseIncremental(f)
|
||||
|> XYShape.T.XtoY.stepwiseIncremental(f)
|
||||
|> E.O.default(0.0)
|
||||
|> DistTypes.MixedPoint.makeContinuous
|
||||
| `Linear =>
|
||||
xyShape
|
||||
|> XYShape.XtoY.linear(f)
|
||||
|> XYShape.T.XtoY.linear(f)
|
||||
|> DistTypes.MixedPoint.makeContinuous
|
||||
};
|
||||
|
||||
|
@ -161,14 +161,14 @@ module Discrete = {
|
|||
let integral = (~cache, t) =>
|
||||
switch (cache) {
|
||||
| Some(c) => c
|
||||
| None => Continuous.make(XYShape.accumulateYs(t), `Stepwise)
|
||||
| None => Continuous.make(XYShape.T.accumulateYs(t), `Stepwise)
|
||||
};
|
||||
let integralEndY = (~cache, t) =>
|
||||
t |> integral(~cache) |> Continuous.lastY;
|
||||
let minX = XYShape.minX;
|
||||
let maxX = XYShape.maxX;
|
||||
let minX = XYShape.T.minX;
|
||||
let maxX = XYShape.T.maxX;
|
||||
let toDiscreteProbabilityMass = t => 1.0;
|
||||
let pointwiseFmap = XYShape.pointwiseMap;
|
||||
let pointwiseFmap = XYShape.T.pointwiseMap;
|
||||
let toShape = (t: t): DistTypes.shape => Discrete(t);
|
||||
let toContinuous = _ => None;
|
||||
let toDiscrete = t => Some(t);
|
||||
|
@ -176,7 +176,7 @@ module Discrete = {
|
|||
let toScaledDiscrete = t => Some(t);
|
||||
|
||||
let xToY = (f, t) => {
|
||||
XYShape.XtoY.stepwiseIfAtX(f, t)
|
||||
XYShape.T.XtoY.stepwiseIfAtX(f, t)
|
||||
|> E.O.default(0.0)
|
||||
|> DistTypes.MixedPoint.makeDiscrete;
|
||||
};
|
||||
|
@ -294,7 +294,7 @@ module Mixed = {
|
|||
|
||||
let result =
|
||||
Continuous.make(
|
||||
XYShape.Combine.combineLinear(
|
||||
XYShape.T.Combine.combineLinear(
|
||||
Continuous.getShape(cont), Continuous.getShape(dist), (a, b) =>
|
||||
a +. b
|
||||
),
|
||||
|
|
|
@ -10,8 +10,11 @@ type assumptions = {
|
|||
|
||||
let buildSimple = (~continuous, ~discrete): option(DistTypes.shape) => {
|
||||
let cLength =
|
||||
continuous |> Distributions.Continuous.getShape |> XYShape.xs |> E.A.length;
|
||||
let dLength = discrete |> XYShape.xs |> E.A.length;
|
||||
continuous
|
||||
|> Distributions.Continuous.getShape
|
||||
|> XYShape.T.xs
|
||||
|> E.A.length;
|
||||
let dLength = discrete |> XYShape.T.xs |> E.A.length;
|
||||
switch (cLength, dLength) {
|
||||
| (0 | 1, 0) => None
|
||||
| (0 | 1, _) => Some(Discrete(discrete))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
open DistTypes;
|
||||
|
||||
module T = {
|
||||
type t = xyShape;
|
||||
type ts = array(xyShape);
|
||||
|
||||
let toJs = (t: t) => {
|
||||
{"xs": t.xs, "ys": t.ys};
|
||||
|
@ -115,7 +117,6 @@ let comparePoints = ((x1: float, y1: float), (x2: float, y2: float)) =>
|
|||
|
||||
// todo: This is broken :(
|
||||
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;
|
||||
|
@ -144,7 +145,8 @@ let ySum = yFold((a, b) => a +. b);
|
|||
let _transverse = fn =>
|
||||
Belt.Array.reduce(_, [||], (items, (x, y)) =>
|
||||
switch (E.A.last(items)) {
|
||||
| Some((_, yLast)) => Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
||||
| Some((_, yLast)) =>
|
||||
Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
||||
| None => [|(x, y)|]
|
||||
}
|
||||
);
|
||||
|
@ -164,6 +166,7 @@ let subtractYs = _transverseShape((aCurrent, aLast) => aCurrent -. aLast);
|
|||
|
||||
let findY = CdfLibrary.Distribution.findY;
|
||||
let findX = CdfLibrary.Distribution.findX;
|
||||
};
|
||||
|
||||
// I'm really not sure this part is actually what we want at this point.
|
||||
module Range = {
|
||||
|
@ -171,7 +174,7 @@ module Range = {
|
|||
type zippedRange = ((float, float), (float, float));
|
||||
|
||||
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||
let toT = r => r |> Belt.Array.unzip |> fromArray;
|
||||
let toT = r => r |> Belt.Array.unzip |> T.fromArray;
|
||||
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
||||
|
||||
let rangePointAssumingSteps =
|
||||
|
@ -197,21 +200,21 @@ module Range = {
|
|||
let integrateWithTriangles = z => {
|
||||
let rangeItems = mapYsBasedOnRanges(rangeAreaAssumingTriangles, z);
|
||||
(
|
||||
switch (rangeItems, z |> first) {
|
||||
switch (rangeItems, z |> T.first) {
|
||||
| (Some(r), Some((firstX, _))) =>
|
||||
Some(Belt.Array.concat([|(firstX, 0.0)|], r))
|
||||
| _ => None
|
||||
}
|
||||
)
|
||||
|> E.O.fmap(toT)
|
||||
|> E.O.fmap(accumulateYs);
|
||||
|> E.O.fmap(T.accumulateYs);
|
||||
};
|
||||
|
||||
let derivative = mapYsBasedOnRanges(delta_y_over_delta_x);
|
||||
|
||||
// TODO: It would be nicer if this the diff didn't change the first element, and also maybe if there were a more elegant way of doing this.
|
||||
let stepsToContinuous = t => {
|
||||
let diff = xTotalRange(t) |> E.O.fmap(r => r *. 0.00001);
|
||||
let diff = T.xTotalRange(t) |> E.O.fmap(r => r *. 0.00001);
|
||||
let items =
|
||||
switch (diff, E.A.toRanges(Belt.Array.zip(t.xs, t.ys))) {
|
||||
| (Some(diff), Ok(items)) =>
|
||||
|
@ -219,21 +222,57 @@ module Range = {
|
|||
items
|
||||
|> Belt.Array.map(_, rangePointAssumingSteps)
|
||||
|> Belt.Array.unzip
|
||||
|> fromArray
|
||||
|> intersperce(t |> xMap(e => e +. diff)),
|
||||
|> T.fromArray
|
||||
|> T.intersperce(t |> T.xMap(e => e +. diff)),
|
||||
)
|
||||
| _ => Some(t)
|
||||
};
|
||||
let bar = items |> E.O.fmap(zip) |> E.O.bind(_, E.A.get(_, 0));
|
||||
let bar = items |> E.O.fmap(T.zip) |> E.O.bind(_, E.A.get(_, 0));
|
||||
let items =
|
||||
switch (items, bar) {
|
||||
| (Some(items), Some((0.0, _))) => Some(items)
|
||||
| (Some(items), Some((firstX, _))) =>
|
||||
let all = E.A.append([|(firstX, 0.0)|], items |> zip);
|
||||
let foo = all |> Belt.Array.unzip |> fromArray;
|
||||
let all = E.A.append([|(firstX, 0.0)|], items |> T.zip);
|
||||
let foo = all |> Belt.Array.unzip |> T.fromArray;
|
||||
Some(foo);
|
||||
| _ => None
|
||||
};
|
||||
items;
|
||||
};
|
||||
};
|
||||
|
||||
module Ts = {
|
||||
type t = T.ts;
|
||||
let minX = (t: t) =>
|
||||
t |> E.A.fmap(T.minX) |> E.A.O.concatSomes |> Functions.min;
|
||||
let maxX = (t: t) =>
|
||||
t |> E.A.fmap(T.maxX) |> E.A.O.concatSomes |> Functions.max;
|
||||
|
||||
// TODO/Warning: This will break if the shapes are empty.
|
||||
let equallyDividedXs = (t: t, newLength) => {
|
||||
Functions.range(minX(t), maxX(t), newLength);
|
||||
};
|
||||
};
|
||||
|
||||
let combinePointwise = (fn, sampleCount, t1: xyShape, t2: xyShape) => {
|
||||
let xs = Ts.equallyDividedXs([|t1, t2|], sampleCount);
|
||||
let ys =
|
||||
xs |> E.A.fmap(x => fn(T.XtoY.linear(x, t1), T.XtoY.linear(x, t2)));
|
||||
T.fromArrays(xs, ys);
|
||||
};
|
||||
|
||||
let logScoreDist =
|
||||
combinePointwise((prediction, answer) =>
|
||||
switch (answer) {
|
||||
| 0. => 0.0
|
||||
| answer =>
|
||||
answer *. Js.Math.log2(Js.Math.abs_float(prediction /. answer))
|
||||
}
|
||||
);
|
||||
|
||||
let logScorePoint = (sampleCount, t1, t2) =>
|
||||
logScoreDist(sampleCount, t1, t2)
|
||||
|> Range.integrateWithTriangles
|
||||
|> E.O.fmap(T.accumulateYs)
|
||||
|> E.O.bind(_, T.last)
|
||||
|> E.O.fmap(((_, y)) => y);
|
Loading…
Reference in New Issue
Block a user