Simple visualization of probability distribution

This commit is contained in:
Ozzie Gooen 2020-02-13 12:20:46 +00:00
parent 5046a04c40
commit b8d0368313
8 changed files with 1083 additions and 42 deletions

View File

@ -15,6 +15,7 @@
"suffix": ".bs.js", "suffix": ".bs.js",
"namespace": true, "namespace": true,
"bs-dependencies": [ "bs-dependencies": [
"@foretold/components",
"bs-ant-design-alt", "bs-ant-design-alt",
"reason-react", "reason-react",
"bs-reform", "bs-reform",
@ -25,5 +26,5 @@
"refmt": 3, "refmt": 3,
"ppx-flags": [ "ppx-flags": [
"lenses-ppx/ppx" "lenses-ppx/ppx"
], ]
} }

View File

@ -23,6 +23,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@foretold/cdf": "^1.0.14", "@foretold/cdf": "^1.0.14",
"@foretold/components": "^0.0.3",
"@foretold/guesstimator": "^1.0.10", "@foretold/guesstimator": "^1.0.10",
"antd": "3.17.0", "antd": "3.17.0",
"autoprefixer": "^9.7.4", "autoprefixer": "^9.7.4",
@ -31,6 +32,7 @@
"bs-reform": "9.7.1", "bs-reform": "9.7.1",
"lenses-ppx": "4.0.0", "lenses-ppx": "4.0.0",
"less": "^3.10.3", "less": "^3.10.3",
"lodash": "^4.17.15",
"moment": "^2.24.0", "moment": "^2.24.0",
"parcel-bundler": "^1.12.4", "parcel-bundler": "^1.12.4",
"parcel-plugin-less-js-enabled": "^1.0.2", "parcel-plugin-less-js-enabled": "^1.0.2",
@ -42,10 +44,14 @@
"reschema": "^1.3.0" "reschema": "^1.3.0"
}, },
"devDependencies": { "devDependencies": {
"bs-platform": "^5.0.6", "bs-platform": "5.2.1",
"bsb-js": "^1.1.7", "bsb-js": "^1.1.7",
"gh-pages": "^2.2.0", "gh-pages": "^2.2.0",
"moduleserve": "^0.9.0", "moduleserve": "^0.9.0",
"tailwindcss": "^1.2.0" "tailwindcss": "^1.2.0"
},
"alias": {
"react": "./node_modules/react",
"react-dom": "./node_modules/react-dom"
} }
} }

View File

@ -51,10 +51,7 @@ module ModelForm = {
) )
|> ReasonReact.array} |> ReasonReact.array}
<div className="bg-green-100 p-2 rounded-sm mt-6 text-lg"> <div className="bg-green-100 p-2 rounded-sm mt-6 text-lg">
{model.run(formState.combo) {model.run(formState.combo) |> E.O.React.fmapOrNull(Value.display)}
|> E.O.fmap(Value.to_string)
|> E.O.default("")
|> ReasonReact.string}
</div> </div>
</form> </form>
</div>; </div>;

View File

@ -18,12 +18,43 @@ module Value = {
| Unselected => "" | Unselected => ""
} }
| SelectSingle(r) => r | SelectSingle(r) => r
| FloatCdf(r) => r | FloatCdf(r) =>
let foo: Types.distribution =
CdfLibrary.Distribution.fromString(r, 100);
r;
| Probability(r) => (r *. 100. |> Js.Float.toFixed) ++ "%" | Probability(r) => (r *. 100. |> Js.Float.toFixed) ++ "%"
| DateTime(r) => r |> MomentRe.Moment.defaultFormat | DateTime(r) => r |> MomentRe.Moment.defaultFormat
| FloatPoint(r) => r |> Js.Float.toFixed | FloatPoint(r) => r |> Js.Float.toFixed
}; };
}; };
let display = (t: t) => {
switch (t) {
| BinaryConditional(binaryConditional) =>
(
switch (binaryConditional) {
| Selected(r) => r ? "True" : "False"
| Unselected => ""
}
)
|> ReasonReact.string
| SelectSingle(r) => r |> ReasonReact.string
| FloatCdf(r) =>
let cdf: Types.distribution =
CdfLibrary.Distribution.fromString(r, 100);
<>
<ForetoldComponents.CdfChart__Large
cdf={Types.toComponentsDist(cdf)}
width={Some(400)}
/>
{r |> ReasonReact.string}
</>;
| Probability(r) =>
(r *. 100. |> Js.Float.toFixed) ++ "%" |> ReasonReact.string
| DateTime(r) => r |> MomentRe.Moment.defaultFormat |> ReasonReact.string
| FloatPoint(r) => r |> Js.Float.toFixed |> ReasonReact.string
};
};
}; };
module Type = { module Type = {

View File

@ -2,3 +2,8 @@ type distribution = {
xs: array(float), xs: array(float),
ys: array(float), ys: array(float),
}; };
let toComponentsDist = (d: distribution): ForetoldComponents.Types.Dist.t => {
xs: d.xs,
ys: d.ys,
};

View File

@ -14,26 +14,26 @@ module JS = {
let doAsDist = (f, d: Types.distribution) => d |> distToJs |> f |> jsToDist; let doAsDist = (f, d: Types.distribution) => d |> distToJs |> f |> jsToDist;
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external cdfToPdf: distJs => distJs = "cdfToPdf"; external cdfToPdf: distJs => distJs = "cdfToPdf";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external pdfToCdf: distJs => distJs = "pdfToCdf"; external pdfToCdf: distJs => distJs = "pdfToCdf";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external findY: (float, distJs) => float = "findY"; external findY: (float, distJs) => float = "findY";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external findX: (float, distJs) => float = "findX"; external findX: (float, distJs) => float = "findX";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external integral: distJs => float = "integral"; external integral: distJs => float = "integral";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external differentialEntropy: (int, distJs) => distJs = external differentialEntropy: (int, distJs) => distJs =
"differentialEntropy"; "differentialEntropy";
[@bs.module "./CdfLibraryImporter.js"] [@bs.module "./CdfLibrary.js"]
external scoreNonMarketCdfCdf: (int, distJs, distJs, float) => distJs = external scoreNonMarketCdfCdf: (int, distJs, distJs, float) => distJs =
"scoreNonMarketCdfCdf"; "scoreNonMarketCdfCdf";

View File

@ -1,18 +1,51 @@
import { Guesstimator } from '@foretold/guesstimator'; import { Guesstimator } from '@foretold/guesstimator';
import { Samples } from '@foretold/cdf'; import { Samples } from '@foretold/cdf';
import _ from 'lodash';
const toPdf = (values, min, max) => { /**
*
* @param {number} minValue
* @param {number} maxValue
* @returns {string}
*/
const minMaxRatio = (minValue, maxValue) => {
if (minValue === 0 || maxValue === 0) {
return 'SMALL';
}
const ratio = maxValue / minValue;
if (ratio < 100000) {
return 'SMALL';
} else if (ratio < 10000000) {
return 'MEDIUM';
} else {
return 'LARGE';
}
};
/**
* @param samples
* @return {string}
*/
const ratioSize = samples => {
samples.sort();
const minValue = samples.getPercentile(2);
const maxValue = samples.getPercentile(98);
return minMaxRatio(minValue, maxValue);
};
const toPdf = (values, sampleCount, min, max) => {
const samples = new Samples(values); const samples = new Samples(values);
const ratioSize$ = ratioSize(samples); const ratioSize$ = ratioSize(samples);
const width = ratioSize$ === 'SMALL' ? 20 : 1; const width = ratioSize$ === 'SMALL' ? 20 : 1;
const pdf = samples.toPdf({ size: 1000, width, min, max }); const cdf = samples.toCdf({ size: sampleCount, width, min, max });
return {ys:pdf.ys, xs:pdf.xs}; return {ys:cdf.ys, xs:cdf.xs};
}; };
let run = (text, sampleCount, inputs=[], min=false, max=false) => { let run = (text, sampleCount, inputs=[], min=false, max=false) => {
let [_error, item] = Guesstimator.parse({ text }); console.log(text);
let [_error, item] = Guesstimator.parse({ text: "=" + text });
const { parsedInput } = item; const { parsedInput } = item;
const { guesstimateType } = parsedInput; const { guesstimateType } = parsedInput;
@ -25,18 +58,13 @@ let run = (text, sampleCount, inputs=[], min=false, max=false) => {
const values = _.filter(value.values, _.isFinite); const values = _.filter(value.values, _.isFinite);
this.setState({
value: event.target.value,
items: values,
});
let update; let update;
if (values.length === 0) { if (values.length === 0) {
update = {xs: [], ys: []}; update = {xs: [], ys: []};
} else if (values.length === 1) { } else if (values.length === 1) {
update = {xs: [], ys: []}; update = {xs: [], ys: []};
} else { } else {
update = toPdf(values, min, max); update = toPdf(values, sampleCount, min, max);
} }
return update; return update;
} }

1009
yarn.lock

File diff suppressed because it is too large Load Diff