Compare commits
6 Commits
develop
...
plot-funct
Author | SHA1 | Date | |
---|---|---|---|
|
76fe461363 | ||
|
8d612f75f0 | ||
|
c060304161 | ||
|
56913bc95e | ||
|
2dfb57240e | ||
|
62f735efcb |
|
@ -3,8 +3,9 @@ import {
|
|||
SqDistribution,
|
||||
result,
|
||||
SqDistributionError,
|
||||
LabeledDistribution,
|
||||
resultMap,
|
||||
SqRecord,
|
||||
SqPlot,
|
||||
environment,
|
||||
SqDistributionTag,
|
||||
} from "@quri/squiggle-lang";
|
||||
|
@ -17,7 +18,6 @@ import {
|
|||
DistributionChartSpecOptions,
|
||||
} from "../lib/distributionSpecBuilder";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { Plot, parsePlot } from "../lib/plotParser";
|
||||
import { flattenResult } from "../lib/utility";
|
||||
import { hasMassBelowZero } from "../lib/distributionUtils";
|
||||
|
||||
|
@ -28,27 +28,15 @@ export type DistributionPlottingSettings = {
|
|||
} & DistributionChartSpecOptions;
|
||||
|
||||
export type DistributionChartProps = {
|
||||
plot: Plot;
|
||||
environment: environment;
|
||||
width?: number;
|
||||
height: number;
|
||||
xAxisType?: "number" | "dateTime";
|
||||
} & DistributionPlottingSettings;
|
||||
|
||||
export function defaultPlot(distribution: SqDistribution): Plot {
|
||||
return { distributions: [{ name: "default", distribution }] };
|
||||
}
|
||||
|
||||
export function makePlot(record: SqRecord): Plot | void {
|
||||
const plotResult = parsePlot(record);
|
||||
if (plotResult.tag === "Ok") {
|
||||
return plotResult.value;
|
||||
}
|
||||
}
|
||||
} & DistributionPlottingSettings &
|
||||
({ plot: SqPlot } | { distribution: SqDistribution });
|
||||
|
||||
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
||||
const {
|
||||
plot,
|
||||
environment,
|
||||
height,
|
||||
showSummary,
|
||||
|
@ -57,8 +45,14 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
|||
actions = false,
|
||||
} = props;
|
||||
const [sized] = useSize((size) => {
|
||||
const shapes = flattenResult(
|
||||
plot.distributions.map((x) =>
|
||||
let distributions: LabeledDistribution[];
|
||||
if ("plot" in props) {
|
||||
distributions = props.plot.getDistributions();
|
||||
} else {
|
||||
distributions = [{ name: "default", distribution: props.distribution }];
|
||||
}
|
||||
let shapes = flattenResult(
|
||||
distributions.map((x) =>
|
||||
resultMap(x.distribution.pointSet(environment), (pointSet) => ({
|
||||
name: x.name,
|
||||
// color: x.color, // not supported yet
|
||||
|
@ -77,7 +71,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
|||
|
||||
// if this is a sample set, include the samples
|
||||
const samples: number[] = [];
|
||||
for (const { distribution } of plot?.distributions) {
|
||||
for (const { distribution } of distributions) {
|
||||
if (distribution.tag === SqDistributionTag.SampleSet) {
|
||||
samples.push(...distribution.value());
|
||||
}
|
||||
|
@ -126,9 +120,9 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
|||
/>
|
||||
)}
|
||||
<div className="flex justify-center">
|
||||
{showSummary && plot.distributions.length === 1 && (
|
||||
{showSummary && distributions.length === 1 && (
|
||||
<SummaryTable
|
||||
distribution={plot.distributions[0].distribution}
|
||||
distribution={distributions[0].distribution}
|
||||
environment={environment}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -15,7 +15,6 @@ import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
|
|||
import {
|
||||
DistributionChart,
|
||||
DistributionPlottingSettings,
|
||||
defaultPlot,
|
||||
} from "./DistributionChart";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
@ -184,7 +183,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
|||
mouseItem.tag === "Ok" &&
|
||||
mouseItem.value.tag === SqValueTag.Distribution ? (
|
||||
<DistributionChart
|
||||
plot={defaultPlot(mouseItem.value.value)}
|
||||
distribution={mouseItem.value.value}
|
||||
environment={environment}
|
||||
width={400}
|
||||
height={50}
|
||||
|
@ -194,7 +193,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
|||
|
||||
let getPercentilesMemoized = React.useMemo(
|
||||
() => getPercentiles({ chartSettings, fn, environment }),
|
||||
[environment, fn]
|
||||
[chartSettings, environment, fn]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useContext } from "react";
|
||||
import { SqDistributionTag, SqValue, SqValueTag } from "@quri/squiggle-lang";
|
||||
import { NumberShower } from "../NumberShower";
|
||||
import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
|
||||
import { DistributionChart } from "../DistributionChart";
|
||||
import { FunctionChart } from "../FunctionChart";
|
||||
import clsx from "clsx";
|
||||
import { VariableBox } from "./VariableBox";
|
||||
|
@ -104,7 +104,7 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
|||
{(settings) => {
|
||||
return (
|
||||
<DistributionChart
|
||||
plot={defaultPlot(value.value)}
|
||||
distribution={value.value}
|
||||
environment={settings.environment}
|
||||
{...settings.distributionPlotSettings}
|
||||
height={settings.height}
|
||||
|
@ -219,63 +219,61 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
|||
</VariableBox>
|
||||
);
|
||||
}
|
||||
case SqValueTag.Plot:
|
||||
const plot = value.value;
|
||||
return (
|
||||
<VariableBox
|
||||
value={value}
|
||||
heading="Plot"
|
||||
renderSettingsMenu={({ onChange }) => {
|
||||
let disableLogX = plot.getDistributions().some((x) => {
|
||||
let pointSet = x.distribution.pointSet(
|
||||
getMergedSettings(value.location).environment
|
||||
);
|
||||
return (
|
||||
pointSet.tag === "Ok" &&
|
||||
hasMassBelowZero(pointSet.value.asShape())
|
||||
);
|
||||
});
|
||||
return (
|
||||
<ItemSettingsMenu
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disableLogX={disableLogX}
|
||||
withFunctionSettings={false}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(settings) => {
|
||||
return (
|
||||
<DistributionChart
|
||||
plot={plot}
|
||||
environment={settings.environment}
|
||||
{...settings.distributionPlotSettings}
|
||||
height={settings.height}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</VariableBox>
|
||||
);
|
||||
case SqValueTag.Record:
|
||||
const plot = makePlot(value.value);
|
||||
if (plot) {
|
||||
return (
|
||||
<VariableBox
|
||||
value={value}
|
||||
heading="Plot"
|
||||
renderSettingsMenu={({ onChange }) => {
|
||||
let disableLogX = plot.distributions.some((x) => {
|
||||
let pointSet = x.distribution.pointSet(
|
||||
getMergedSettings(value.location).environment
|
||||
);
|
||||
return (
|
||||
pointSet.tag === "Ok" &&
|
||||
hasMassBelowZero(pointSet.value.asShape())
|
||||
);
|
||||
});
|
||||
return (
|
||||
<ItemSettingsMenu
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disableLogX={disableLogX}
|
||||
withFunctionSettings={false}
|
||||
return (
|
||||
<VariableList value={value} heading="Record">
|
||||
{(_) =>
|
||||
value.value
|
||||
.entries()
|
||||
.map(([key, r]) => (
|
||||
<ExpressionViewer
|
||||
key={key}
|
||||
value={r}
|
||||
width={width !== undefined ? width - 20 : width}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(settings) => {
|
||||
return (
|
||||
<DistributionChart
|
||||
plot={plot}
|
||||
environment={settings.environment}
|
||||
{...settings.distributionPlotSettings}
|
||||
height={settings.height}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</VariableBox>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariableList value={value} heading="Record">
|
||||
{(_) =>
|
||||
value.value
|
||||
.entries()
|
||||
.map(([key, r]) => (
|
||||
<ExpressionViewer
|
||||
key={key}
|
||||
value={r}
|
||||
width={width !== undefined ? width - 20 : width}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</VariableList>
|
||||
);
|
||||
}
|
||||
))
|
||||
}
|
||||
</VariableList>
|
||||
);
|
||||
case SqValueTag.Array:
|
||||
return (
|
||||
<VariableList value={value} heading="Array">
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
import * as yup from "yup";
|
||||
import {
|
||||
SqValue,
|
||||
SqValueTag,
|
||||
SqDistribution,
|
||||
result,
|
||||
SqRecord,
|
||||
} from "@quri/squiggle-lang";
|
||||
|
||||
export type LabeledDistribution = {
|
||||
name: string;
|
||||
distribution: SqDistribution;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type Plot = {
|
||||
distributions: LabeledDistribution[];
|
||||
};
|
||||
|
||||
function error<a, b>(err: b): result<a, b> {
|
||||
return { tag: "Error", value: err };
|
||||
}
|
||||
|
||||
function ok<a, b>(x: a): result<a, b> {
|
||||
return { tag: "Ok", value: x };
|
||||
}
|
||||
|
||||
const schema = yup
|
||||
.object()
|
||||
.noUnknown()
|
||||
.strict()
|
||||
.shape({
|
||||
distributions: yup
|
||||
.array()
|
||||
.required()
|
||||
.of(
|
||||
yup.object().required().shape({
|
||||
name: yup.string().required(),
|
||||
distribution: yup.mixed().required(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
type JsonObject =
|
||||
| string
|
||||
| { [key: string]: JsonObject }
|
||||
| JsonObject[]
|
||||
| SqDistribution;
|
||||
|
||||
function toJson(val: SqValue): JsonObject {
|
||||
if (val.tag === SqValueTag.String) {
|
||||
return val.value;
|
||||
} else if (val.tag === SqValueTag.Record) {
|
||||
return toJsonRecord(val.value);
|
||||
} else if (val.tag === SqValueTag.Array) {
|
||||
return val.value.getValues().map(toJson);
|
||||
} else if (val.tag === SqValueTag.Distribution) {
|
||||
return val.value;
|
||||
} else {
|
||||
throw new Error("Could not parse object of type " + val.tag);
|
||||
}
|
||||
}
|
||||
|
||||
function toJsonRecord(val: SqRecord): JsonObject {
|
||||
let recordObject: JsonObject = {};
|
||||
val.entries().forEach(([key, value]) => (recordObject[key] = toJson(value)));
|
||||
return recordObject;
|
||||
}
|
||||
|
||||
export function parsePlot(record: SqRecord): result<Plot, string> {
|
||||
try {
|
||||
const plotRecord = schema.validateSync(toJsonRecord(record));
|
||||
if (plotRecord.distributions) {
|
||||
return ok({ distributions: plotRecord.distributions.map((x) => x) });
|
||||
} else {
|
||||
// I have no idea why yup's typings thinks this is possible
|
||||
return error("no distributions field. Should never get here");
|
||||
}
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : "Unknown error";
|
||||
return error(message);
|
||||
}
|
||||
}
|
|
@ -99,7 +99,7 @@ describe("stacktraces", () => {
|
|||
|
||||
expect(
|
||||
error,
|
||||
)->toBe(`Error: There are function matches for add(), but with different arguments: [add(number, number)]; [add(distribution, number)]; [add(number, distribution)]; [add(distribution, distribution)]; [add(date, duration)]; [add(duration, duration)]
|
||||
)->toBe(`Error: There are function matches for add(), but with different arguments: [add(number, number)]; [add(date, duration)]; [add(duration, duration)]; [add(distribution, number)]; [add(number, distribution)]; [add(distribution, distribution)]
|
||||
Stack trace:
|
||||
f at line 4, column 5
|
||||
g at line 6, column 12
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
open Jest
|
||||
open Reducer_TestHelpers
|
||||
|
||||
describe("Plot Library", () => {
|
||||
testEvalToBe(
|
||||
`Plot.dist({
|
||||
show: [{
|
||||
name: "normal",
|
||||
value: normal(0, 1)
|
||||
}, {
|
||||
name: "lognormal",
|
||||
value: 1 to 2
|
||||
}, {
|
||||
name: "constant",
|
||||
value: 3
|
||||
}]
|
||||
})`,
|
||||
"Ok(Plot showing normal,lognormal,constant)",
|
||||
)
|
||||
})
|
25
packages/squiggle-lang/src/js/SqPlot.ts
Normal file
25
packages/squiggle-lang/src/js/SqPlot.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import * as RSPlot from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Plot.gen";
|
||||
import { SqDistribution, wrapDistribution } from "./SqDistribution";
|
||||
import { SqValueLocation } from "./SqValueLocation";
|
||||
|
||||
type T = RSPlot.squiggleValue_Plot;
|
||||
|
||||
export type LabeledDistribution = {
|
||||
name: string;
|
||||
distribution: SqDistribution;
|
||||
};
|
||||
|
||||
export class SqPlot {
|
||||
constructor(private _value: T, public location: SqValueLocation) {}
|
||||
|
||||
getDistributions(): LabeledDistribution[] {
|
||||
return this._value.distributions.map((v: RSPlot.labeledDistribution) => ({
|
||||
...v,
|
||||
distribution: wrapDistribution(v.distribution),
|
||||
}));
|
||||
}
|
||||
|
||||
toString() {
|
||||
return RSPlot.toString(this._value);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import { wrapDistribution } from "./SqDistribution";
|
|||
import { SqLambda } from "./SqLambda";
|
||||
import { SqLambdaDeclaration } from "./SqLambdaDeclaration";
|
||||
import { SqRecord } from "./SqRecord";
|
||||
import { SqPlot } from "./SqPlot";
|
||||
import { SqArray } from "./SqArray";
|
||||
import { SqValueLocation } from "./SqValueLocation";
|
||||
|
||||
|
@ -91,6 +92,14 @@ export class SqNumberValue extends SqAbstractValue {
|
|||
}
|
||||
}
|
||||
|
||||
export class SqPlotValue extends SqAbstractValue {
|
||||
tag = Tag.Plot as const;
|
||||
|
||||
get value() {
|
||||
return new SqPlot(this.valueMethod(RSValue.getPlot), this.location);
|
||||
}
|
||||
}
|
||||
|
||||
export class SqRecordValue extends SqAbstractValue {
|
||||
tag = Tag.Record as const;
|
||||
|
||||
|
@ -131,6 +140,7 @@ const tagToClass = {
|
|||
[Tag.Distribution]: SqDistributionValue,
|
||||
[Tag.Lambda]: SqLambdaValue,
|
||||
[Tag.Number]: SqNumberValue,
|
||||
[Tag.Plot]: SqPlotValue,
|
||||
[Tag.Record]: SqRecordValue,
|
||||
[Tag.String]: SqStringValue,
|
||||
[Tag.TimeDuration]: SqTimeDurationValue,
|
||||
|
@ -148,6 +158,7 @@ export type SqValue =
|
|||
| SqLambdaValue
|
||||
| SqNumberValue
|
||||
| SqRecordValue
|
||||
| SqPlotValue
|
||||
| SqStringValue
|
||||
| SqTimeDurationValue
|
||||
| SqVoidValue;
|
||||
|
|
|
@ -6,6 +6,7 @@ export { result } from "../rescript/ForTS/ForTS_Result_tag";
|
|||
export { SqDistribution, SqDistributionTag } from "./SqDistribution";
|
||||
export { SqDistributionError } from "./SqDistributionError";
|
||||
export { SqRecord } from "./SqRecord";
|
||||
export { SqPlot, LabeledDistribution } from "./SqPlot";
|
||||
export { SqLambda } from "./SqLambda";
|
||||
export { SqProject };
|
||||
export { SqValue, SqValueTag };
|
||||
|
@ -14,7 +15,7 @@ export {
|
|||
defaultEnvironment,
|
||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
||||
export { SqError, SqFrame, SqLocation } from "./SqError";
|
||||
export { SqShape } from "./SqPointSetDist";
|
||||
export { SqShape, SqPoint } from "./SqPointSetDist";
|
||||
|
||||
export { resultMap } from "./types";
|
||||
|
||||
|
|
146
packages/squiggle-lang/src/rescript/FR/FR_Plot.res
Normal file
146
packages/squiggle-lang/src/rescript/FR/FR_Plot.res
Normal file
|
@ -0,0 +1,146 @@
|
|||
open FunctionRegistry_Core
|
||||
open FunctionRegistry_Helpers
|
||||
|
||||
let nameSpace = "Plot"
|
||||
|
||||
module FnApp = {
|
||||
type fnApp<'a> = {
|
||||
result: Reducer_T.value => result<'a, SqError.Message.t>,
|
||||
typeRequired: frType,
|
||||
}
|
||||
|
||||
let fmap = (f: 'a => 'b, m: fnApp<'a>): fnApp<'b> => {
|
||||
{
|
||||
result: (a: Reducer_T.value) => E.R.fmap(f, m.result(a)),
|
||||
typeRequired: m.typeRequired,
|
||||
}
|
||||
}
|
||||
|
||||
module Record = {
|
||||
type t<'a> = {
|
||||
result: Reducer_T.map => result<'a, SqError.Message.t>,
|
||||
typesRequired: array<(string, frType)>,
|
||||
}
|
||||
|
||||
let getField = (key: string, parser: fnApp<'a>): t<'a> => {
|
||||
let func = (a: Reducer_T.map) =>
|
||||
switch Belt.Map.String.get(a, key) {
|
||||
| Some(x) => parser.result(x)
|
||||
| None => Error(impossibleError)
|
||||
}
|
||||
{result: func, typesRequired: [(key, parser.typeRequired)]}
|
||||
}
|
||||
|
||||
let merge = (m1: t<'a>, m2: t<'b>): t<('a, 'b)> => {
|
||||
{
|
||||
result: (a: Reducer_T.map) => E.R.merge(m1.result(a), m2.result(a)),
|
||||
typesRequired: Belt.Array.concat(m1.typesRequired, m2.typesRequired),
|
||||
}
|
||||
}
|
||||
|
||||
let fmap = (f: 'a => 'b, m: t<'a>): t<'b> => {
|
||||
{
|
||||
result: (a: Reducer_T.map) => E.R.fmap(f, m.result(a)),
|
||||
typesRequired: m.typesRequired,
|
||||
}
|
||||
}
|
||||
|
||||
let app = (m1: t<'a => 'b>, m2: t<'a>): t<'b> => {
|
||||
{
|
||||
result: (a: Reducer_T.map) =>
|
||||
E.R.merge(m1.result(a), m2.result(a))->E.R2.fmap(((f, x)) => f(x)),
|
||||
typesRequired: Belt.Array.concat(m1.typesRequired, m2.typesRequired),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let getString: fnApp<string> = {
|
||||
let func = (a: Reducer_T.value) =>
|
||||
switch a {
|
||||
| IEvString(s) => Ok(s)
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
{result: func, typeRequired: FRTypeString}
|
||||
}
|
||||
|
||||
let getArray = (child: fnApp<'a>): fnApp<array<'a>> => {
|
||||
let func = (a: Reducer_T.value) =>
|
||||
switch a {
|
||||
| IEvArray(x) => x->E.A2.fmap(child.result)->E.A.R.firstErrorOrOpen
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
{result: func, typeRequired: FRTypeArray(child.typeRequired)}
|
||||
}
|
||||
let getRecord = (recMonad: Record.t<'a>): fnApp<'a> => {
|
||||
let func = (a: Reducer_T.value) =>
|
||||
switch a {
|
||||
| IEvRecord(s) => recMonad.result(s)
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
{result: func, typeRequired: FRTypeRecord(recMonad.typesRequired)}
|
||||
}
|
||||
|
||||
let getDistOrNumber: fnApp<GenericDist.t> = {
|
||||
let func = (a: Reducer_T.value) =>
|
||||
switch a {
|
||||
| IEvDistribution(s) => Ok(s)
|
||||
| IEvNumber(s) => Ok(GenericDist.fromFloat(s))
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
{result: func, typeRequired: FRTypeDistOrNumber}
|
||||
}
|
||||
|
||||
let oneArgDef = (
|
||||
name: string,
|
||||
arg1: fnApp<'a>,
|
||||
def: 'a => result<Reducer_T.value, SqError.Message.t>,
|
||||
): FnDefinition.t =>
|
||||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[arg1.typeRequired],
|
||||
~run=(inputs, _, _) => {
|
||||
E.R.bind(arg1.result(inputs[0]), def)
|
||||
},
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
||||
module Internals = {
|
||||
let makeLabeledDistribution = (
|
||||
name: string,
|
||||
distribution: GenericDist.t,
|
||||
): Reducer_T.labeledDistribution => {name: name, distribution: distribution}
|
||||
|
||||
let getLabeledDistribution: FnApp.fnApp<Reducer_T.labeledDistribution> = {
|
||||
makeLabeledDistribution
|
||||
->FnApp.Record.fmap(FnApp.Record.getField("name", FnApp.getString))
|
||||
->FnApp.Record.app(FnApp.Record.getField("value", FnApp.getDistOrNumber))
|
||||
->FnApp.getRecord
|
||||
}
|
||||
|
||||
let makePlot = (show: array<Reducer_T.labeledDistribution>): Reducer_T.plotValue => {
|
||||
distributions: show,
|
||||
}
|
||||
|
||||
let parsePlotValue: FnApp.fnApp<Reducer_T.plotValue> = {
|
||||
makePlot
|
||||
->FnApp.Record.fmap(FnApp.Record.getField("show", FnApp.getArray(getLabeledDistribution)))
|
||||
->FnApp.getRecord
|
||||
}
|
||||
}
|
||||
|
||||
let library = [
|
||||
Function.make(
|
||||
~name="dist",
|
||||
~nameSpace,
|
||||
~requiresNamespace=true,
|
||||
~output=EvtPlot,
|
||||
~examples=[
|
||||
`Plot.dist({show: [{name: "Control", value: 1 to 2}, {name: "Treatment", value: 1.5 to 2.5}]}) `,
|
||||
],
|
||||
~definitions=[
|
||||
FnApp.oneArgDef("dist", Internals.parsePlotValue, (a: Reducer_T.plotValue) => Ok(IEvPlot(a))),
|
||||
],
|
||||
(),
|
||||
),
|
||||
]
|
|
@ -6,6 +6,7 @@ type error = SqError.t //use
|
|||
type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use
|
||||
type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use
|
||||
type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use
|
||||
@genType type squiggleValue_Plot = Reducer_T.plotValue //use
|
||||
|
||||
// Return values are kept as they are if they are JavaScript types.
|
||||
|
||||
|
@ -30,6 +31,9 @@ external svtLambda_: string = "Lambda"
|
|||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
|
||||
external svtNumber_: string = "Number"
|
||||
|
||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
|
||||
external svtPlot_: string = "Plot"
|
||||
|
||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
|
||||
external svtRecord_: string = "Record"
|
||||
|
||||
|
@ -57,6 +61,7 @@ let getTag = (variant: squiggleValue): squiggleValueTag =>
|
|||
| IEvDistribution(_) => svtDistribution_->castEnum
|
||||
| IEvLambda(_) => svtLambda_->castEnum
|
||||
| IEvNumber(_) => svtNumber_->castEnum
|
||||
| IEvPlot(_) => svtPlot_->castEnum
|
||||
| IEvRecord(_) => svtRecord_->castEnum
|
||||
| IEvString(_) => svtString_->castEnum
|
||||
| IEvTimeDuration(_) => svtTimeDuration_->castEnum
|
||||
|
@ -122,6 +127,13 @@ let getNumber = (variant: squiggleValue): option<float> =>
|
|||
| _ => None
|
||||
}
|
||||
|
||||
@genType
|
||||
let getPlot = (variant: squiggleValue): option<squiggleValue_Plot> =>
|
||||
switch variant {
|
||||
| IEvPlot(value) => value->Some
|
||||
| _ => None
|
||||
}
|
||||
|
||||
@genType
|
||||
let getRecord = (variant: squiggleValue): option<squiggleValue_Record> =>
|
||||
switch variant {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
|
||||
@genType type squiggleValue_Plot = ForTS_SquiggleValue.squiggleValue_Plot //re-export recursive type
|
||||
@genType type labeledDistribution = Reducer_T.labeledDistribution // use
|
||||
|
||||
@genType
|
||||
let toString = (v: squiggleValue_Plot) => Reducer_Value.toStringPlot(v)
|
|
@ -6,6 +6,7 @@ export enum squiggleValueTag {
|
|||
Distribution = "Distribution",
|
||||
Lambda = "Lambda",
|
||||
Number = "Number",
|
||||
Plot = "Plot",
|
||||
Record = "Record",
|
||||
String = "String",
|
||||
TimeDuration = "TimeDuration",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
type internalExpressionValueType = Reducer_Value.internalExpressionValueType
|
||||
let valueTypeToString = Reducer_Value.valueTypeToString
|
||||
type errorMessage = SqError.Message.t
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
let fnList = Belt.Array.concatMany([
|
||||
FR_Builtin.library,
|
||||
FR_Danger.library,
|
||||
FR_Date.library,
|
||||
FR_Dict.library,
|
||||
FR_Dist.library,
|
||||
FR_Danger.library,
|
||||
FR_Fn.library,
|
||||
FR_Sampleset.library,
|
||||
FR_List.library,
|
||||
FR_Number.library,
|
||||
FR_Pointset.library,
|
||||
FR_Scoring.library,
|
||||
FR_GenericDist.library,
|
||||
FR_Units.library,
|
||||
FR_Date.library,
|
||||
FR_List.library,
|
||||
FR_Math.library,
|
||||
FR_Number.library,
|
||||
FR_Plot.library,
|
||||
FR_Pointset.library,
|
||||
FR_Sampleset.library,
|
||||
FR_Scoring.library,
|
||||
FR_Units.library,
|
||||
])
|
||||
|
||||
let registry = FunctionRegistry_Core.Registry.make(fnList)
|
||||
|
|
|
@ -9,10 +9,12 @@ type rec value =
|
|||
| IEvDistribution(DistributionTypes.genericDist)
|
||||
| IEvLambda(lambdaValue)
|
||||
| IEvNumber(float)
|
||||
| IEvPlot(plotValue)
|
||||
| IEvRecord(map)
|
||||
| IEvString(string)
|
||||
| IEvTimeDuration(float)
|
||||
| IEvVoid
|
||||
|
||||
@genType.opaque and arrayValue = array<value>
|
||||
@genType.opaque and map = Belt.Map.String.t<value>
|
||||
and lambdaBody = (array<value>, context, reducerFn) => value
|
||||
|
@ -66,4 +68,12 @@ and context = {
|
|||
|
||||
and reducerFn = (expression, context) => (value, context)
|
||||
|
||||
@genType and plotValue = {distributions: array<labeledDistribution>}
|
||||
|
||||
@genType
|
||||
and labeledDistribution = {
|
||||
name: string,
|
||||
distribution: DistributionTypes.genericDist,
|
||||
}
|
||||
|
||||
let topFrameName = "<top>"
|
||||
|
|
|
@ -14,6 +14,7 @@ let rec toString = (aValue: T.value) =>
|
|||
| IEvDistribution(dist) => toStringDistribution(dist)
|
||||
| IEvLambda(lambdaValue) => toStringLambda(lambdaValue)
|
||||
| IEvNumber(aNumber) => toStringNumber(aNumber)
|
||||
| IEvPlot(aPlot) => toStringPlot(aPlot)
|
||||
| IEvRecord(aMap) => aMap->toStringRecord
|
||||
| IEvString(aString) => toStringString(aString)
|
||||
| IEvTimeDuration(t) => toStringTimeDuration(t)
|
||||
|
@ -35,6 +36,10 @@ and toStringLambda = (lambdaValue: T.lambdaValue) => {
|
|||
}
|
||||
}
|
||||
and toStringNumber = aNumber => Js.String.make(aNumber)
|
||||
and toStringPlot = aPlot => {
|
||||
let chartNames = E.A.fmap((x: Reducer_T.labeledDistribution) => x.name, aPlot.distributions)
|
||||
`Plot showing ${Js.Array2.toString(chartNames)}`
|
||||
}
|
||||
and toStringRecord = aMap => aMap->toStringMap
|
||||
and toStringString = aString => `'${aString}'`
|
||||
and toStringSymbol = aString => `:${aString}`
|
||||
|
@ -59,6 +64,7 @@ let toStringWithType = (aValue: T.value) =>
|
|||
| IEvDistribution(_) => `Distribution::${toString(aValue)}`
|
||||
| IEvLambda(_) => `Lambda::${toString(aValue)}`
|
||||
| IEvNumber(_) => `Number::${toString(aValue)}`
|
||||
| IEvPlot(_) => `Plot::${toString(aValue)}`
|
||||
| IEvRecord(_) => `Record::${toString(aValue)}`
|
||||
| IEvString(_) => `String::${toString(aValue)}`
|
||||
| IEvTimeDuration(_) => `Date::${toString(aValue)}`
|
||||
|
@ -91,6 +97,7 @@ type internalExpressionValueType =
|
|||
| EvtDistribution
|
||||
| EvtLambda
|
||||
| EvtNumber
|
||||
| EvtPlot
|
||||
| EvtRecord
|
||||
| EvtString
|
||||
| EvtTimeDuration
|
||||
|
@ -109,6 +116,7 @@ let valueToValueType = (value: T.value) =>
|
|||
| IEvDistribution(_) => EvtDistribution
|
||||
| IEvLambda(_) => EvtLambda
|
||||
| IEvNumber(_) => EvtNumber
|
||||
| IEvPlot(_) => EvtPlot
|
||||
| IEvRecord(_) => EvtRecord
|
||||
| IEvString(_) => EvtString
|
||||
| IEvTimeDuration(_) => EvtTimeDuration
|
||||
|
@ -129,6 +137,7 @@ let valueTypeToString = (valueType: internalExpressionValueType): string =>
|
|||
| EvtDistribution => `Distribution`
|
||||
| EvtLambda => `Lambda`
|
||||
| EvtNumber => `Number`
|
||||
| EvtPlot => `Plot`
|
||||
| EvtRecord => `Record`
|
||||
| EvtString => `String`
|
||||
| EvtTimeDuration => `Duration`
|
||||
|
|
19
packages/website/docs/Api/Plot.md
Normal file
19
packages/website/docs/Api/Plot.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
sidebar_position: 8
|
||||
title: Plot
|
||||
---
|
||||
|
||||
Plot objects can be created to make plots of different kinds. If you wish to plot
|
||||
multiple distributions simultaneously, you can use `Plot.dist`.
|
||||
|
||||
**Example**
|
||||
|
||||
### dist
|
||||
|
||||
```
|
||||
Plot.dist({show: list({name: string, value: distribution|number})})
|
||||
```
|
||||
|
||||
```js
|
||||
Plot.dist({show: [{name: "normal", value: normal(0, 1)}, {name: "lognormal", value: 2 to 3}]})
|
||||
```
|
Loading…
Reference in New Issue
Block a user