Framework
1.13.0 GitHub️ 2.5k

Mosaic vgplot

Mosaic is a system for linking data visualizations, tables, and inputs, leveraging DuckDB for scalable processing. Mosaic includes an interactive grammar of graphics, Mosaic vgplot, built on Observable Plot. With vgplot, you can interactively visualize and explore millions — even billions — of data points.

The example below shows the pickup and dropoff locations of one million taxi rides in New York City from Jan 1–3, 2010. The dataset is stored in a 8MB Apache Parquet file, generated with a data loader.

The views above are coordinated: brushing a time window in the histogram, or a region in either map, will filter both maps. What spatial patterns can you find?

Mosaic vgplot is available by default in Markdown as vg and is backed by the default DuckDB client that is configured using SQL front matter. If you would prefer to initialize Mosaic yourself, you can do something like:

import {DuckDBClient} from "npm:@observablehq/duckdb";
import * as vgplot from "npm:@uwdata/vgplot";

const db = await DuckDBClient.of({trips: FileAttachment("nyc-taxi.parquet")});
const coordinator = new vgplot.Coordinator();
coordinator.databaseConnector(vgplot.wasmConnector({duckdb: db._db}));
const vg = vgplot.createAPIContext({coordinator});

The code below creates three views, coordinated by Mosaic’s crossfilter helper.

// Create a shared filter
const $filter = vg.Selection.crossfilter();

// Shared attributes for the maps
const attributes = [
  vg.width(315),
  vg.height(550),
  vg.margin(0),
  vg.xAxis(null),
  vg.yAxis(null),
  vg.xDomain([297000, 297000 + 28.36 * 315]),
  vg.yDomain([57900, 57900 + 28.36 * 550]), // ensure aspect ratio of 1
  vg.colorScale("symlog")
];

// Create two side-by-side maps
const maps = vg.hconcat(
  vg.plot(
    vg.raster(vg.from("trips", {filterBy: $filter}), {x: "px", y: "py", imageRendering: "pixelated"}),
    vg.intervalXY({as: $filter}),
    vg.text([{label: "Taxi pickups"}], {
      dx: 10,
      dy: 10,
      text: "label",
      fill: "white",
      frameAnchor: "top-left"
    }),
    ...attributes,
    vg.colorScheme("turbo"),
    vg.frame({stroke: "black"})
  ),
  vg.hspace(10),
  vg.plot(
    vg.raster(vg.from("trips", {filterBy: $filter}), {x: "dx", y: "dy", imageRendering: "pixelated"}),
    vg.intervalXY({as: $filter}),
    vg.text([{label: "Taxi dropoffs"}], {
      dx: 10,
      dy: 10,
      text: "label",
      fill: "white",
      frameAnchor: "top-left"
    }),
    ...attributes,
    vg.colorScheme("turbo"),
    vg.frame({stroke: "black"})
  )
);

// Create the histogram
const histogram = vg.plot(
  vg.rectY(vg.from("trips"), {x: vg.bin("time"), y: vg.count(), insetLeft: 0.5, insetRight: 0.5}),
  vg.intervalX({as: $filter}),
  vg.yTickFormat("s"),
  vg.xLabel("Hour of pickup"),
  vg.yLabel("Number of rides"),
  vg.width(640),
  vg.height(100)
);

For more Mosaic examples, see the Mosaic + Framework website.