2022-06-24 07:22:31 +00:00
|
|
|
import { VisualizationSpec } from "react-vega";
|
|
|
|
import type { LogScale, LinearScale, PowScale } from "vega";
|
|
|
|
|
|
|
|
export type DistributionChartSpecOptions = {
|
|
|
|
/** Set the x scale to be logarithmic by deault */
|
|
|
|
logX: boolean;
|
|
|
|
/** Set the y scale to be exponential by deault */
|
|
|
|
expY: boolean;
|
|
|
|
/** The minimum x coordinate shown on the chart */
|
|
|
|
minX?: number;
|
|
|
|
/** The maximum x coordinate shown on the chart */
|
|
|
|
maxX?: number;
|
|
|
|
/** The title of the chart */
|
|
|
|
title?: string;
|
|
|
|
/** The formatting of the ticks */
|
|
|
|
format?: string;
|
|
|
|
};
|
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
export const linearXScale: LinearScale = {
|
2022-06-24 07:22:31 +00:00
|
|
|
name: "xscale",
|
|
|
|
clamp: true,
|
|
|
|
type: "linear",
|
|
|
|
range: "width",
|
|
|
|
zero: false,
|
|
|
|
nice: false,
|
2022-07-13 04:15:07 +00:00
|
|
|
domain: { data: "domain", field: "x" },
|
2022-06-24 07:22:31 +00:00
|
|
|
};
|
2022-08-23 15:04:35 +00:00
|
|
|
export const linearYScale: LinearScale = {
|
2022-06-24 07:22:31 +00:00
|
|
|
name: "yscale",
|
|
|
|
type: "linear",
|
|
|
|
range: "height",
|
2022-07-16 03:29:18 +00:00
|
|
|
zero: true,
|
2022-07-13 04:15:07 +00:00
|
|
|
domain: { data: "domain", field: "y" },
|
2022-06-24 07:22:31 +00:00
|
|
|
};
|
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
export const logXScale: LogScale = {
|
2022-06-24 07:22:31 +00:00
|
|
|
name: "xscale",
|
|
|
|
type: "log",
|
|
|
|
range: "width",
|
|
|
|
zero: false,
|
|
|
|
base: 10,
|
|
|
|
nice: false,
|
|
|
|
clamp: true,
|
2022-07-13 04:15:07 +00:00
|
|
|
domain: { data: "domain", field: "x" },
|
2022-06-24 07:22:31 +00:00
|
|
|
};
|
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
export const expYScale: PowScale = {
|
2022-06-24 07:22:31 +00:00
|
|
|
name: "yscale",
|
|
|
|
type: "pow",
|
|
|
|
exponent: 0.1,
|
|
|
|
range: "height",
|
2022-07-16 03:29:18 +00:00
|
|
|
zero: true,
|
2022-06-24 07:22:31 +00:00
|
|
|
nice: false,
|
2022-07-13 04:15:07 +00:00
|
|
|
domain: { data: "domain", field: "y" },
|
2022-06-24 07:22:31 +00:00
|
|
|
};
|
|
|
|
|
2022-07-22 19:24:49 +00:00
|
|
|
export const defaultTickFormat = ".9~s";
|
|
|
|
|
2022-06-24 07:22:31 +00:00
|
|
|
export function buildVegaSpec(
|
|
|
|
specOptions: DistributionChartSpecOptions
|
|
|
|
): VisualizationSpec {
|
2022-07-13 04:15:07 +00:00
|
|
|
const {
|
2022-07-22 19:24:49 +00:00
|
|
|
format = defaultTickFormat,
|
2022-06-24 07:22:31 +00:00
|
|
|
title,
|
|
|
|
minX,
|
|
|
|
maxX,
|
|
|
|
logX,
|
|
|
|
expY,
|
|
|
|
} = specOptions;
|
|
|
|
|
|
|
|
let xScale = logX ? logXScale : linearXScale;
|
2022-07-11 01:22:21 +00:00
|
|
|
if (minX !== undefined && Number.isFinite(minX)) {
|
2022-06-24 07:22:31 +00:00
|
|
|
xScale = { ...xScale, domainMin: minX };
|
|
|
|
}
|
|
|
|
|
2022-07-11 01:22:21 +00:00
|
|
|
if (maxX !== undefined && Number.isFinite(maxX)) {
|
2022-06-24 07:22:31 +00:00
|
|
|
xScale = { ...xScale, domainMax: maxX };
|
|
|
|
}
|
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
const spec: VisualizationSpec = {
|
2022-06-24 07:22:31 +00:00
|
|
|
$schema: "https://vega.github.io/schema/vega/v5.json",
|
2022-08-19 11:17:41 +00:00
|
|
|
description: "Squiggle plot chart",
|
2022-06-24 07:22:31 +00:00
|
|
|
width: 500,
|
|
|
|
height: 100,
|
|
|
|
padding: 5,
|
|
|
|
data: [
|
2022-08-23 14:05:02 +00:00
|
|
|
{name: "data",},
|
|
|
|
{ name: "domain",},
|
2022-06-24 07:22:31 +00:00
|
|
|
],
|
2022-08-22 15:00:28 +00:00
|
|
|
signals: [
|
|
|
|
{
|
|
|
|
"name": "hover",
|
|
|
|
"value": null,
|
|
|
|
"on": [
|
2022-08-23 14:05:02 +00:00
|
|
|
{"events": "mouseover", "update": "datum"},
|
|
|
|
{"events": "mouseout", "update": "null"}
|
2022-08-22 15:00:28 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
2022-08-23 14:05:02 +00:00
|
|
|
"name": "position",
|
|
|
|
"value": "[0, 0]",
|
|
|
|
"on": [
|
2022-08-23 15:04:35 +00:00
|
|
|
{ "events": "mousemove", "update": "xy() "},
|
2022-08-23 14:05:02 +00:00
|
|
|
{ "events": "mouseout", "update": "null"},
|
|
|
|
]
|
|
|
|
},
|
2022-08-23 15:04:35 +00:00
|
|
|
{
|
|
|
|
"name": "position_scaled",
|
|
|
|
"value": 0,
|
|
|
|
"update": "position ? invert('xscale', position[0]) : null"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "announcer",
|
|
|
|
"value": "",
|
|
|
|
"update": "hover ? 'Value: ' + hover.x : ''"
|
|
|
|
},
|
2022-08-23 14:05:02 +00:00
|
|
|
|
|
|
|
|
2022-08-22 15:00:28 +00:00
|
|
|
],
|
2022-07-12 07:09:24 +00:00
|
|
|
scales: [
|
|
|
|
xScale,
|
|
|
|
expY ? expYScale : linearYScale,
|
|
|
|
{
|
|
|
|
name: "color",
|
|
|
|
type: "ordinal",
|
|
|
|
domain: {
|
2022-07-13 04:15:07 +00:00
|
|
|
data: "data",
|
|
|
|
field: "name",
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
2022-08-19 17:14:08 +00:00
|
|
|
range: { scheme: "blues" },
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
|
|
|
],
|
2022-06-24 07:22:31 +00:00
|
|
|
axes: [
|
|
|
|
{
|
|
|
|
orient: "bottom",
|
|
|
|
scale: "xscale",
|
|
|
|
labelColor: "#727d93",
|
|
|
|
tickColor: "#fff",
|
|
|
|
tickOpacity: 0.0,
|
|
|
|
domainColor: "#fff",
|
|
|
|
domainOpacity: 0.0,
|
|
|
|
format: format,
|
|
|
|
tickCount: 10,
|
2022-08-19 17:14:08 +00:00
|
|
|
labelOverlap: "greedy",
|
2022-06-24 07:22:31 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
marks: [
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-06-24 07:22:31 +00:00
|
|
|
{
|
2022-07-13 04:15:07 +00:00
|
|
|
name: "all_distributions",
|
2022-07-12 07:09:24 +00:00
|
|
|
type: "group",
|
2022-06-24 07:22:31 +00:00
|
|
|
from: {
|
2022-07-12 07:09:24 +00:00
|
|
|
facet: {
|
2022-07-13 04:15:07 +00:00
|
|
|
name: "distribution_facet",
|
|
|
|
data: "data",
|
2022-07-12 07:09:24 +00:00
|
|
|
groupby: ["name"],
|
|
|
|
},
|
2022-06-24 07:22:31 +00:00
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
marks: [
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-07-12 07:09:24 +00:00
|
|
|
{
|
2022-07-13 04:15:07 +00:00
|
|
|
name: "continuous_distribution",
|
|
|
|
type: "group",
|
2022-07-12 07:09:24 +00:00
|
|
|
from: {
|
2022-07-13 04:15:07 +00:00
|
|
|
facet: {
|
|
|
|
name: "continuous_facet",
|
|
|
|
data: "distribution_facet",
|
|
|
|
field: "continuous",
|
|
|
|
},
|
2022-06-24 07:22:31 +00:00
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
encode: {
|
2022-07-13 04:15:07 +00:00
|
|
|
update: {},
|
|
|
|
},
|
|
|
|
marks: [
|
|
|
|
{
|
|
|
|
name: "continuous_area",
|
|
|
|
type: "area",
|
|
|
|
from: {
|
|
|
|
data: "continuous_facet",
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
encode: {
|
|
|
|
update: {
|
|
|
|
interpolate: { value: "linear" },
|
|
|
|
x: {
|
|
|
|
scale: "xscale",
|
|
|
|
field: "x",
|
|
|
|
},
|
|
|
|
y: {
|
|
|
|
scale: "yscale",
|
|
|
|
field: "y",
|
|
|
|
},
|
|
|
|
fill: {
|
|
|
|
scale: "color",
|
|
|
|
field: { parent: "name" },
|
|
|
|
},
|
|
|
|
y2: {
|
|
|
|
scale: "yscale",
|
|
|
|
value: 0,
|
|
|
|
},
|
|
|
|
fillOpacity: {
|
|
|
|
value: 1,
|
|
|
|
},
|
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "discrete_distribution",
|
|
|
|
type: "group",
|
|
|
|
from: {
|
|
|
|
facet: {
|
|
|
|
name: "discrete_facet",
|
|
|
|
data: "distribution_facet",
|
|
|
|
field: "discrete",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
marks: [
|
2022-08-23 14:05:02 +00:00
|
|
|
{
|
|
|
|
"name": "samples",
|
|
|
|
"type": "rect",
|
|
|
|
"from": {"data": "discrete_facet"},
|
|
|
|
"encode": {
|
|
|
|
"enter": {
|
|
|
|
"x": {"scale": "xscale", "field":"x"},
|
|
|
|
"width": {"value": 1},
|
|
|
|
|
|
|
|
"y": {"value": 25, "offset": {"signal": "height"}},
|
|
|
|
"height": {"value": 5},
|
|
|
|
"fill": {"value": "steelblue"},
|
|
|
|
"fillOpacity": {"value": 0.8}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
{
|
|
|
|
type: "rect",
|
|
|
|
from: {
|
|
|
|
data: "discrete_facet",
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
encode: {
|
|
|
|
enter: {
|
|
|
|
width: {
|
|
|
|
value: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
update: {
|
|
|
|
x: {
|
|
|
|
scale: "xscale",
|
|
|
|
field: "x",
|
|
|
|
},
|
|
|
|
y: {
|
|
|
|
scale: "yscale",
|
|
|
|
field: "y",
|
|
|
|
},
|
|
|
|
y2: {
|
|
|
|
scale: "yscale",
|
|
|
|
value: 0,
|
|
|
|
},
|
2022-07-13 05:32:28 +00:00
|
|
|
fill: {
|
|
|
|
scale: "color",
|
|
|
|
field: { parent: "name" },
|
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: "symbol",
|
|
|
|
from: {
|
|
|
|
data: "discrete_facet",
|
|
|
|
},
|
|
|
|
encode: {
|
|
|
|
enter: {
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-07-13 04:15:07 +00:00
|
|
|
shape: {
|
|
|
|
value: "circle",
|
|
|
|
},
|
|
|
|
size: [{ value: 100 }],
|
|
|
|
tooltip: {
|
2022-08-11 11:12:59 +00:00
|
|
|
signal: "{ probability: datum.y, value: datum.x }",
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
update: {
|
|
|
|
x: {
|
|
|
|
scale: "xscale",
|
|
|
|
field: "x",
|
2022-08-22 15:00:28 +00:00
|
|
|
offset: 0.5, // if this is not included, the circles are slightly left of center.
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
|
|
|
y: {
|
|
|
|
scale: "yscale",
|
|
|
|
field: "y",
|
|
|
|
},
|
2022-07-13 05:32:28 +00:00
|
|
|
fill: {
|
|
|
|
scale: "color",
|
|
|
|
field: { parent: "name" },
|
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
},
|
|
|
|
},
|
2022-07-13 04:15:07 +00:00
|
|
|
],
|
2022-06-24 07:22:31 +00:00
|
|
|
},
|
2022-07-12 07:09:24 +00:00
|
|
|
],
|
2022-06-24 07:22:31 +00:00
|
|
|
},
|
2022-08-22 15:00:28 +00:00
|
|
|
{
|
|
|
|
"type": "text",
|
|
|
|
"interactive": false,
|
|
|
|
"encode": {
|
|
|
|
"enter": {
|
|
|
|
"x": {"signal": "width", "offset": 1},
|
|
|
|
"fill": {"value": "black"},
|
|
|
|
"fontSize": {"value": 20},
|
|
|
|
"align": {"value": "right"}
|
|
|
|
},
|
|
|
|
"update": {
|
2022-08-24 16:57:35 +00:00
|
|
|
"text": {"signal": "position_scaled ? format(position_scaled, ',.4r') : ''", }
|
2022-08-22 15:00:28 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-23 14:05:02 +00:00
|
|
|
},
|
2022-08-23 15:04:35 +00:00
|
|
|
{
|
|
|
|
"type": "rule",
|
|
|
|
"encode": {
|
|
|
|
"enter": {
|
|
|
|
x: {value: 0},
|
|
|
|
"y": {"scale": "yscale", "value":0},
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
y2: {
|
|
|
|
signal: "height",
|
|
|
|
offset: 2
|
|
|
|
},
|
|
|
|
"strokeDash": {"value": [5, 5]},
|
|
|
|
},
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
"update": {
|
|
|
|
"x": {"signal": "position ? position[0] < 0 ? null : position[0] > width ? null : position[0]: null"},
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
"opacity": {"signal": "position ? 1 : 0"}
|
|
|
|
},
|
|
|
|
}
|
2022-08-23 14:05:02 +00:00
|
|
|
|
2022-08-23 15:04:35 +00:00
|
|
|
}
|
2022-06-24 07:22:31 +00:00
|
|
|
],
|
2022-08-19 17:14:08 +00:00
|
|
|
legends: [
|
|
|
|
{
|
|
|
|
fill: "color",
|
|
|
|
orient: "top",
|
|
|
|
labelFontSize: 12,
|
|
|
|
encode: {
|
|
|
|
symbols: {
|
|
|
|
update: {
|
|
|
|
fill: [
|
|
|
|
{ test: "length(domain('color')) == 1", value: "transparent" },
|
|
|
|
{ scale: "color", field: "value" },
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
labels: {
|
|
|
|
interactive: true,
|
|
|
|
update: {
|
|
|
|
fill: [
|
|
|
|
{ test: "length(domain('color')) == 1", value: "transparent" },
|
|
|
|
{ value: "black" },
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
...(title && {
|
2022-06-24 07:22:31 +00:00
|
|
|
title: {
|
|
|
|
text: title,
|
|
|
|
},
|
2022-08-19 17:14:08 +00:00
|
|
|
}),
|
|
|
|
};
|
2022-06-24 07:22:31 +00:00
|
|
|
|
|
|
|
return spec;
|
|
|
|
}
|