Quantile Dot Plot Example

A quantile dot plot represents a probability distribution by taking a uniform sample of quantile values and plotting them in a dot plot. It visualizes a representative set of possible outcomes of a random process, and provides a discrete alternative to probability density and violin plots in which finding probability intervals reduces to counting dots in the display.

The plot below visualizes quantiles for a log-normal distribution that models hypothetical bus arrival times (in minutes from the current time), following the example of Kay, Kola, Hullman, & Munson, 2016. If we are willing to miss a bus 2 out of 20 times, given 20 quantiles we can count up 2 dots from the left to get the time we should arrive at the bus stop.

Click or drag on the chart to explore risk thresholds for arriving at the bus stop. Double-click to remove the threshold.

Vega JSON Specification <>

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A quantile dot plot conveying the uncertainty of bus arrival times.",
  "width": 400,
  "height": 90,
  "padding": 5,

  "signals": [
    {
      "name": "quantiles", "value": 20,
      "bind": {"input": "range", "min": 10, "max": 200, "step": 1}
    },
    {"name": "mean", "update": "log(11.4)"},
    {"name": "sd", "value": 0.2},
    {"name": "step", "update": "1.25 * sqrt(20 / quantiles)"},
    {"name": "size", "update": "scale('x', step) - scale('x', 0)"},
    {"name": "area", "update": "size * size"},
    {
      "name": "select", "init": "quantileLogNormal(0.05, mean, sd)",
      "on": [
        {
          "events": "click, [pointerdown, window:pointerup] > pointermove",
          "update": "clamp(invert('x', x()), 0.0001, 30)"
        },
        {
          "events": "dblclick",
          "update": "0"
        }
      ]
    }
  ],

  "data": [
    {
      "name": "quantiles",
      "transform": [
        {
          "type": "sequence", "as": "p",
          "start": {"signal": "0.5 / quantiles"},
          "step": {"signal": "1 / quantiles"},
          "stop": 1
        },
        {
          "type": "formula", "as": "value",
          "expr": "quantileLogNormal(datum.p, mean, sd)"
        },
        {
          "type": "dotbin",
          "field": "value",
          "step": {"signal": "step"}
        },
        {
          "type": "stack",
          "groupby": ["bin"]
        },
        {
          "type": "extent",
          "field": "y1",
          "signal": "ext"
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "x",
      "domain": [0, 30],
      "range": "width"
    },
    {
      "name": "y",
      "domain": {"signal": "[0, height / size]"},
      "range": "height"
    }
  ],

  "axes": [
    {"scale": "x", "orient": "bottom"}
  ],

  "marks": [
    {
      "type": "symbol",
      "from": {"data": "quantiles"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "bin"},
          "y": {"scale": "y", "signal": "datum.y0 + 0.5"},
          "size": {"signal": "area"}
        },
        "update": {
          "fill": {"signal": "datum.bin < select ? 'firebrick' : 'steelblue'"}
        }
      }
    },
    {
      "type": "rule",
      "interactive": false,
      "encode": {
        "update": {
          "x": {"scale": "x", "signal": "select"},
          "y": {"value": 0},
          "y2": {"signal": "height"},
          "stroke": {"signal": "select ? '#ccc': 'transparent'"}
        }
      }
    },
    {
      "type": "text",
      "interactive": false,
      "encode": {
        "enter": {
          "baseline": {"value": "top"},
          "dx": {"value": 3},
          "y": {"value": 2}
        },
        "update": {
          "x": {"scale": "x", "signal": "select"},
          "text": {"signal": "format(cumulativeLogNormal(select, mean, sd), '.1%')"},
          "fill": {"signal": "select ? '#000': 'transparent'"}
        }
      }
    }
  ]
}