Add simple percentiles to distribution shower

This commit is contained in:
Ozzie Gooen 2020-03-14 18:33:39 +00:00
parent c9161d230e
commit f887af22ea
3 changed files with 111 additions and 8 deletions

View File

@ -6,6 +6,9 @@ let showAsForm = (distPlus: DistTypes.distPlus) => {
</div>;
};
let showFloat = (~precision=3, number) =>
<ForetoldComponents.NumberShower number precision />;
let table = (distPlus, x) => {
<div>
<table className="table-auto text-sm">
@ -120,6 +123,60 @@ let table = (distPlus, x) => {
</table>
</div>;
};
let percentiles = distPlus => {
<table className="table-auto text-sm">
<thead>
<tr>
<td className="px-4 py-2"> {"1" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"5" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"25" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"50" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"75" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"95" |> ReasonReact.string} </td>
<td className="px-4 py-2"> {"99" |> ReasonReact.string} </td>
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.01)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.05)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.25)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.5)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.75)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.95)
|> showFloat}
</td>
<td className="px-4 py-2 border">
{distPlus
|> Distributions.DistPlus.T.Integral.yToX(~cache=None, 0.99)
|> showFloat}
</td>
</tr>
</tbody>
</table>;
};
let adjustBoth = discreteProbabilityMass => {
let yMaxDiscreteDomainFactor = discreteProbabilityMass;
@ -287,8 +344,12 @@ let make = (~distPlus: DistTypes.distPlus) => {
|> E.L.toArray
|> ReasonReact.array}
<div className="inline-flex opacity-50 hover:opacity-100">
<button
className=button onClick={_ => dispatch(CHANGE_SHOW_PERCENTILES)}>
{"Percentiles" |> ReasonReact.string}
</button>
<button className=button onClick={_ => dispatch(CHANGE_SHOW_STATS)}>
{"Stats" |> ReasonReact.string}
{"Debug Stats" |> ReasonReact.string}
</button>
<button className=button onClick={_ => dispatch(CHANGE_SHOW_PARAMS)}>
{"Params" |> ReasonReact.string}
@ -299,5 +360,6 @@ let make = (~distPlus: DistTypes.distPlus) => {
</div>
{state.showParams ? showAsForm(distPlus) : ReasonReact.null}
{state.showStats ? table(distPlus, x) : ReasonReact.null}
{state.showPercentiles ? percentiles(distPlus) : ReasonReact.null}
</div>;
};

View File

@ -7,6 +7,7 @@ type chartConfig = {
type state = {
showStats: bool,
showPercentiles: bool,
showParams: bool,
distributions: list(chartConfig),
};
@ -14,6 +15,7 @@ type state = {
type action =
| CHANGE_SHOW_STATS
| CHANGE_SHOW_PARAMS
| CHANGE_SHOW_PERCENTILES
| REMOVE_DIST(int)
| ADD_DIST
| CHANGE_X_LOG(int)
@ -90,11 +92,16 @@ let reducer = (state: state, action: action) =>
}
| CHANGE_SHOW_STATS => {...state, showStats: !state.showStats}
| CHANGE_SHOW_PARAMS => {...state, showParams: !state.showParams}
| CHANGE_SHOW_PERCENTILES => {
...state,
showPercentiles: !state.showPercentiles,
}
};
let init = {
showStats: false,
showParams: false,
showPercentiles: true,
distributions: [
{yLog: false, xLog: false, isCumulative: false, height: 2},
{yLog: false, xLog: false, isCumulative: true, height: 1},

View File

@ -31,6 +31,7 @@ module type dist = {
let integral: (~cache: option(integral), t) => integral;
let integralEndY: (~cache: option(integral), t) => float;
let integralXtoY: (~cache: option(integral), float, t) => float;
let integralYtoX: (~cache: option(integral), float, t) => float;
};
module Dist = (T: dist) => {
@ -58,6 +59,7 @@ module Dist = (T: dist) => {
type t = T.integral;
let get = T.integral;
let xToY = T.integralXtoY;
let yToX = T.integralYtoX;
let sum = T.integralEndY;
};
@ -105,7 +107,7 @@ module Continuous = {
type integral = DistTypes.continuousShape;
let minX = shapeFn(XYShape.minX);
let maxX = shapeFn(XYShape.maxX);
let toDiscreteProbabilityMass = t => 0.0;
let toDiscreteProbabilityMass = _ => 0.0;
let pointwiseFmap = (fn, t: t) =>
t |> xyShape |> XYShape.pointwiseMap(fn) |> fromShape;
let toShape = (t: t): DistTypes.shape => Continuous(t);
@ -142,6 +144,8 @@ module Continuous = {
let integralEndY = (~cache, t) => t |> integral(~cache) |> lastY;
let integralXtoY = (~cache, f, t) =>
t |> integral(~cache) |> shapeFn(CdfLibrary.Distribution.findY(f));
let integralYtoX = (~cache, f, t) =>
t |> integral(~cache) |> shapeFn(CdfLibrary.Distribution.findX(f));
let toContinuous = t => Some(t);
let toDiscrete = _ => None;
let toScaledContinuous = t => Some(t);
@ -176,12 +180,19 @@ module Discrete = {
|> E.O.default(0.0)
|> 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) =>
t
|> integral(~cache)
|> Continuous.getShape
|> CdfLibrary.Distribution.findY(f);
let integralYtoX = (~cache, f, t) =>
t
|> integral(~cache)
|> Continuous.getShape
|> CdfLibrary.Distribution.findX(f);
});
};
@ -297,10 +308,18 @@ module Mixed = {
integral(~cache, t) |> Continuous.lastY;
};
let integralXtoY = (~cache, f, {discrete, continuous} as t: t) => {
let cont = Continuous.T.Integral.xToY(~cache, f, continuous);
let discrete = Discrete.T.Integral.xToY(~cache, f, discrete);
scaleDiscreteFn(t, discrete) +. scaleContinuousFn(t, cont);
let integralXtoY = (~cache, f, t) => {
t
|> integral(~cache)
|> Continuous.getShape
|> CdfLibrary.Distribution.findX(f);
};
let integralYtoX = (~cache, f, t) => {
t
|> integral(~cache)
|> Continuous.getShape
|> CdfLibrary.Distribution.findY(f);
};
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
@ -421,6 +440,16 @@ module Shape = {
),
);
};
let integralYtoX = (~cache, f, t) => {
mapToAll(
t,
(
Mixed.T.Integral.yToX(~cache, f),
Discrete.T.Integral.yToX(~cache, f),
Continuous.T.Integral.yToX(~cache, f),
),
);
};
let maxX = (t: t) =>
mapToAll(t, (Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX));
let pointwiseFmap = (fn, t: t) =>
@ -543,6 +572,11 @@ module DistPlus = {
Shape.T.Integral.xToY(~cache=Some(t.integralCache), f, toShape(t))
|> domainIncludedProbabilityMassAdjustment(t);
};
// TODO: This part is broken when there is a limit, if this is supposed to be taken into account.
let integralYtoX = (~cache as _, f, t: t) => {
Shape.T.Integral.yToX(~cache=Some(t.integralCache), f, toShape(t));
};
});
};