Add Discrete chart viewer

This commit is contained in:
Sam Nolan 2022-02-13 20:20:53 +11:00
parent 974ca2335e
commit 3d3450d281
4 changed files with 108 additions and 25 deletions

View File

@ -12,10 +12,12 @@
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0", "@types/jest": "^27.4.0",
"@types/lodash": "^4.14.178",
"@types/node": "^17.0.16", "@types/node": "^17.0.16",
"@types/react": "^17.0.39", "@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
@ -11544,6 +11546,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
}, },
"node_modules/@types/lodash": {
"version": "4.14.178",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
"integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw=="
},
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@ -40916,6 +40923,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
}, },
"@types/lodash": {
"version": "4.14.178",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
"integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw=="
},
"@types/mdast": { "@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",

View File

@ -8,10 +8,12 @@
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0", "@types/jest": "^27.4.0",
"@types/lodash": "^4.14.178",
"@types/node": "^17.0.16", "@types/node": "^17.0.16",
"@types/react": "^17.0.39", "@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",

View File

@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import * as _ from 'lodash';
import './button.css'; import './button.css';
import type { LinearScale, Spec } from 'vega'; import type { LinearScale, Spec } from 'vega';
import { run } from '@squiggle/squiggle-lang'; import { run } from '@squiggle/squiggle-lang';
@ -11,16 +12,24 @@ let scales : LinearScale[] = [{
"range": "width", "range": "width",
"zero": false, "zero": false,
"nice": false, "nice": false,
"domain": "domain": {
{"data": "table", "field": "x"} "fields": [
{ "data": "con", "field": "x"},
{ "data": "dis", "field": "x"}
]
}
}, { }, {
"name": "yscale", "name": "yscale",
"type": "linear", "type": "linear",
"range": "height", "range": "height",
"nice": true, "nice": true,
"zero": true, "zero": true,
"domain": "domain": {
{"data": "table", "field": "y"} "fields": [
{ "data": "con", "field": "y"},
{ "data": "dis", "field": "y"}
]
}
} }
] ]
@ -31,7 +40,7 @@ let specification : Spec = {
"width": 500, "width": 500,
"height": 200, "height": 200,
"padding": 5, "padding": 5,
"data": [{"name": "table"}], "data": [{"name": "con"}, {"name": "dis"}],
"signals": [ "signals": [
{ {
@ -52,7 +61,7 @@ let specification : Spec = {
"marks": [ "marks": [
{ {
"type": "area", "type": "area",
"from": {"data": "table"}, "from": {"data": "con"},
"encode": { "encode": {
"enter": { "enter": {
"x": {"scale": "xscale", "field": "x"}, "x": {"scale": "xscale", "field": "x"},
@ -68,22 +77,37 @@ let specification : Spec = {
"fillOpacity": {"value": 1} "fillOpacity": {"value": 1}
} }
} }
},
{
"type": "rect",
"from": {"data": "dis"},
"encode": {
"enter": {
"x": {"scale": "xscale", "field": "x"},
"y": {"scale": "yscale", "field": "y"},
"y2": {"scale": "yscale", "value": 0},
"width": {"value": 1}
}
}
},
{
"type": "symbol",
"from": {"data": "dis"},
"encode": {
"enter": {
"shape": {"value": "circle"},
"x": {"scale": "xscale", "field": "x"},
"y": {"scale": "yscale", "field": "y"},
"width": {"value": 5},
"tooltip": {"signal": "datum.y"},
}
}
} }
] ]
}; };
let SquiggleVegaChart = createClassFromSpec({'spec': specification}); let SquiggleVegaChart = createClassFromSpec({'spec': specification});
function zip<T>(a: Array<T>, b: Array<T>): Array<Array<T>>{
return a.map(function(e, i) {
return [e, b[i]];
})
}
function zip3<T>(a: Array<T>, b: Array<T>, c: Array<T>): Array<Array<T>>{
return a.map(function(e, i) {
return [e, b[i], c[i]];
})
}
/** /**
* Primary UI component for user interaction * Primary UI component for user interaction
*/ */
@ -106,11 +130,11 @@ export const SquiggleChart = ({ squiggleString }: { squiggleString: string}) =>
return total / totalY; return total / totalY;
}) })
console.log(cdf) console.log(cdf)
let values = zip3(cdf, xyShape.xs, xyShape.ys).map(([c, x, y ]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y})); let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y ]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y}));
return ( return (
<SquiggleVegaChart <SquiggleVegaChart
data={{"table": values}} data={{"con": values}}
/> />
); );
} }
@ -122,23 +146,63 @@ export const SquiggleChart = ({ squiggleString }: { squiggleString: string}) =>
total += y; total += y;
return total / totalY; return total / totalY;
}) })
let values = zip3(cdf, xyShape.xs, xyShape.ys).map(([c, x,y]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y})); let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x,y]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y}));
return ( return (
<SquiggleVegaChart <SquiggleVegaChart
data={{"name": "table", "values": values}} data={{"dis": values}}
/> />
); );
} }
else if(shape.tag === "Mixed"){ else if(shape.tag === "Mixed"){
console.log(shape.value.integralSumCache) console.log(shape)
console.log(shape.value.integralCache) console.log(shape.value.continuous.integralSumCache)
let xyShape = shape.value.continuous.xyShape; let discreteShape = shape.value.discrete.xyShape;
let values = zip(xyShape.xs, xyShape.ys).map(([x,y]) => ({x: x, y: y})); let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b);
let discretePoints = _.zip(discreteShape.xs, discreteShape.ys);
let continuousShape = shape.value.continuous.xyShape;
let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys);
interface labeledPoint {
x: number,
y: number,
type: "discrete" | "continuous"
};
let markedDisPoints : labeledPoint[] = discretePoints.map(([x,y]) => ({x: x, y: y, type: "discrete"}))
let markedConPoints : labeledPoint[] = continuousPoints.map(([x,y]) => ({x: x, y: y, type: "continuous"}))
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), 'x')
let totalContinuous = 1 - totalDiscrete;
let totalY = continuousShape.ys.reduce((a:number, b:number) => a + b);
let total = 0;
let cdf = sortedPoints.map((point: labeledPoint) => {
if(point.type == "discrete") {
total += point.y;
return total;
}
else if (point.type == "continuous") {
total += point.y / totalY * totalContinuous;
return total;
}
});
interface cdfLabeledPoint {
cdf: string,
x: number,
y: number,
type: "discrete" | "continuous"
}
let cdfLabeledPoint : cdfLabeledPoint[] = _.zipWith(cdf, sortedPoints, (c: number, point: labeledPoint) => ({...point, cdf: (c * 100).toFixed(2) + "%"}))
let continuousValues = cdfLabeledPoint.filter(x => x.type == "continuous")
let discreteValues = cdfLabeledPoint.filter(x => x.type == "discrete")
return ( return (
<SquiggleVegaChart <SquiggleVegaChart
data={{"name": "table", "values": values}} data={{"con": continuousValues, "dis": discreteValues}}
/> />
); );
} }

View File

@ -3085,6 +3085,11 @@
"resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" "resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
"version" "0.0.29" "version" "0.0.29"
"@types/lodash@^4.14.178":
"integrity" "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw=="
"resolved" "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz"
"version" "4.14.178"
"@types/mdast@^3.0.0": "@types/mdast@^3.0.0":
"integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==" "integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA=="
"resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz" "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz"