Basic stats table. Unformatted

This commit is contained in:
Sam Nolan 2022-04-30 15:16:13 +00:00
parent 99b9bc6b1e
commit 64fbe05f3a
4 changed files with 113 additions and 20 deletions

View File

@ -1,8 +1,12 @@
import * as React from "react"; import * as React from "react";
import _ from "lodash"; import _ from "lodash";
import type { Spec } from "vega"; import type { Spec } from "vega";
import type { Distribution } from "@quri/squiggle-lang"; import {
import { distributionErrorToString } from "@quri/squiggle-lang"; Distribution,
result,
distributionError,
distributionErrorToString,
} from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega"; import { createClassFromSpec } from "react-vega";
import * as chartSpecification from "../vega-specs/spec-distributions.json"; import * as chartSpecification from "../vega-specs/spec-distributions.json";
import { ErrorBox } from "./ErrorBox"; import { ErrorBox } from "./ErrorBox";
@ -16,30 +20,101 @@ type DistributionChartProps = {
distribution: Distribution; distribution: Distribution;
width: number; width: number;
height: number; height: number;
/** Whether to show a summary of means, stdev, percentiles etc */
showSummary: boolean;
}; };
export const DistributionChart: React.FC<DistributionChartProps> = ({ export const DistributionChart: React.FC<DistributionChartProps> = ({
distribution, distribution,
width, width,
height, height,
showSummary,
}: DistributionChartProps) => { }: DistributionChartProps) => {
let shape = distribution.pointSet(); let shape = distribution.pointSet();
if (shape.tag === "Ok") { if (shape.tag === "Ok") {
let widthProp = width ? width - 20 : undefined; let widthProp = width ? width - 20 : undefined;
var result = ( return (
<SquiggleVegaChart <>
data={{ con: shape.value.continuous, dis: shape.value.discrete }} <SquiggleVegaChart
width={widthProp} data={{ con: shape.value.continuous, dis: shape.value.discrete }}
height={height} width={widthProp}
actions={false} height={height}
/> actions={false}
/>
{showSummary ? <SummaryTable distribution={distribution} /> : <></>}
</>
); );
} else { } else {
var result = ( return (
<ErrorBox heading="Distribution Error"> <ErrorBox heading="Distribution Error">
{distributionErrorToString(shape.value)} {distributionErrorToString(shape.value)}
</ErrorBox> </ErrorBox>
); );
} }
return result; };
type SummaryTableProps = {
distribution: Distribution;
};
const Table = styled.table``;
const Row = styled.tr``;
const Cell = styled.td``;
const TableHeader = styled.th``;
const SummaryTable: React.FC<SummaryTableProps> = ({
distribution,
}: SummaryTableProps) => {
let mean = distribution.mean();
let median = distribution.inv(0.5);
let p5 = distribution.inv(0.05);
let p10 = distribution.inv(0.1);
let Q1 = distribution.inv(0.25);
let Q3 = distribution.inv(0.75);
let p90 = distribution.inv(0.9);
let p95 = distribution.inv(0.95);
let unwrapResult = (
x: result<number, distributionError>
): React.ReactNode => {
if (x.tag === "Ok") {
return (
<span>
{Intl.NumberFormat("en-US", { maximumSignificantDigits: 3 }).format(
x.value
)}
</span>
);
} else {
return (
<ErrorBox heading="Distribution Error">
{distributionErrorToString(x.value)}
</ErrorBox>
);
}
};
return (
<Table>
<Row>
<TableHeader>{"Mean"}</TableHeader>
<TableHeader>{"5%"}</TableHeader>
<TableHeader>{"10%"}</TableHeader>
<TableHeader>{"Q1 (25%)"}</TableHeader>
<TableHeader>{"Median (50%)"}</TableHeader>
<TableHeader>{"Q3 (75%)"}</TableHeader>
<TableHeader>{"90%"}</TableHeader>
<TableHeader>{"95%"}</TableHeader>
</Row>
<Row>
<Cell>{unwrapResult(mean)}</Cell>
<Cell>{unwrapResult(p5)}</Cell>
<Cell>{unwrapResult(p10)}</Cell>
<Cell>{unwrapResult(Q1)}</Cell>
<Cell>{unwrapResult(median)}</Cell>
<Cell>{unwrapResult(Q3)}</Cell>
<Cell>{unwrapResult(p90)}</Cell>
<Cell>{unwrapResult(p95)}</Cell>
</Row>
</Table>
);
}; };

View File

@ -61,6 +61,7 @@ export const FunctionChart: React.FC<{
distribution={mouseItem.value} distribution={mouseItem.value}
width={400} width={400}
height={140} height={140}
showSummary={false}
/> />
) : ( ) : (
<></> <></>

View File

@ -54,12 +54,15 @@ export interface SquiggleItemProps {
expression: squiggleExpression; expression: squiggleExpression;
width: number; width: number;
height: number; height: number;
/** Whether to show a summary of statistics for distributions */
showSummary: boolean;
} }
const SquiggleItem: React.FC<SquiggleItemProps> = ({ const SquiggleItem: React.FC<SquiggleItemProps> = ({
expression, expression,
width, width,
height, height,
showSummary,
}: SquiggleItemProps) => { }: SquiggleItemProps) => {
switch (expression.tag) { switch (expression.tag) {
case "number": case "number":
@ -83,6 +86,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
distribution={expression.value} distribution={expression.value}
height={height} height={height}
width={width} width={width}
showSummary={showSummary}
/> />
</VariableBox> </VariableBox>
); );
@ -105,7 +109,12 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
return ( return (
<VariableBox heading="Array"> <VariableBox heading="Array">
{expression.value.map((r) => ( {expression.value.map((r) => (
<SquiggleItem expression={r} width={width - 20} height={50} /> <SquiggleItem
expression={r}
width={width - 20}
height={50}
showSummary={showSummary}
/>
))} ))}
</VariableBox> </VariableBox>
); );
@ -115,17 +124,16 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
{Object.entries(expression.value).map(([key, r]) => ( {Object.entries(expression.value).map(([key, r]) => (
<> <>
<RecordKeyHeader>{key}</RecordKeyHeader> <RecordKeyHeader>{key}</RecordKeyHeader>
<SquiggleItem expression={r} width={width - 20} height={50} /> <SquiggleItem
expression={r}
width={width - 20}
height={50}
showSummary={showSummary}
/>
</> </>
))} ))}
</VariableBox> </VariableBox>
); );
default:
return (
<ErrorBox heading="No Viewer">
{"We don't currently have a working viewer for record types."}
</ErrorBox>
);
} }
}; };
@ -155,6 +163,8 @@ export interface SquiggleChartProps {
bindings?: bindings; bindings?: bindings;
/** JS imported parameters */ /** JS imported parameters */
jsImports?: jsImports; jsImports?: jsImports;
/** Whether to show a summary of the distirbution */
showSummary?: boolean;
} }
const ChartWrapper = styled.div` const ChartWrapper = styled.div`
@ -172,6 +182,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
bindings = defaultBindings, bindings = defaultBindings,
jsImports = defaultImports, jsImports = defaultImports,
width = NaN, width = NaN,
showSummary = false,
}: SquiggleChartProps) => { }: SquiggleChartProps) => {
let samplingInputs: samplingParams = { let samplingInputs: samplingParams = {
sampleCount: sampleCount, sampleCount: sampleCount,
@ -188,7 +199,12 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
let expression = expressionResult.value; let expression = expressionResult.value;
onChange(expression); onChange(expression);
internal = ( internal = (
<SquiggleItem expression={expression} width={width} height={height} /> <SquiggleItem
expression={expression}
width={width}
height={height}
showSummary={showSummary}
/>
); );
} else { } else {
internal = ( internal = (

View File

@ -11,6 +11,7 @@ export {
makeSampleSetDist, makeSampleSetDist,
errorValueToString, errorValueToString,
distributionErrorToString, distributionErrorToString,
distributionError,
} from "../rescript/TypescriptInterface.gen"; } from "../rescript/TypescriptInterface.gen";
export type { export type {
samplingParams, samplingParams,