Cached FunctionChart percentiles calculation

This commit is contained in:
Ozzie Gooen 2022-05-15 14:39:50 -04:00
parent 2da5d5394e
commit b015c20fa4
4 changed files with 93 additions and 70 deletions

View File

@ -7,6 +7,8 @@ import {
lambdaValue, lambdaValue,
environment, environment,
runForeign, runForeign,
squiggleExpression,
errorValue,
errorValueToString, errorValueToString,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega"; import { createClassFromSpec } from "react-vega";
@ -45,6 +47,23 @@ interface FunctionChartProps {
environment: environment; environment: environment;
} }
type percentiles = {
x: number;
p1: number;
p5: number;
p10: number;
p20: number;
p30: number;
p40: number;
p50: number;
p60: number;
p70: number;
p80: number;
p90: number;
p95: number;
p99: number;
}[];
export const FunctionChart: React.FC<FunctionChartProps> = ({ export const FunctionChart: React.FC<FunctionChartProps> = ({
fn, fn,
chartSettings, chartSettings,
@ -58,7 +77,9 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
setMouseOverlay(NaN); setMouseOverlay(NaN);
} }
const signalListeners = { mousemove: handleHover, mouseout: handleOut }; const signalListeners = { mousemove: handleHover, mouseout: handleOut };
let mouseItem = runForeign(fn, [mouseOverlay], environment); let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
? runForeign(fn, [mouseOverlay], { sampleCount: 10000, xyPointLength: 1000 })
: { tag: "Error", value: { tag: "REExpectedType", value: "Expected float, got NaN" } };
let showChart = let showChart =
mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? ( mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? (
<DistributionChart <DistributionChart
@ -76,9 +97,9 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
chartSettings.count chartSettings.count
); );
type point = { x: number; value: result<Distribution, string> }; type point = { x: number; value: result<Distribution, string> };
let valueData: point[] = React.useMemo(
() => let getPercentiles: () => percentiles = () => {
data1.map((x) => { let valueData:any = data1.map((x) => {
let result = runForeign(fn, [x], environment); let result = runForeign(fn, [x], environment);
if (result.tag === "Ok") { if (result.tag === "Ok") {
if (result.value.tag == "distribution") { if (result.value.tag == "distribution") {
@ -99,10 +120,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
value: { tag: "Error", value: errorValueToString(result.value) }, value: { tag: "Error", value: errorValueToString(result.value) },
}; };
} }
}), });
[environment, fn]
);
let initialPartition: [ let initialPartition: [
{ x: number; value: Distribution }[], { x: number; value: Distribution }[],
{ x: number; value: string }[] { x: number; value: string }[]
@ -116,46 +134,38 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
return acc; return acc;
}, initialPartition); }, initialPartition);
let percentiles = functionImage.map(({ x, value }) => { let percentiles:percentiles = functionImage.map(({ x, value }) => {
let toPointSet: Distribution = unwrap(value.toPointSet());
return { return {
x: x, x: x,
p1: unwrap(value.inv(0.01)), p1: unwrap(toPointSet.inv(0.01)),
p5: unwrap(value.inv(0.05)), p5: unwrap(toPointSet.inv(0.05)),
p10: unwrap(value.inv(0.12)), p10: unwrap(toPointSet.inv(0.12)),
p20: unwrap(value.inv(0.2)), p20: unwrap(toPointSet.inv(0.2)),
p30: unwrap(value.inv(0.3)), p30: unwrap(toPointSet.inv(0.3)),
p40: unwrap(value.inv(0.4)), p40: unwrap(toPointSet.inv(0.4)),
p50: unwrap(value.inv(0.5)), p50: unwrap(toPointSet.inv(0.5)),
p60: unwrap(value.inv(0.6)), p60: unwrap(toPointSet.inv(0.6)),
p70: unwrap(value.inv(0.7)), p70: unwrap(toPointSet.inv(0.7)),
p80: unwrap(value.inv(0.8)), p80: unwrap(toPointSet.inv(0.8)),
p90: unwrap(value.inv(0.9)), p90: unwrap(toPointSet.inv(0.9)),
p95: unwrap(value.inv(0.95)), p95: unwrap(toPointSet.inv(0.95)),
p99: unwrap(value.inv(0.99)), p99: unwrap(toPointSet.inv(0.99)),
}; };
}); });
return percentiles;
};
let _getPercentiles = React.useMemo(getPercentiles, [environment, fn])
let groupedErrors = _.groupBy(errors, (x) => x.value);
return ( return (
<> <>
<SquigglePercentilesChart <SquigglePercentilesChart
data={{ facet: percentiles }} data={{ facet: _getPercentiles }}
actions={false} actions={false}
signalListeners={signalListeners} signalListeners={signalListeners}
/> />
{showChart} {showChart}
{_.entries(groupedErrors).map(([errorName, errorPoints]) => (
<ErrorBox key={errorName} heading={errorName}>
Values:{" "}
{errorPoints
.map((r, i) => <NumberShower key={i} number={r.x} />)
.reduce((a, b) => (
<>
{a}, {b}
</>
))}
</ErrorBox>
))}
</> </>
); );
}; };

View File

@ -194,7 +194,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
<FunctionChart <FunctionChart
fn={expression.value} fn={expression.value}
chartSettings={chartSettings} chartSettings={chartSettings}
environment={environment} environment={{ sampleCount: environment.sampleCount / 10, xyPointLength: environment.sampleCount / 10 }}
/> />
); );
} }
@ -232,7 +232,7 @@ const ChartWrapper = styled.div`
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
`; `;
let defaultChartSettings = { start: 0, stop: 10, count: 100 }; let defaultChartSettings = { start: 0, stop: 10, count: 20 };
export const SquiggleChart: React.FC<SquiggleChartProps> = ({ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
squiggleString = "", squiggleString = "",
environment, environment,
@ -247,7 +247,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
chartSettings = defaultChartSettings, chartSettings = defaultChartSettings,
}: SquiggleChartProps) => { }: SquiggleChartProps) => {
let expressionResult = run(squiggleString, bindings, environment, jsImports); let expressionResult = run(squiggleString, bindings, environment, jsImports);
let e = environment ? environment : defaultEnvironment; let e = environment ? environment : { sampleCount: 100000, xyPointLength: 1000 };
let internal: JSX.Element; let internal: JSX.Element;
if (expressionResult.tag === "Ok") { if (expressionResult.tag === "Ok") {
let expression = expressionResult.value; let expression = expressionResult.value;

View File

@ -56,7 +56,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
environment, environment,
diagramStart = 0, diagramStart = 0,
diagramStop = 10, diagramStop = 10,
diagramCount = 100, diagramCount = 20,
onChange, onChange,
bindings = defaultBindings, bindings = defaultBindings,
jsImports = defaultImports, jsImports = defaultImports,

View File

@ -153,6 +153,19 @@ to allow large and small numbers being printed cleanly.
</Story> </Story>
</Canvas> </Canvas>
## Functions
<Canvas>
<Story
name="Function"
args={{
squiggleString: "foo(t) = normal(t,2)*normal(5,3); foo",
width,
}}
>
{Template.bind({})}
</Story>
</Canvas>
## Records ## Records
<Canvas> <Canvas>