diff --git a/expr.ts b/expr.ts
index 0d08113..e0f4aa7 100644
--- a/expr.ts
+++ b/expr.ts
@@ -1,9 +1,9 @@
-import pl from "npm:nodejs-polars"
+import pl from "npm:nodejs-polars";
export function residuals(x: pl.Expr, y: pl.Expr): pl.Expr {
- const xM = x.minus(x.mean())
- const yM = y.minus(y.mean())
- const xMSQ = xM.pow(2)
- const beta = xM.dot(yM).div(xMSQ.sum())
- return yM.minus(beta.mul(xM))
-}
\ No newline at end of file
+ const xM = x.minus(x.mean());
+ const yM = y.minus(y.mean());
+ const xMSQ = xM.pow(2);
+ const beta = xM.dot(yM).div(xMSQ.sum());
+ return yM.minus(beta.mul(xM));
+}
diff --git a/plots.ts b/plots.ts
index 6d2e8d7..aeeaec3 100644
--- a/plots.ts
+++ b/plots.ts
@@ -1,28 +1,35 @@
import * as _Plot from "npm:@observablehq/plot";
import { DOMParser, SVGElement } from "npm:linkedom";
+import * as _vega from "npm:vega";
+import * as _lite from "npm:vega-lite";
const defaultPlotSettings = {
- grid: true,
- margin: 50,
- style: {
- backgroundColor: "#fff"
- }
-}
+ grid: true,
+ margin: 50,
+ style: {
+ backgroundColor: "#fff",
+ },
+};
/**
* Configure default plot settings
- * @param options
+ * @param options
*/
export function configurePlots(options: any) {
- Object.assign(defaultPlotSettings, options)
+ Object.assign(defaultPlotSettings, options);
}
-export const document = new DOMParser().parseFromString(``, "text/html");
+export const document = new DOMParser().parseFromString(
+ ``,
+ "text/html",
+);
export const Plot = _Plot;
+export const vega = _vega;
+export const vegalite = _lite;
/**
- * Draw side-by-side plots
+ * Draw side-by-side plots
* Example:
* ```ts
* const plt = sideBySidePlot({
@@ -39,34 +46,168 @@ export const Plot = _Plot;
* @param marks List of plot callbacks
* @param cols Number of columns
*/
-export function sideBySidePlot(opts: {
- x: string[],
- y: string[],
- marks: any[]
- cols: number,
- options,
+export function sideBySidePlot(opts: {
+ x: string[];
+ y: string[];
+ marks: any[];
+ cols: number;
+ options;
}) {
- const imgTags: string[] = []
- for (const xTarget of opts.x) {
- for (const yTarget of opts.y) {
- const plt = Plot.plot({
- ...(opts.options ?? defaultPlotSettings),
- marks: opts.marks.map(fn => fn(xTarget, yTarget)),
- document
- })
- plt.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
- const svgUrl = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(plt)))}`
- imgTags.push(``)
- }
+ const imgTags: string[] = [];
+ for (const xTarget of opts.x) {
+ for (const yTarget of opts.y) {
+ const plt = Plot.plot({
+ ...(opts.options ?? defaultPlotSettings),
+ marks: opts.marks.map((fn) => fn(xTarget, yTarget)),
+ document,
+ });
+ plt.setAttribute("xmlns", "http://www.w3.org/2000/svg");
+ const svgUrl = `data:image/svg+xml;base64,${
+ btoa(unescape(encodeURIComponent(plt)))
+ }`;
+ imgTags.push(``);
}
- const output = `
-
- ${imgTags.join('')}
+ }
+ const output = `
+
- `
- return {
- [Symbol.for("Jupyter.display")]: () => ({
- "text/html": output
- })
- }
-}
\ No newline at end of file
+ `;
+ return {
+ [Symbol.for("Jupyter.display")]: () => ({
+ "text/html": output,
+ }),
+ };
+}
+
+/**
+ * Histogram plot
+ *
+ * @param data
+ * @param x
+ * @param opts
+ * @returns Plot
+ */
+export function histPlot(
+ data: any[],
+ x = "column",
+ opts = { options: null, fn: "proportion" },
+) {
+ return Plot.plot({
+ ...(opts.options ?? defaultPlotSettings),
+ y: { grid: true },
+ marks: [
+ Plot.ruleY([0]),
+ Plot.ruleX([0]),
+ Plot.rectY(data, Plot.binX({ y: opts.fn ?? "count" }, { x: x })),
+ ],
+ document,
+ });
+}
+
+export function oneBoxPlot(
+ data: any[],
+ y = "column",
+ opts = { options: null, box: null },
+) {
+ return Plot.plot({
+ ...(opts.options ?? defaultPlotSettings),
+ y: { grid: true },
+ marks: [
+ Plot.ruleY([0]),
+ Plot.boxY(data, { y, ...(opts.box ?? {}) }),
+ ],
+ document,
+ });
+}
+
+export async function quantilePlotSVG(
+ data: any[],
+ x = "column",
+ opts = { width: 500 },
+) {
+ const spec = {
+ data: { values: data },
+ width: opts.width,
+ "transform": [
+ {
+ "quantile": "price",
+ "as": ["prob", "value"],
+ },
+ {
+ "calculate": "quantileNormal(datum.prob)",
+ "as": "norm",
+ },
+ ],
+ "layer": [
+ {
+ "mark": { type: "circle", size: 80 },
+ "encoding": {
+ "x": {
+ "field": "norm",
+ "type": "quantitative",
+ "title": "Theoretical Quantiles→",
+ },
+ "y": {
+ "field": "value",
+ "type": "quantitative",
+ "title": "Ordered Values→",
+ },
+ },
+ },
+ {
+ mark: { type: "line", color: "red" },
+ transform: [
+ {
+ "regression": "value",
+ "on": "norm",
+ },
+ ],
+ "encoding": {
+ "x": { "field": "norm", "type": "quantitative" },
+ "y": { "field": "value", "type": "quantitative" },
+ },
+ },
+ ],
+ };
+ let vegaspec = vegalite.compile(spec).spec;
+ var view = new vega.View(vega.parse(vegaspec), { renderer: "none" });
+
+ return await view.toSVG();
+}
+
+export function quantilePlot(data: any[], x = "column", opts = { width: 500 }) {
+ return {
+ [Symbol.for("Jupyter.display")]: async () => ({
+ "text/html": await quantilePlotSVG(data, x, opts),
+ }),
+ };
+}
+
+const svgDataUrl = (plt) =>
+ `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(plt)))}`;
+
+export function threeChart(data: any[], x = "column", opts = { width: 800 }) {
+ const hist = histPlot(data, x);
+ hist.setAttribute("xmlns", "http://www.w3.org/2000/svg");
+ const box = oneBoxPlot(data, x);
+ box.setAttribute("xmlns", "http://www.w3.org/2000/svg");
+ const qq = quantilePlotSVG(data, x);
+ return {
+ [Symbol.for("Jupyter.display")]: async () => ({
+ "text/html": `
+
+ `,
+ }),
+ };
+}