Merge branch 'develop' into function-charts

This commit is contained in:
Sam Nolan 2022-05-10 15:57:59 +00:00
commit 07b93e5080
8 changed files with 155 additions and 30 deletions

View File

@ -43,7 +43,7 @@
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.3",
"web-vitals": "^2.1.4",
"webpack": "^5.72.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
},

View File

@ -1,7 +1,11 @@
import * as React from "react";
import _ from "lodash";
import type { Distribution } from "@quri/squiggle-lang";
import { distributionErrorToString } from "@quri/squiggle-lang";
import {
Distribution,
result,
distributionError,
distributionErrorToString,
} from "@quri/squiggle-lang";
import { Vega, VisualizationSpec } from "react-vega";
import * as chartSpecification from "../vega-specs/spec-distributions.json";
import { ErrorBox } from "./ErrorBox";
@ -13,11 +17,14 @@ import {
expYScale,
} from "./DistributionVegaScales";
import styled from "styled-components";
import { NumberShower } from "./NumberShower";
type DistributionChartProps = {
distribution: Distribution;
width?: number;
height: number;
/** Whether to show a summary of means, stdev, percentiles etc */
showSummary: boolean;
/** Whether to show the user graph controls (scale etc) */
showControls?: boolean;
};
@ -25,6 +32,7 @@ type DistributionChartProps = {
export const DistributionChart: React.FC<DistributionChartProps> = ({
distribution,
height,
showSummary,
width,
showControls = false,
}: DistributionChartProps) => {
@ -37,7 +45,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
shape.value.continuous.some((x) => x.x <= 0) ||
shape.value.discrete.some((x) => x.x <= 0);
let spec = buildVegaSpec(isLogX, isExpY);
let widthProp = width ? width - 20 : size.width - 10;
let widthProp = width ? width : size.width;
// Check whether we should disable the checkbox
var logCheckbox = (
@ -58,21 +66,22 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
}
var result = (
<div>
<ChartContainer width={widthProp + "px"}>
<Vega
spec={spec}
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
width={widthProp}
width={widthProp - 10}
height={height}
actions={false}
/>
{showSummary && <SummaryTable distribution={distribution} />}
{showControls && (
<div>
{logCheckbox}
<CheckBox label="Exp Y scale" value={isExpY} onChange={setExpY} />
</div>
)}
</div>
</ChartContainer>
);
} else {
var result = (
@ -87,6 +96,12 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
return sized;
};
type ChartContainerProps = { width: string };
let ChartContainer = styled.div<ChartContainerProps>`
width: ${(props) => props.width};
`;
function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec {
return {
...chartSpecification,
@ -128,3 +143,90 @@ export const CheckBox = ({
</span>
);
};
type SummaryTableProps = {
distribution: Distribution;
};
const Table = styled.table`
margin-left: auto;
margin-right: auto;
border-collapse: collapse;
text-align: center;
border-style: hidden;
`;
const TableHead = styled.thead`
border-bottom: 1px solid rgb(141 149 167);
`;
const TableHeadCell = styled.th`
border-right: 1px solid rgb(141 149 167);
border-left: 1px solid rgb(141 149 167);
padding: 0.3em;
`;
const TableBody = styled.tbody``;
const Row = styled.tr``;
const Cell = styled.td`
padding: 0.3em;
border-right: 1px solid rgb(141 149 167);
border-left: 1px solid rgb(141 149 167);
`;
const SummaryTable: React.FC<SummaryTableProps> = ({
distribution,
}: SummaryTableProps) => {
let mean = distribution.mean();
let p5 = distribution.inv(0.05);
let p10 = distribution.inv(0.1);
let p25 = distribution.inv(0.25);
let p50 = distribution.inv(0.5);
let p75 = 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 <NumberShower number={x.value} />;
} else {
return (
<ErrorBox heading="Distribution Error">
{distributionErrorToString(x.value)}
</ErrorBox>
);
}
};
return (
<Table>
<TableHead>
<Row>
<TableHeadCell>{"Mean"}</TableHeadCell>
<TableHeadCell>{"5%"}</TableHeadCell>
<TableHeadCell>{"10%"}</TableHeadCell>
<TableHeadCell>{"25%"}</TableHeadCell>
<TableHeadCell>{"50%"}</TableHeadCell>
<TableHeadCell>{"75%"}</TableHeadCell>
<TableHeadCell>{"90%"}</TableHeadCell>
<TableHeadCell>{"95%"}</TableHeadCell>
</Row>
</TableHead>
<TableBody>
<Row>
<Cell>{unwrapResult(mean)}</Cell>
<Cell>{unwrapResult(p5)}</Cell>
<Cell>{unwrapResult(p10)}</Cell>
<Cell>{unwrapResult(p25)}</Cell>
<Cell>{unwrapResult(p50)}</Cell>
<Cell>{unwrapResult(p75)}</Cell>
<Cell>{unwrapResult(p90)}</Cell>
<Cell>{unwrapResult(p95)}</Cell>
</Row>
</TableBody>
</Table>
);
};

View File

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

View File

@ -66,6 +66,8 @@ export interface SquiggleItemProps {
expression: squiggleExpression;
width?: number;
height: number;
/** Whether to show a summary of statistics for distributions */
showSummary: boolean;
/** Whether to show type information */
showTypes: boolean;
/** Whether to show users graph controls (scale etc) */
@ -80,6 +82,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
expression,
width,
height,
showSummary,
showTypes = false,
showControls = false,
chartSettings,
@ -110,6 +113,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
distribution={expression.value}
height={height}
width={width}
showSummary={showSummary}
showControls={showControls}
/>
</VariableBox>
@ -152,6 +156,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
showControls={showControls}
chartSettings={chartSettings}
environment={environment}
showSummary={showSummary}
/>
))}
</VariableBox>
@ -167,6 +172,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
width={width !== undefined ? width - 20 : width}
height={50}
showTypes={showTypes}
showSummary={showSummary}
showControls={showControls}
chartSettings={chartSettings}
environment={environment}
@ -209,7 +215,9 @@ export interface SquiggleChartProps {
/** Bindings of previous variables declared */
bindings: bindings;
/** JS imported parameters */
jsImports: jsImports;
jsImports?: jsImports;
/** Whether to show a summary of the distirbution */
showSummary?: boolean;
/** Whether to show type information about returns, default false */
showTypes: boolean;
/** Whether to show graph controls (scale etc)*/
@ -230,6 +238,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
height = 60,
bindings = defaultBindings,
jsImports = defaultImports,
showSummary = false,
width,
showTypes = false,
showControls = false,
@ -245,6 +254,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
expression={expression}
width={width}
height={height}
showSummary={showSummary}
showTypes={showTypes}
showControls={showControls}
chartSettings={chartSettings}

View File

@ -41,7 +41,9 @@ export interface SquiggleEditorProps {
/** Whether to show detail about types of the returns, default false */
showTypes?: boolean;
/** Whether to give users access to graph controls */
showControls: boolean;
showControls?: boolean;
/** Whether to show a summary table */
showSummary?: boolean;
}
const Input = styled.div`
@ -63,6 +65,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
jsImports = defaultImports,
showTypes = false,
showControls = false,
showSummary = false,
}: SquiggleEditorProps) => {
let [expression, setExpression] = React.useState(initialSquiggleString);
let chartSettings = {
@ -96,6 +99,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
jsImports={jsImports}
showTypes={showTypes}
showControls={showControls}
showSummary={showSummary}
/>
</div>
);

View File

@ -45,18 +45,11 @@ function FieldFloat(Props: FieldFloatProps) {
);
}
interface Props {
initialSquiggleString?: string;
height?: number;
showTypes?: boolean;
showControls?: boolean;
}
interface Props2 {
interface ShowBoxProps {
height: number;
}
const ShowBox = styled.div<Props2>`
const ShowBox = styled.div<ShowBoxProps>`
border: 1px solid #eee;
border-radius: 2px;
height: ${(props) => props.height};
@ -81,12 +74,26 @@ const Row = styled.div`
`;
const Col = styled.div``;
let SquigglePlayground: FC<Props> = ({
interface PlaygroundProps {
/** The initial squiggle string to put in the playground */
initialSquiggleString?: string;
/** How many pixels high is the playground */
height?: number;
/** Whether to show the types of outputs in the playground */
showTypes?: boolean;
/** Whether to show the log scale controls in the playground */
showControls?: boolean;
/** Whether to show the summary table in the playground */
showSummary?: boolean;
}
let SquigglePlayground: FC<PlaygroundProps> = ({
initialSquiggleString = "",
height = 300,
showTypes = false,
showControls = false,
}: Props) => {
showSummary = false,
}: PlaygroundProps) => {
let [squiggleString, setSquiggleString] = useState(initialSquiggleString);
let [sampleCount, setSampleCount] = useState(1000);
let [outputXYPoints, setOutputXYPoints] = useState(1000);
@ -126,6 +133,7 @@ let SquigglePlayground: FC<Props> = ({
showControls={showControls}
bindings={defaultBindings}
jsImports={defaultImports}
showSummary={showSummary}
/>
</Display>
</Col>
@ -134,7 +142,7 @@ let SquigglePlayground: FC<Props> = ({
);
};
export default SquigglePlayground;
export function renderSquigglePlaygroundToDom(props: Props) {
export function renderSquigglePlaygroundToDom(props: PlaygroundProps) {
let parent = document.createElement("div");
ReactDOM.render(<SquigglePlayground {...props} />, parent);
return parent;

View File

@ -60,7 +60,7 @@
"ts-loader": "^9.3.0",
"ts-node": "^10.7.0",
"typescript": "^4.6.3",
"webpack": "^5.72.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
},
"source": "./src/js/index.ts",

View File

@ -8017,7 +8017,7 @@ enhanced-resolve@^4.5.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0, enhanced-resolve@^5.9.2:
enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0, enhanced-resolve@^5.9.3:
version "5.9.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
@ -11301,7 +11301,7 @@ json-parse-better-errors@^1.0.2:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
json-parse-even-better-errors@^2.3.0:
json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
@ -18228,10 +18228,10 @@ webpack@4:
watchpack "^1.7.4"
webpack-sources "^1.4.1"
webpack@^5, webpack@^5.64.4, webpack@^5.72.0, webpack@^5.9.0:
version "5.72.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.0.tgz#f8bc40d9c6bb489a4b7a8a685101d6022b8b6e28"
integrity sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==
webpack@^5, webpack@^5.64.4, webpack@^5.72.0, webpack@^5.72.1, webpack@^5.9.0:
version "5.72.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.1.tgz#3500fc834b4e9ba573b9f430b2c0a61e1bb57d13"
integrity sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^0.0.51"
@ -18242,13 +18242,13 @@ webpack@^5, webpack@^5.64.4, webpack@^5.72.0, webpack@^5.9.0:
acorn-import-assertions "^1.7.6"
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.9.2"
enhanced-resolve "^5.9.3"
es-module-lexer "^0.9.0"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.9"
json-parse-better-errors "^1.0.2"
json-parse-even-better-errors "^2.3.1"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"