Merge pull request #1 from foretold-app/improvements/1080
Improvements/1080
This commit is contained in:
commit
00301ec7d3
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -12,4 +12,5 @@ build
|
|||
yarn-error.log
|
||||
*.bs.js
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.netlify
|
||||
.idea
|
51
package.json
51
package.json
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "probtest",
|
||||
"name": "estiband",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://foretold-app.github.io/estiband/",
|
||||
"scripts": {
|
||||
|
@ -26,38 +26,35 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@foretold/cdf": "^1.0.15",
|
||||
"@foretold/components": "^0.0.3",
|
||||
"@foretold/guesstimator": "^1.0.10",
|
||||
"@glennsl/bs-jest": "^0.4.9",
|
||||
"@foretold/cdf": "1.0.15",
|
||||
"@foretold/components": "0.0.3",
|
||||
"@foretold/guesstimator": "1.0.10",
|
||||
"antd": "3.17.0",
|
||||
"autoprefixer": "^9.7.4",
|
||||
"babel-jest": "^25.1.0",
|
||||
"autoprefixer": "9.7.4",
|
||||
"babel-jest": "25.1.0",
|
||||
"bs-ant-design-alt": "2.0.0-alpha.31",
|
||||
"bs-css": "^11.0.0",
|
||||
"bs-css": "11.0.0",
|
||||
"bs-moment": "0.4.4",
|
||||
"bs-reform": "9.7.1",
|
||||
"d3": "^5.15.0",
|
||||
"d3": "5.15.0",
|
||||
"lenses-ppx": "5.1.0",
|
||||
"jest": "^25.1.0",
|
||||
"lenses-ppx": "4.0.0",
|
||||
"less": "^3.10.3",
|
||||
"lodash": "^4.17.15",
|
||||
"moment": "^2.24.0",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"parcel-plugin-less-js-enabled": "^1.0.2",
|
||||
"postcss-cli": "^7.1.0",
|
||||
"rationale": "^0.2.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1",
|
||||
"less": "3.10.3",
|
||||
"lodash": "4.17.15",
|
||||
"moment": "2.24.0",
|
||||
"parcel-bundler": "1.12.4",
|
||||
"parcel-plugin-less-js-enabled": "1.0.2",
|
||||
"postcss-cli": "7.1.0",
|
||||
"rationale": "0.2.0",
|
||||
"react": "16.12.0",
|
||||
"react-dom": "16.12.0",
|
||||
"reason-react": ">=0.7.0",
|
||||
"reschema": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bs-platform": "5.2.1",
|
||||
"bsb-js": "^1.1.7",
|
||||
"gh-pages": "^2.2.0",
|
||||
"moduleserve": "^0.9.0",
|
||||
"tailwindcss": "^1.2.0"
|
||||
"reschema": "1.3.0",
|
||||
"bs-platform": "7.0.1",
|
||||
"bsb-js": "1.1.7",
|
||||
"gh-pages": "2.2.0",
|
||||
"moduleserve": "0.9.1",
|
||||
"tailwindcss": "1.2.0"
|
||||
},
|
||||
"alias": {
|
||||
"react": "./node_modules/react",
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
open ForetoldComponents.Base;
|
||||
|
||||
let data: DistributionTypes.xyShape = {
|
||||
xs: [|1., 10., 10., 200., 250., 292., 330.|],
|
||||
ys: [|0.0, 0.0, 0.1, 0.3, 0.5, 0.2, 0.1|],
|
||||
|
@ -13,7 +11,7 @@ let mixedDist =
|
|||
),
|
||||
~probabilityType=Pdf,
|
||||
~domain=Complete,
|
||||
~unit=Unspecified,
|
||||
~unit=UnspecifiedDistribution,
|
||||
(),
|
||||
)
|
||||
|> GenericDistribution.renderIfNeeded(~sampleCount=1000);
|
||||
|
@ -23,7 +21,7 @@ let timeDist =
|
|||
~generationSource=GuesstimatorString("mm(3, normal(5,1), [.5,.5])"),
|
||||
~probabilityType=Pdf,
|
||||
~domain=Complete,
|
||||
~unit=Time({zero: MomentRe.momentNow(), unit: `years}),
|
||||
~unit=TimeDistribution({zero: MomentRe.momentNow(), unit: `years}),
|
||||
(),
|
||||
)
|
||||
|> GenericDistribution.renderIfNeeded(~sampleCount=1000);
|
||||
|
@ -33,7 +31,7 @@ let domainLimitedDist =
|
|||
~generationSource=GuesstimatorString("mm(3, normal(5,1), [.5,.5])"),
|
||||
~probabilityType=Pdf,
|
||||
~domain=RightLimited({xPoint: 6.0, excludingProbabilityMass: 0.3}),
|
||||
~unit=Unspecified,
|
||||
~unit=UnspecifiedDistribution,
|
||||
(),
|
||||
)
|
||||
|> GenericDistribution.renderIfNeeded(~sampleCount=1000);
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
// type t = {
|
||||
// distribution: Types.ContinuousDistribution.t,
|
||||
// domainMaxX: float,
|
||||
// };
|
||||
// let make = (~distribution, ~domainMaxX): t => {distribution, domainMaxX};
|
||||
// let fromCdf =
|
||||
// (
|
||||
// cdf: Types.ContinuousDistribution.t,
|
||||
// domainMaxX: float,
|
||||
// probabilityAtMaxX: float,
|
||||
// ) => {
|
||||
// let distribution: Types.ContinuousDistribution.t = {
|
||||
// xs: cdf.xs,
|
||||
// ys: cdf.ys |> E.A.fmap(r => r *. probabilityAtMaxX),
|
||||
// };
|
||||
// {distribution, domainMaxX};
|
||||
// };
|
||||
// let _lastElement = (a: array('a)) =>
|
||||
// switch (Belt.Array.size(a)) {
|
||||
// | 0 => None
|
||||
// | n => Belt.Array.get(a, n)
|
||||
// };
|
||||
// let probabilityBeforeDomainMax = (t: t) => _lastElement(t.distribution.ys);
|
||||
// let domainMaxX = (t: t) => t.domainMaxX /* CdfLibrary.Distribution.findX(yPoint, t.distribution)*/;
|
||||
// let probabilityDistribution = (t: t) =>
|
||||
// t.distribution |> CdfLibrary.Distribution.toPdf;
|
||||
// let probability = (t: t, xPoint: float) =>
|
||||
// CdfLibrary.Distribution.findY(xPoint, probabilityDistribution(t));
|
||||
// let probabilityInverse = (t: t, yPoint: float) =>
|
||||
// CdfLibrary.Distribution.findX(yPoint, probabilityDistribution(t));
|
||||
// let cumulativeProbability = (t: t, xPoint: float) =>
|
||||
// CdfLibrary.Distribution.findY(xPoint, t.distribution);
|
||||
/* let cumulativeProbabilityInverse = (t: t, yPoint: float) =*/
|
|
@ -1,50 +0,0 @@
|
|||
// open EAFunds_Data;
|
||||
// let handleChange = (handleChange, event) =>
|
||||
// handleChange(ReactEvent.Form.target(event)##value);
|
||||
// [@react.component]
|
||||
// let make = () => {
|
||||
// let (year, setYear) = React.useState(() => 2021.);
|
||||
// <>
|
||||
// <h1> {"EA Funds Forecasting Model 0.1" |> ReasonReact.string} </h1>
|
||||
// <input
|
||||
// type_="number"
|
||||
// value={year |> Js.Float.toString}
|
||||
// onChange={handleChange(r =>
|
||||
// switch (Js.Float.fromString(r)) {
|
||||
// | r when r >= 2020.0 && r <= 2050.0 => setYear(_ => r)
|
||||
// | _ => ()
|
||||
// }
|
||||
// )}
|
||||
// />
|
||||
// <table className="table-auto">
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th className="px-4 py-2"> {"Fund Name" |> ReasonReact.string} </th>
|
||||
// <th className="px-4 py-2"> {"Donations" |> ReasonReact.string} </th>
|
||||
// <th className="px-4 py-2"> {"Payouts" |> ReasonReact.string} </th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// {funds
|
||||
// |> Belt.Array.map(_, r =>
|
||||
// <tr>
|
||||
// <th className="px-4 py-2 border ">
|
||||
// {r.name |> ReasonReact.string}
|
||||
// </th>
|
||||
// <th className="px-4 py-2 border font-normal">
|
||||
// {EAFunds_Model.go(r.group, year, DONATIONS)
|
||||
// |> Model.InputTypes.to_string
|
||||
// |> ReasonReact.string}
|
||||
// </th>
|
||||
// <th className="px-4 py-2 border font-normal">
|
||||
// {EAFunds_Model.go(r.group, year, PAYOUTS)
|
||||
// |> Model.InputTypes.to_string
|
||||
// |> ReasonReact.string}
|
||||
// </th>
|
||||
// </tr>
|
||||
// )
|
||||
// |> ReasonReact.array}
|
||||
// </tbody>
|
||||
// </table>
|
||||
// </>;
|
||||
/* }*/
|
|
@ -1,56 +0,0 @@
|
|||
type senate =
|
||||
| DEMOCRAT_VICTORY;
|
||||
|
||||
type house =
|
||||
| DEMOCRAT_VICTORY;
|
||||
|
||||
type statement =
|
||||
| Senate(senate)
|
||||
| House(house);
|
||||
|
||||
type outcome =
|
||||
| Bool(bool)
|
||||
| Option(string);
|
||||
|
||||
type fullStatement = {
|
||||
statement,
|
||||
outcome,
|
||||
};
|
||||
|
||||
type jointStatement = {
|
||||
statements: array(fullStatement),
|
||||
probability: option(float),
|
||||
};
|
||||
|
||||
let foo = (joint: jointStatement) => {
|
||||
[|
|
||||
{
|
||||
statements: [|
|
||||
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||
|],
|
||||
probability: Some(0.2),
|
||||
},
|
||||
{
|
||||
statements: [|
|
||||
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||
|],
|
||||
probability: Some(0.2),
|
||||
},
|
||||
{
|
||||
statements: [|
|
||||
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||
|],
|
||||
probability: Some(0.5),
|
||||
},
|
||||
{
|
||||
statements: [|
|
||||
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||
|],
|
||||
probability: Some(0.1),
|
||||
},
|
||||
|];
|
||||
};
|
|
@ -1,60 +0,0 @@
|
|||
module Math = {
|
||||
let normal = (mean: float, std: float) =>
|
||||
Js.Float.(
|
||||
{
|
||||
let nMean = toString(mean);
|
||||
let nStd = toString(std);
|
||||
{j|normal($(nMean), $(nStd))|j};
|
||||
}
|
||||
);
|
||||
|
||||
let divide = (str1: string, str2: string) => {j|$(str1)/$(str2)|j};
|
||||
};
|
||||
|
||||
type param =
|
||||
| SHARE_PRICE
|
||||
| SHARES_OUTSTANDING
|
||||
| MARKET_CAP;
|
||||
|
||||
type company = {
|
||||
name: string,
|
||||
currentPrice: option(float),
|
||||
marketCap: option(float),
|
||||
};
|
||||
|
||||
type otherSettings = {currentYear: int};
|
||||
|
||||
let sharesOutstanding = (price, marketCap) =>
|
||||
switch (price, marketCap) {
|
||||
| (Some(price), Some(marketCap)) =>
|
||||
Some(GuesstimatorDist.divide(marketCap, price))
|
||||
| _ => None
|
||||
};
|
||||
|
||||
let rec run =
|
||||
(
|
||||
company: company,
|
||||
year: int,
|
||||
param: param,
|
||||
otherSettings: otherSettings,
|
||||
) => {
|
||||
switch (param, year, company.currentPrice, company.marketCap) {
|
||||
| (SHARE_PRICE, year, Some(price), _) when year > 2019 && year < 2030 =>
|
||||
let diffYears = year - otherSettings.currentYear;
|
||||
let diffPerYear = 0.1;
|
||||
Some(
|
||||
GuesstimatorDist.normal(price, float_of_int(diffYears) *. diffPerYear),
|
||||
);
|
||||
| (MARKET_CAP, year, _, Some(price)) when year > 2019 && year < 2030 =>
|
||||
let diffYears = year - otherSettings.currentYear;
|
||||
let diffPerYear = 0.1;
|
||||
Some(
|
||||
GuesstimatorDist.normal(price, float_of_int(diffYears) *. diffPerYear),
|
||||
);
|
||||
| (SHARES_OUTSTANDING, year, _, _) when year > 2019 && year < 2030 =>
|
||||
let price = run(company, year, SHARE_PRICE, otherSettings);
|
||||
let marketCap = run(company, year, MARKET_CAP, otherSettings);
|
||||
sharesOutstanding(price, marketCap);
|
||||
| _ => None
|
||||
};
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
// open TimeTypes;
|
||||
// type t = {
|
||||
// timeVector,
|
||||
// limitedDomainCdf: LimitedDomainCdf.t,
|
||||
// };
|
||||
// let make =
|
||||
// (
|
||||
// ~timeVector: timeVector,
|
||||
// ~distribution: Types.ContinuousDistribution.t,
|
||||
// ~probabilityAtMaxX: float,
|
||||
// ~maxX: [ | `time(MomentRe.Moment.t) | `x(float)],
|
||||
// )
|
||||
// : t => {
|
||||
// let domainMaxX =
|
||||
// switch (maxX) {
|
||||
// | `time(m) => TimePoint.fromMoment(timeVector, m)
|
||||
// | `x(r) => r
|
||||
// };
|
||||
// let limitedDomainCdf =
|
||||
// LimitedDomainCdf.fromCdf(distribution, domainMaxX, probabilityAtMaxX);
|
||||
// {timeVector, limitedDomainCdf};
|
||||
// };
|
||||
// let probabilityBeforeDomainMax = (t: t) =>
|
||||
// LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf);
|
||||
// let domainMaxX = (t: t) =>
|
||||
// LimitedDomainCdf.probabilityBeforeDomainMax(t.limitedDomainCdf) /* |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r)))*/;
|
||||
// let probability = (t: t, m: MomentRe.Moment.t) => {
|
||||
// RelativeTimePoint.toXValue(t.timeVector, Time(m))
|
||||
// |> LimitedDomainCdf.probability(t.limitedDomainCdf);
|
||||
// };
|
||||
// let probabilityInverse = (t: t, y: float) =>
|
||||
// LimitedDomainCdf.probabilityInverse(t.limitedDomainCdf, y)
|
||||
// |> (r => RelativeTimePoint.toTime(t.timeVector, XValue(r)));
|
||||
// let cumulativeProbability = (t: t, m: MomentRe.Moment.t) =>
|
||||
// RelativeTimePoint.toXValue(t.timeVector, Time(m))
|
||||
// |> LimitedDomainCdf.cumulativeProbability(t.limitedDomainCdf);
|
||||
// let cumulativeProbabilityInverse = (t: t, y: float) =>
|
||||
/* LimitedDomainCdf.cumulativeProbabilityInverse(t.limitedDomainCdf, y*/
|
|
@ -11,15 +11,17 @@ type primaryDistribution = {
|
|||
let make =
|
||||
(
|
||||
~height=?,
|
||||
~verticalLine=?,
|
||||
~showVerticalLine=?,
|
||||
~marginBottom=?,
|
||||
~marginTop=?,
|
||||
~showDistributionLines=?,
|
||||
~maxX=?,
|
||||
~minX=?,
|
||||
~onHover=(f: float) => (),
|
||||
~primaryDistribution=?,
|
||||
~scale=?,
|
||||
~showDistributionLines=?,
|
||||
~showVerticalLine=?,
|
||||
~timeScale=?,
|
||||
~verticalLine=?,
|
||||
~children=[||],
|
||||
) =>
|
||||
ReasonReact.wrapJsForReason(
|
||||
|
@ -27,15 +29,17 @@ let make =
|
|||
~props=
|
||||
makeProps(
|
||||
~height?,
|
||||
~verticalLine?,
|
||||
~marginBottom?,
|
||||
~marginTop?,
|
||||
~onHover,
|
||||
~showVerticalLine?,
|
||||
~showDistributionLines?,
|
||||
~maxX?,
|
||||
~minX?,
|
||||
~onHover,
|
||||
~primaryDistribution?,
|
||||
~scale?,
|
||||
~showDistributionLines?,
|
||||
~showVerticalLine?,
|
||||
~timeScale?,
|
||||
~verticalLine?,
|
||||
(),
|
||||
),
|
||||
children,
|
||||
|
|
|
@ -18,24 +18,28 @@ module Styles = {
|
|||
[@react.component]
|
||||
let make =
|
||||
(
|
||||
~data,
|
||||
~minX=?,
|
||||
~maxX=?,
|
||||
~height=200,
|
||||
~color=`hex("111"),
|
||||
~data,
|
||||
~height=200,
|
||||
~maxX=?,
|
||||
~minX=?,
|
||||
~onHover: float => unit,
|
||||
~scale=?,
|
||||
~timeScale=?,
|
||||
) => {
|
||||
<div className={Styles.graph(color)}>
|
||||
<CdfChart__Base
|
||||
height
|
||||
?minX
|
||||
?maxX
|
||||
?minX
|
||||
?scale
|
||||
?timeScale
|
||||
height
|
||||
marginBottom=50
|
||||
marginTop=0
|
||||
onHover
|
||||
showVerticalLine=false
|
||||
showDistributionLines=false
|
||||
primaryDistribution={data |> Shape.XYShape.toJs}
|
||||
showDistributionLines=false
|
||||
showVerticalLine=false
|
||||
/>
|
||||
</div>;
|
||||
};
|
|
@ -1,17 +1,22 @@
|
|||
module Continuous = {
|
||||
[@react.component]
|
||||
let make = (~data) => {
|
||||
let make = (~data, ~
|
||||
|
||||
) => {
|
||||
let (x, setX) = React.useState(() => 0.);
|
||||
let timeScale = unit |> DistributionTypes.DistributionUnit.toJson;
|
||||
let chart =
|
||||
React.useMemo1(
|
||||
() =>
|
||||
<CdfChart__Plain
|
||||
data
|
||||
timeScale
|
||||
color={`hex("333")}
|
||||
onHover={r => setX(_ => r)}
|
||||
/>,
|
||||
[|data|],
|
||||
);
|
||||
|
||||
<div>
|
||||
chart
|
||||
<table className="table-auto">
|
||||
|
@ -100,6 +105,7 @@ module Mixed = {
|
|||
let make = (~dist) => {
|
||||
switch ((dist: option(DistributionTypes.genericDistribution))) {
|
||||
| Some({
|
||||
unit,
|
||||
generationSource:
|
||||
Shape(
|
||||
Mixed({
|
||||
|
@ -110,8 +116,9 @@ let make = (~dist) => {
|
|||
),
|
||||
}) =>
|
||||
<div>
|
||||
<Continuous data=n />
|
||||
<Continuous data={n |> Shape.Continuous.toPdf} unit />
|
||||
<Continuous
|
||||
unit
|
||||
data={
|
||||
n
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|
@ -133,6 +140,7 @@ let make = (~dist) => {
|
|||
}
|
||||
/>
|
||||
<Continuous
|
||||
unit
|
||||
data={
|
||||
n
|
||||
|> Shape.XYShape.Range.integrateWithTriangles
|
||||
|
|
407
src/components/charts/cdfChartD3.js
Normal file
407
src/components/charts/cdfChartD3.js
Normal file
|
@ -0,0 +1,407 @@
|
|||
const _ = require('lodash');
|
||||
const d3 = require('d3');
|
||||
const moment = require('moment');
|
||||
|
||||
export class CdfChartD3 {
|
||||
|
||||
constructor() {
|
||||
this.attrs = {
|
||||
svgWidth: 400,
|
||||
svgHeight: 400,
|
||||
|
||||
marginTop: 5,
|
||||
marginBottom: 5,
|
||||
marginRight: 50,
|
||||
marginLeft: 5,
|
||||
|
||||
container: null,
|
||||
minX: false,
|
||||
maxX: false,
|
||||
scale: 'linear',
|
||||
timeScale: null,
|
||||
showDistributionLines: true,
|
||||
areaColors: ['#E1E5EC', '#E1E5EC'],
|
||||
logBase: 10,
|
||||
verticalLine: 110,
|
||||
showVerticalLine: true,
|
||||
data: null,
|
||||
onHover: (e) => {
|
||||
},
|
||||
};
|
||||
this.hoverLine = null;
|
||||
this.xScale = null;
|
||||
this.dataPoints = null;
|
||||
this.mouseover = this.mouseover.bind(this);
|
||||
this.mouseout = this.mouseout.bind(this);
|
||||
this.formatDates = this.formatDates.bind(this);
|
||||
}
|
||||
|
||||
svgWidth(svgWidth) {
|
||||
this.attrs.svgWidth = svgWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
svgHeight(height) {
|
||||
this.attrs.svgHeight = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
maxX(maxX) {
|
||||
this.attrs.maxX = maxX;
|
||||
return this;
|
||||
}
|
||||
|
||||
minX(minX) {
|
||||
this.attrs.minX = minX;
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(scale) {
|
||||
this.attrs.scale = scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
timeScale(timeScale) {
|
||||
this.attrs.timeScale = timeScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
onHover(onHover) {
|
||||
this.attrs.onHover = onHover;
|
||||
return this;
|
||||
}
|
||||
|
||||
marginBottom(marginBottom) {
|
||||
this.attrs.marginBottom = marginBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
marginLeft(marginLeft) {
|
||||
this.attrs.marginLeft = marginLeft;
|
||||
return this;
|
||||
}
|
||||
|
||||
marginRight(marginRight) {
|
||||
this.attrs.marginRight = marginRight;
|
||||
return this;
|
||||
}
|
||||
|
||||
marginTop(marginTop) {
|
||||
this.attrs.marginTop = marginTop;
|
||||
return this;
|
||||
}
|
||||
|
||||
showDistributionLines(showDistributionLines) {
|
||||
this.attrs.showDistributionLines = showDistributionLines;
|
||||
return this;
|
||||
}
|
||||
|
||||
verticalLine(verticalLine) {
|
||||
this.attrs.verticalLine = verticalLine;
|
||||
return this;
|
||||
}
|
||||
|
||||
showVerticalLine(showVerticalLine) {
|
||||
this.attrs.showVerticalLine = showVerticalLine;
|
||||
return this;
|
||||
}
|
||||
|
||||
container(container) {
|
||||
this.attrs.container = container;
|
||||
return this;
|
||||
}
|
||||
|
||||
data(data) {
|
||||
this.attrs.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @returns {[]}
|
||||
*/
|
||||
getDataPoints(key) {
|
||||
const dt = [];
|
||||
const data = this.attrs.data[key];
|
||||
const len = data.xs.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
dt.push({ x: data.xs[i], y: data.ys[i] });
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
render() {
|
||||
const attrs = this.attrs;
|
||||
const container = d3.select(attrs.container);
|
||||
if (container.node() === null) {
|
||||
console.error('Container for D3 is not defined.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sets the width from the DOM element.
|
||||
const containerRect = container.node().getBoundingClientRect();
|
||||
if (containerRect.width > 0) {
|
||||
attrs.svgWidth = containerRect.width;
|
||||
}
|
||||
|
||||
// Calculated properties.
|
||||
const calc = {};
|
||||
calc.chartLeftMargin = attrs.marginLeft;
|
||||
calc.chartTopMargin = attrs.marginTop;
|
||||
calc.chartWidth = attrs.svgWidth - attrs.marginRight - attrs.marginLeft;
|
||||
calc.chartHeight = attrs.svgHeight - attrs.marginBottom - attrs.marginTop;
|
||||
|
||||
const areaColorRange = d3.scaleOrdinal().range(attrs.areaColors);
|
||||
this.dataPoints = [this.getDataPoints('primary')];
|
||||
|
||||
// Scales.
|
||||
const xMin = d3.min(attrs.data.primary.xs);
|
||||
const xMax = d3.max(attrs.data.primary.xs);
|
||||
|
||||
if (attrs.scale === 'linear') {
|
||||
this.xScale = d3.scaleLinear()
|
||||
.domain([attrs.minX || xMin, attrs.maxX || xMax])
|
||||
.range([0, calc.chartWidth]);
|
||||
} else {
|
||||
this.xScale = d3.scaleLog()
|
||||
.base(attrs.logBase)
|
||||
.domain([attrs.minX, attrs.maxX])
|
||||
.range([0, calc.chartWidth]);
|
||||
}
|
||||
|
||||
const yMin = d3.min(attrs.data.primary.ys);
|
||||
const yMax = d3.max(attrs.data.primary.ys);
|
||||
|
||||
this.yScale = d3.scaleLinear()
|
||||
.domain([yMin, yMax])
|
||||
.range([calc.chartHeight, 0]);
|
||||
|
||||
// Axis generator.
|
||||
if (!!this.attrs.timeScale) {
|
||||
const zero = _.get(this.attrs.timeScale, 'zero', moment());
|
||||
const unit = _.get(this.attrs.timeScale, 'unit', 'years');
|
||||
const diff = Math.abs(xMax - xMin);
|
||||
const left = zero.clone().add(xMin, unit);
|
||||
const right = left.clone().add(diff, unit);
|
||||
|
||||
const xScaleTime = d3.scaleTime()
|
||||
.domain([left.toDate(), right.toDate()])
|
||||
.nice()
|
||||
.range([0, calc.chartWidth]);
|
||||
|
||||
this.xAxis = d3.axisBottom()
|
||||
.scale(xScaleTime)
|
||||
.ticks(this.getTimeTicksByStr(unit))
|
||||
.tickFormat(this.formatDates);
|
||||
} else {
|
||||
this.xAxis = d3.axisBottom(this.xScale)
|
||||
.ticks(3)
|
||||
.tickFormat(d => {
|
||||
if (Math.abs(d) < 1) {
|
||||
return d3.format(".2")(d);
|
||||
} else if (xMin > 1000 && xMax < 3000) {
|
||||
// Condition which identifies years; 2019, 2020, 2021.
|
||||
return d3.format(".0")(d);
|
||||
} else {
|
||||
const prefix = d3.formatPrefix(".0", d);
|
||||
return prefix(d).replace("G", "B");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Objects.
|
||||
const line = d3.line()
|
||||
.x(d => this.xScale(d.x))
|
||||
.y(d => this.yScale(d.y));
|
||||
|
||||
const area = d3.area()
|
||||
.x(d => this.xScale(d.x))
|
||||
.y1(d => this.yScale(d.y))
|
||||
.y0(calc.chartHeight);
|
||||
|
||||
// Add svg.
|
||||
const svg = container
|
||||
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
||||
.attr('width', "100%")
|
||||
.attr('height', attrs.svgHeight)
|
||||
.attr('pointer-events', 'none');
|
||||
|
||||
// Add container g element.
|
||||
this.chart = svg
|
||||
.createObject({ tag: 'g', selector: 'chart' })
|
||||
.attr(
|
||||
'transform',
|
||||
'translate(' + calc.chartLeftMargin + ',' + calc.chartTopMargin + ')',
|
||||
);
|
||||
|
||||
// Add axis.
|
||||
this.chart.createObject({ tag: 'g', selector: 'axis' })
|
||||
.attr('transform', 'translate(' + 0 + ',' + calc.chartHeight + ')')
|
||||
.call(this.xAxis);
|
||||
|
||||
// Draw area.
|
||||
this.chart
|
||||
.createObjectsWithData({
|
||||
tag: 'path',
|
||||
selector: 'area-path',
|
||||
data: this.dataPoints,
|
||||
})
|
||||
.attr('d', area)
|
||||
.attr('fill', (d, i) => areaColorRange(i))
|
||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 0.5);
|
||||
|
||||
// Draw line.
|
||||
if (attrs.showDistributionLines) {
|
||||
this.chart
|
||||
.createObjectsWithData({
|
||||
tag: 'path',
|
||||
selector: 'line-path',
|
||||
data: this.dataPoints,
|
||||
})
|
||||
.attr('d', line)
|
||||
.attr('id', (d, i) => 'line-' + (i + 1))
|
||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 1)
|
||||
.attr('fill', 'none');
|
||||
}
|
||||
|
||||
if (attrs.showVerticalLine) {
|
||||
this.chart
|
||||
.createObject({ tag: 'line', selector: 'v-line' })
|
||||
.attr('x1', this.xScale(attrs.verticalLine))
|
||||
.attr('x2', this.xScale(attrs.verticalLine))
|
||||
.attr('y1', 0)
|
||||
.attr('y2', calc.chartHeight)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', 'steelblue');
|
||||
}
|
||||
|
||||
this.hoverLine = this.chart
|
||||
.createObject({ tag: 'line', selector: 'hover-line' })
|
||||
.attr('x1', 0)
|
||||
.attr('x2', 0)
|
||||
.attr('y1', 0)
|
||||
.attr('y2', calc.chartHeight)
|
||||
.attr('opacity', 0)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', '#22313F');
|
||||
|
||||
// Add drawing rectangle.
|
||||
const thi$ = this;
|
||||
this.chart
|
||||
.createObject({ tag: 'rect', selector: 'mouse-rect' })
|
||||
.attr('width', calc.chartWidth)
|
||||
.attr('height', calc.chartHeight)
|
||||
.attr('fill', 'transparent')
|
||||
.attr('pointer-events', 'all')
|
||||
.on('mouseover', function () {
|
||||
thi$.mouseover(this);
|
||||
})
|
||||
.on('mousemove', function () {
|
||||
thi$.mouseover(this);
|
||||
})
|
||||
.on('mouseout', this.mouseout);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
mouseover(constructor) {
|
||||
const mouse = d3.mouse(constructor);
|
||||
this.hoverLine.attr('opacity', 1)
|
||||
.attr('x1', mouse[0])
|
||||
.attr('x2', mouse[0]);
|
||||
|
||||
const xValue = this.xScale.invert(mouse[0]).toFixed(2);
|
||||
|
||||
const range = [
|
||||
this.xScale(this.dataPoints[this.dataPoints.length - 1][0].x),
|
||||
this.xScale(
|
||||
this.dataPoints
|
||||
[this.dataPoints.length - 1]
|
||||
[this.dataPoints[this.dataPoints.length - 1].length - 1].x,
|
||||
),
|
||||
];
|
||||
|
||||
if (mouse[0] > range[0] && mouse[0] < range[1]) {
|
||||
this.attrs.onHover(xValue);
|
||||
} else {
|
||||
this.attrs.onHover(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
mouseout() {
|
||||
this.hoverLine.attr('opacity', 0)
|
||||
}
|
||||
|
||||
formatDates(ts) {
|
||||
return moment(ts).format("MMMM Do YYYY");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} unit
|
||||
* @returns {*}
|
||||
*/
|
||||
getTimeTicksByStr(unit) {
|
||||
switch (unit) {
|
||||
case "months":
|
||||
return d3.timeMonth.every(1);
|
||||
case "quarters":
|
||||
return d3.timeMonth.every(3);
|
||||
case "hours":
|
||||
return d3.timeHour.every(1);
|
||||
case "days":
|
||||
return d3.timeDay.every(1);
|
||||
case "seconds":
|
||||
return d3.timeSecond.every(1);
|
||||
case "years":
|
||||
return d3.timeYear.every(1);
|
||||
case "minutes":
|
||||
return d3.timeMinute.every(1);
|
||||
case "weeks":
|
||||
return d3.timeWeek.every(1);
|
||||
case "milliseconds":
|
||||
return d3.timeMillisecond.every(1);
|
||||
default:
|
||||
return d3.timeYear.every(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @docs: https://github.com/d3/d3-selection
|
||||
* @param params
|
||||
* @returns {*}
|
||||
*/
|
||||
d3.selection.prototype.createObject = function createObject(params) {
|
||||
const selector = params.selector;
|
||||
const tag = params.tag;
|
||||
return this.insert(tag).attr('class', selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* @example:
|
||||
* This call example
|
||||
* createObjectsByData({
|
||||
* tag: 'path',
|
||||
* selector: 'line-path',
|
||||
* data: this.dataPoints,
|
||||
* })
|
||||
* will create a new tag "<path class="line-path">1,2,3</path>".
|
||||
* @docs: https://github.com/d3/d3-selection
|
||||
* @param params
|
||||
* @returns {*}
|
||||
*/
|
||||
d3.selection.prototype.createObjectsWithData = function createObjectsWithData(params) {
|
||||
const selector = params.selector;
|
||||
const tag = params.tag;
|
||||
const data = params.data;
|
||||
|
||||
return this.selectAll('.' + selector)
|
||||
.data(data)
|
||||
.enter()
|
||||
.insert(tag)
|
||||
.attr('class', selector);
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useSize } from 'react-use';
|
||||
|
||||
import chart from './cdfChartd3';
|
||||
import { CdfChartD3 } from './cdfChartD3';
|
||||
|
||||
/**
|
||||
* @param min
|
||||
|
@ -15,14 +15,16 @@ function getRandomInt(min, max) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Example input:
|
||||
* {
|
||||
* xs: [50,100,300,400,500,600],
|
||||
* ys: [0.1, 0.4, 0.6, 0.7,0.8, 0.9]}
|
||||
* }
|
||||
* @param props
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
function CdfChart(props) {
|
||||
const id = "chart-" + getRandomInt(0, 100000);
|
||||
function CdfChartReact(props) {
|
||||
const containerRef = React.createRef();
|
||||
const key = "cdf-chart-react-" + getRandomInt(0, 1000);
|
||||
const scale = props.scale || 'linear';
|
||||
const style = !!props.width ? { width: props.width + "px" } : {};
|
||||
|
||||
const [sized, { width }] = useSize(() => {
|
||||
return React.createElement("div", {
|
||||
key: "resizable-div",
|
||||
|
@ -32,7 +34,7 @@ function CdfChart(props) {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
chart()
|
||||
new CdfChartD3()
|
||||
.svgWidth(width)
|
||||
.svgHeight(props.height)
|
||||
.maxX(props.maxX)
|
||||
|
@ -45,13 +47,13 @@ function CdfChart(props) {
|
|||
.showDistributionLines(props.showDistributionLines)
|
||||
.verticalLine(props.verticalLine)
|
||||
.showVerticalLine(props.showVerticalLine)
|
||||
.container("#" + id)
|
||||
.data({ primary: props.primaryDistribution }).render();
|
||||
.container(containerRef.current)
|
||||
.data({ primary: props.primaryDistribution })
|
||||
.scale(scale)
|
||||
.timeScale(props.timeScale)
|
||||
.render();
|
||||
});
|
||||
|
||||
const style = !!props.width ? { width: props.width + "px" } : {};
|
||||
const key = id;
|
||||
|
||||
return React.createElement("div", {
|
||||
style: {
|
||||
paddingLeft: "10px",
|
||||
|
@ -59,8 +61,12 @@ function CdfChart(props) {
|
|||
},
|
||||
}, [
|
||||
sized,
|
||||
React.createElement("div", { id, style, key }),
|
||||
React.createElement("div", {
|
||||
key,
|
||||
style,
|
||||
ref: containerRef,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export default CdfChart;
|
||||
export default CdfChartReact;
|
||||
|
|
|
@ -1,302 +0,0 @@
|
|||
import * as d3 from 'd3';
|
||||
|
||||
function chart() {
|
||||
// Id for event handlings.
|
||||
var attrs = {
|
||||
id: 'ID' + Math.floor(Math.random() * 1000000),
|
||||
svgWidth: 400,
|
||||
svgHeight: 400,
|
||||
|
||||
marginTop: 5,
|
||||
marginBottom: 5,
|
||||
marginRight: 50,
|
||||
marginLeft: 5,
|
||||
|
||||
container: 'body',
|
||||
minX: false,
|
||||
maxX: false,
|
||||
scale: 'linear',
|
||||
showDistributionLines: true,
|
||||
areaColors: ['#E1E5EC', '#E1E5EC'],
|
||||
logBase: 10,
|
||||
verticalLine: 110,
|
||||
showVerticalLine: true,
|
||||
data: null,
|
||||
onHover: (e) => {},
|
||||
};
|
||||
|
||||
var main = function main() {
|
||||
// Drawing containers.
|
||||
var container = d3.select(attrs.container);
|
||||
|
||||
if (container.node() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var containerRect = container.node().getBoundingClientRect();
|
||||
if (containerRect.width > 0) {
|
||||
attrs.svgWidth = containerRect.width;
|
||||
}
|
||||
|
||||
// Calculated properties.
|
||||
// id for event handlings.
|
||||
var calc = {};
|
||||
calc.id = 'ID' + Math.floor(Math.random() * 1000000);
|
||||
calc.chartLeftMargin = attrs.marginLeft;
|
||||
calc.chartTopMargin = attrs.marginTop;
|
||||
calc.chartWidth = attrs.svgWidth - attrs.marginRight - attrs.marginLeft;
|
||||
calc.chartHeight = attrs.svgHeight - attrs.marginBottom - attrs.marginTop;
|
||||
|
||||
var areaColor = d3.scaleOrdinal().range(attrs.areaColors);
|
||||
|
||||
var dataPoints = [getDatapoints('primary')];
|
||||
|
||||
// Scales.
|
||||
var xScale;
|
||||
|
||||
var xMin = d3.min(attrs.data.primary.xs);
|
||||
var xMax = d3.max(attrs.data.primary.xs);
|
||||
|
||||
if (attrs.scale === 'linear') {
|
||||
xScale = d3.scaleLinear()
|
||||
.domain([
|
||||
attrs.minX || xMin,
|
||||
attrs.maxX || xMax
|
||||
])
|
||||
.range([0, calc.chartWidth]);
|
||||
} else {
|
||||
xScale = d3.scaleLog()
|
||||
.base(attrs.logBase)
|
||||
.domain([
|
||||
attrs.minX,
|
||||
attrs.maxX,
|
||||
])
|
||||
.range([0, calc.chartWidth]);
|
||||
}
|
||||
|
||||
var yMin = d3.min(attrs.data.primary.ys);
|
||||
var yMax = d3.max(attrs.data.primary.ys);
|
||||
|
||||
var yScale = d3.scaleLinear()
|
||||
.domain([
|
||||
yMin,
|
||||
yMax,
|
||||
])
|
||||
.range([calc.chartHeight, 0]);
|
||||
|
||||
// Axis generator.
|
||||
var xAxis = d3.axisBottom(xScale)
|
||||
.ticks(3)
|
||||
.tickFormat(d => {
|
||||
if (Math.abs(d) < 1) {
|
||||
return d3.format(".2")(d);
|
||||
} else if (xMin > 1000 && xMax < 3000) {
|
||||
// Condition which identifies years; 2019, 2020, 2021.
|
||||
return d3.format(".0")(d);
|
||||
} else {
|
||||
var prefix = d3.formatPrefix(".0", d);
|
||||
var output = prefix(d);
|
||||
return output.replace("G", "B");
|
||||
}
|
||||
});
|
||||
|
||||
// Line generator.
|
||||
var line = d3.line()
|
||||
.x(function (d, i) {
|
||||
return xScale(d.x);
|
||||
})
|
||||
.y(function (d, i) {
|
||||
return yScale(d.y);
|
||||
});
|
||||
|
||||
var area = d3.area()
|
||||
.x(function (d, i) {
|
||||
return xScale(d.x);
|
||||
})
|
||||
.y1(function (d, i) {
|
||||
return yScale(d.y);
|
||||
})
|
||||
.y0(calc.chartHeight);
|
||||
|
||||
// Add svg.
|
||||
var svg = container
|
||||
.patternify({ tag: 'svg', selector: 'svg-chart-container' })
|
||||
.attr('width', "100%")
|
||||
.attr('height', attrs.svgHeight)
|
||||
.attr('pointer-events', 'none');
|
||||
|
||||
// Add container g element.
|
||||
var chart = svg
|
||||
.patternify({ tag: 'g', selector: 'chart' })
|
||||
.attr(
|
||||
'transform',
|
||||
'translate(' + calc.chartLeftMargin + ',' + calc.chartTopMargin + ')',
|
||||
);
|
||||
|
||||
// Add axis.
|
||||
chart.patternify({ tag: 'g', selector: 'axis' })
|
||||
.attr('transform', 'translate(' + 0 + ',' + calc.chartHeight + ')')
|
||||
.call(xAxis);
|
||||
|
||||
// Draw area.
|
||||
chart
|
||||
.patternify({
|
||||
tag: 'path',
|
||||
selector: 'area-path',
|
||||
data: dataPoints
|
||||
})
|
||||
.attr('d', area)
|
||||
.attr('fill', (d, i) => areaColor(i))
|
||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 1);
|
||||
|
||||
// Draw line.
|
||||
if (attrs.showDistributionLines) {
|
||||
chart
|
||||
.patternify({
|
||||
tag: 'path',
|
||||
selector: 'line-path',
|
||||
data: dataPoints
|
||||
})
|
||||
.attr('d', line)
|
||||
.attr('id', (d, i) => 'line-' + (i + 1))
|
||||
.attr('opacity', (d, i) => {
|
||||
return i === 0 ? 0.7 : 1
|
||||
})
|
||||
.attr('fill', 'none');
|
||||
}
|
||||
|
||||
if (attrs.showVerticalLine) {
|
||||
chart.patternify({ tag: 'line', selector: 'v-line' })
|
||||
.attr('x1', xScale(attrs.verticalLine))
|
||||
.attr('x2', xScale(attrs.verticalLine))
|
||||
.attr('y1', 0)
|
||||
.attr('y2', calc.chartHeight)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', 'steelblue');
|
||||
}
|
||||
|
||||
var hoverLine = chart.patternify({ tag: 'line', selector: 'hover-line' })
|
||||
.attr('x1', 0)
|
||||
.attr('x2', 0)
|
||||
.attr('y1', 0)
|
||||
.attr('y2', calc.chartHeight)
|
||||
.attr('opacity', 0)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', '#22313F');
|
||||
|
||||
// Add drawing rectangle.
|
||||
chart.patternify({ tag: 'rect', selector: 'mouse-rect' })
|
||||
.attr('width', calc.chartWidth)
|
||||
.attr('height', calc.chartHeight)
|
||||
.attr('fill', 'transparent')
|
||||
.attr('pointer-events', 'all')
|
||||
.on('mouseover', mouseover)
|
||||
.on('mousemove', mouseover)
|
||||
.on('mouseout', mouseout);
|
||||
|
||||
function mouseover() {
|
||||
var mouse = d3.mouse(this);
|
||||
|
||||
hoverLine.attr('opacity', 1)
|
||||
.attr('x1', mouse[0])
|
||||
.attr('x2', mouse[0]);
|
||||
|
||||
var range = [
|
||||
xScale(dataPoints[dataPoints.length - 1][0].x),
|
||||
xScale(
|
||||
dataPoints
|
||||
[dataPoints.length - 1]
|
||||
[dataPoints[dataPoints.length - 1].length - 1].x,
|
||||
),
|
||||
];
|
||||
|
||||
var xValue = xScale.invert(mouse[0]).toFixed(2);
|
||||
|
||||
if (mouse[0] > range[0] && mouse[0] < range[1]) {
|
||||
attrs.onHover(xValue);
|
||||
} else {
|
||||
attrs.onHover(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseout() {
|
||||
hoverLine.attr('opacity', 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @returns {[]}
|
||||
*/
|
||||
function getDatapoints(key) {
|
||||
var dt = [];
|
||||
var data = attrs.data[key];
|
||||
var len = data.xs.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
dt.push({
|
||||
x: data.xs[i],
|
||||
y: data.ys[i]
|
||||
})
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
};
|
||||
|
||||
d3.selection.prototype.patternify = function patternify(params) {
|
||||
var container = this;
|
||||
var selector = params.selector;
|
||||
var elementTag = params.tag;
|
||||
var data = params.data || [selector];
|
||||
|
||||
// Pattern in action.
|
||||
var selection = container.selectAll('.' + selector).data(data, (d, i) => {
|
||||
if (typeof d === 'object') {
|
||||
if (d.id) {
|
||||
return d.id;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
});
|
||||
|
||||
selection.exit().remove();
|
||||
selection = selection.enter().append(elementTag).merge(selection);
|
||||
selection.attr('class', selector);
|
||||
return selection;
|
||||
};
|
||||
|
||||
// @todo: Do not do like that.
|
||||
// Dynamic keys functions.
|
||||
// Attach variables to main function.
|
||||
Object.keys(attrs).forEach((key) => {
|
||||
main[key] = function (_) {
|
||||
if (!arguments.length) {
|
||||
return attrs[key];
|
||||
}
|
||||
attrs[key] = _;
|
||||
return main;
|
||||
};
|
||||
});
|
||||
|
||||
//Set attrs as property.
|
||||
main.attrs = attrs;
|
||||
|
||||
//Exposed update functions.
|
||||
main.data = function data(value) {
|
||||
if (!arguments.length) return attrs.data;
|
||||
attrs.data = value;
|
||||
return main;
|
||||
};
|
||||
|
||||
// Run visual.
|
||||
main.render = function render() {
|
||||
main();
|
||||
return main;
|
||||
};
|
||||
|
||||
return main;
|
||||
}
|
||||
|
||||
export default chart;
|
|
@ -33,8 +33,8 @@ type generationSource =
|
|||
| Shape(pointsType);
|
||||
|
||||
type distributionUnit =
|
||||
| Unspecified
|
||||
| Time(TimeTypes.timeVector);
|
||||
| UnspecifiedDistribution
|
||||
| TimeDistribution(TimeTypes.timeVector);
|
||||
|
||||
type probabilityType =
|
||||
| Cdf
|
||||
|
@ -46,4 +46,15 @@ type genericDistribution = {
|
|||
probabilityType,
|
||||
domain,
|
||||
unit: distributionUnit,
|
||||
};
|
||||
|
||||
module DistributionUnit = {
|
||||
let toJson = (distributionUnit: distributionUnit) =>
|
||||
switch (distributionUnit) {
|
||||
| TimeDistribution({zero, unit}) =>
|
||||
Js.Null.fromOption(
|
||||
Some({"zero": zero, "unit": unit |> TimeTypes.TimeUnit.toString}),
|
||||
)
|
||||
| _ => Js.Null.fromOption(None)
|
||||
};
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
open DistributionTypes;
|
||||
|
||||
let make =
|
||||
(
|
||||
~generationSource,
|
||||
~probabilityType=Pdf,
|
||||
~domain=Complete,
|
||||
~unit=Unspecified,
|
||||
~unit=UnspecifiedDistribution,
|
||||
(),
|
||||
)
|
||||
: genericDistribution => {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
type assumption =
|
||||
| ADDS_TO_1
|
||||
| ADDS_TO_CORRECT_PROBABILITY;
|
||||
|
||||
type assumptions = {
|
||||
continuous: assumption,
|
||||
discrete: assumption,
|
||||
discreteProbabilityMass: option(float),
|
||||
};
|
||||
|
||||
let build = (~continuous, ~discrete, ~assumptions) =>
|
||||
switch (assumptions) {
|
||||
| {
|
||||
|
@ -21,6 +23,7 @@ let build = (~continuous, ~discrete, ~assumptions) =>
|
|||
~discreteProbabilityMassFraction=r,
|
||||
),
|
||||
)
|
||||
|
||||
| {
|
||||
continuous: ADDS_TO_1,
|
||||
discrete: ADDS_TO_1,
|
||||
|
@ -33,18 +36,21 @@ let build = (~continuous, ~discrete, ~assumptions) =>
|
|||
~discreteProbabilityMassFraction=r,
|
||||
),
|
||||
)
|
||||
|
||||
| {
|
||||
continuous: ADDS_TO_1,
|
||||
discrete: ADDS_TO_1,
|
||||
discreteProbabilityMass: None,
|
||||
} =>
|
||||
None
|
||||
|
||||
| {
|
||||
continuous: ADDS_TO_CORRECT_PROBABILITY,
|
||||
discrete: ADDS_TO_1,
|
||||
discreteProbabilityMass: None,
|
||||
} =>
|
||||
None
|
||||
|
||||
| {
|
||||
continuous: ADDS_TO_1,
|
||||
discrete: ADDS_TO_CORRECT_PROBABILITY,
|
||||
|
|
|
@ -20,6 +20,21 @@ type timePoint = {
|
|||
value: float,
|
||||
};
|
||||
|
||||
module TimeUnit = {
|
||||
let toString = (timeUnit: timeUnit) =>
|
||||
switch (timeUnit) {
|
||||
| `days => "days"
|
||||
| `hours => "hours"
|
||||
| `milliseconds => "milliseconds"
|
||||
| `minutes => "minutes"
|
||||
| `months => "months"
|
||||
| `quarters => "quarters"
|
||||
| `seconds => "seconds"
|
||||
| `weeks => "weeks"
|
||||
| `years => "years"
|
||||
};
|
||||
};
|
||||
|
||||
module TimePoint = {
|
||||
let fromTimeVector = (timeVector, value): timePoint => {timeVector, value};
|
||||
|
||||
|
|
|
@ -139,10 +139,11 @@ module Model = {
|
|||
~generationSource=GuesstimatorString(str),
|
||||
~probabilityType=Cdf,
|
||||
~domain=Complete,
|
||||
~unit=Unspecified,
|
||||
~unit=UnspecifiedDistribution,
|
||||
(),
|
||||
);
|
||||
Prop.Value.GenericDistribution(genericDistribution);
|
||||
|
||||
| CHANCE_OF_EXISTENCE =>
|
||||
Prop.Value.GenericDistribution(
|
||||
GenericDistribution.make(
|
||||
|
@ -155,7 +156,7 @@ module Model = {
|
|||
),
|
||||
~probabilityType=Cdf,
|
||||
~domain=RightLimited({xPoint: 100., excludingProbabilityMass: 0.3}),
|
||||
~unit=Time({zero: currentDateTime, unit: `years}),
|
||||
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
|
||||
(),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ module Model = {
|
|||
~generationSource=GuesstimatorString(guesstimatorString),
|
||||
~probabilityType=Cdf,
|
||||
~domain=RightLimited({xPoint: 200., excludingProbabilityMass: 0.3}),
|
||||
~unit=Time({zero: currentDateTime, unit: `years}),
|
||||
~unit=TimeDistribution({zero: currentDateTime, unit: `years}),
|
||||
(),
|
||||
);
|
||||
Prop.Value.GenericDistribution(genericDistribution);
|
||||
|
|
Loading…
Reference in New Issue
Block a user