Beeswarm Plot Example

A beeswarm plot conveys the size of a group of items by visually clustering the each individual data point. This example uses Vega’s force transform to calculate the clustered layout. The example uses non-standard xfocus and yfocus encoding channels to create anchor coordinates that parameterize the x and y forces.

Vega JSON Specification <>

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A beeswarm chart example that uses a force-directed layout to group items by category.",
  "width": 800,
  "height": 100,
  "padding": {"left": 5, "right": 5, "top": 0, "bottom": 20},
  "autosize": "none",

  "signals": [
    { "name": "cx", "update": "width / 2" },
    { "name": "cy", "update": "height / 2" },
    { "name": "radius", "value": 8, "bind": {"input": "range", "min": 2, "max": 15, "step": 1} },
    { "name": "collide", "value": 1, "bind": {"input": "range", "min": 1, "max": 10, "step": 1} },
    { "name": "gravityX", "value": 0.2, "bind": {"input": "range", "min": 0, "max": 1} },
    { "name": "gravityY", "value": 0.1, "bind": {"input": "range", "min": 0, "max": 1} },
    { "name": "static", "value": true, "bind": {"input": "checkbox"} }
  ],

  "data": [
    {
      "name": "people",
      "url": "data/miserables.json",
      "format": {"type": "json", "property": "nodes"}
    }
  ],

  "scales": [
    {
      "name": "xscale",
      "type": "band",
      "domain": {
        "data": "people",
        "field": "group",
        "sort": true
      },
      "range": "width"
    },
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "people", "field": "group"},
      "range": {"scheme": "category20c"}
    }
  ],

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

  "marks": [
    {
      "name": "nodes",
      "type": "symbol",
      "from": {"data": "people"},
      "encode": {
        "enter": {
          "fill": {"scale": "color", "field": "group"},
          "xfocus": {"scale": "xscale", "field": "group", "band": 0.5},
          "yfocus": {"signal": "cy"}
        },
        "update": {
          "size": {"signal": "pow(2 * radius, 2)"},
          "stroke": {"value": "white"},
          "strokeWidth": {"value": 1},
          "zindex": {"value": 0}
        },
        "hover": {
          "stroke": {"value": "purple"},
          "strokeWidth": {"value": 3},
          "zindex": {"value": 1}
        }
      },
      "transform": [
        {
          "type": "force",
          "iterations": 300,
          "static": {"signal": "static"},
          "forces": [
            {"force": "collide", "iterations": {"signal": "collide"}, "radius": {"signal": "radius"}},
            {"force": "x", "x": "xfocus", "strength": {"signal": "gravityX"}},
            {"force": "y", "y": "yfocus", "strength": {"signal": "gravityY"}}
          ]
        }
      ]
    }
  ]
}