Diverging stacked bar chart - translating d3 to Vega

184 views
Skip to first unread message

Randy Zwitch

unread,
Jun 22, 2016, 11:29:42 AM6/22/16
to vega-js
I'm trying to replicate this d3 block:


I know how to make a horizontal, stacked, normalized bar in Vega, but I can't figure out what the best way to create the offset. What the original block author did was split the middle group in half, but I can't quite figure out how to express this in Vega. Any help would be greatly appreciated!


{
  "name": "Vega Visualization",
  "height": 450,
  "padding": "auto",
  "marks": [
    {
      "properties": {
        "enter": {
          "height": {"offset": -1,"scale": "x","band": true},
          "x2": {"field": "layout_end","scale": "y"},
          "x": {"field": "layout_start","scale": "y"},
          "width": {"scale": "x","band": true},
          "fill": {"field": "group","scale": "group"},
          "y": {"field": "x","scale": "x"}
        }
      },
      "from": {
        "data": "table_uquz4",
        "transform": [
          {
            "sortby": ["group"],
            "offset": "normalize",
            "field": "y",
            "groupby": ["x"],
            "type": "stack"
          }
        ]
      },
      "type": "rect"
    }
  ],
  "axes": [
    {
      "layer": "front",
      "properties": {"title": {"fontSize": {"value": 14}}},
      "title": "y",
      "grid": false,
      "type": "x",
      "scale": "y"
    },
    {
      "layer": "front",
      "properties": {"title": {"fontSize": {"value": 14}}},
      "title": "x",
      "grid": false,
      "type": "y",
      "scale": "x"
    }
  ],
  "data": [
    {
      "name": "table_uquz4",
      "values": [
        {"x": 1,"y2": 0,"group": 1,"y": 24},
        {"x": 2,"y2": 0,"group": 2,"y": 2},
        {"x": 3,"y2": 0,"group": 3,"y": 2},
        {"x": 4,"y2": 0,"group": 4,"y": 0},
        {"x": 5,"y2": 0,"group": 5,"y": 0},
        {"x": 6,"y2": 0,"group": 1,"y": 2},
        {"x": 7,"y2": 0,"group": 2,"y": 0},
        {"x": 8,"y2": 0,"group": 3,"y": 0},
        {"x": 1,"y2": 0,"group": 4,"y": 294},
        {"x": 2,"y2": 0,"group": 5,"y": 2},
        {"x": 3,"y2": 0,"group": 1,"y": 0},
        {"x": 4,"y2": 0,"group": 2,"y": 2},
        {"x": 5,"y2": 0,"group": 3,"y": 1},
        {"x": 6,"y2": 0,"group": 4,"y": 1},
        {"x": 7,"y2": 0,"group": 5,"y": 0},
        {"x": 8,"y2": 0,"group": 1,"y": 0},
        {"x": 1,"y2": 0,"group": 2,"y": 594},
        {"x": 2,"y2": 0,"group": 3,"y": 0},
        {"x": 3,"y2": 0,"group": 4,"y": 2},
        {"x": 4,"y2": 0,"group": 5,"y": 1},
        {"x": 5,"y2": 0,"group": 1,"y": 3},
        {"x": 6,"y2": 0,"group": 2,"y": 2},
        {"x": 7,"y2": 0,"group": 3,"y": 1},
        {"x": 8,"y2": 0,"group": 4,"y": 0},
        {"x": 1,"y2": 0,"group": 5,"y": 1927},
        {"x": 2,"y2": 0,"group": 1,"y": 7},
        {"x": 3,"y2": 0,"group": 2,"y": 4},
        {"x": 4,"y2": 0,"group": 3,"y": 7},
        {"x": 5,"y2": 0,"group": 4,"y": 16},
        {"x": 6,"y2": 0,"group": 5,"y": 9},
        {"x": 7,"y2": 0,"group": 1,"y": 4},
        {"x": 8,"y2": 0,"group": 2,"y": 0},
        {"x": 1,"y2": 0,"group": 3,"y": 376},
        {"x": 2,"y2": 0,"group": 4,"y": 0},
        {"x": 3,"y2": 0,"group": 5,"y": 2},
        {"x": 4,"y2": 0,"group": 1,"y": 6},
        {"x": 5,"y2": 0,"group": 2,"y": 4},
        {"x": 6,"y2": 0,"group": 3,"y": 3},
        {"x": 7,"y2": 0,"group": 4,"y": 0},
        {"x": 8,"y2": 0,"group": 5,"y": 2}
      ]
    },
    {
      "name": "stats",
      "source": "table_uquz4",
      "transform": [
        {
          "groupby": ["x"],
          "type": "aggregate",
          "summarize": [{"ops": ["sum"],"field": "y"}]
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "range": "height",
      "domain": {"sort": true,"data": "table_uquz4","field": "x"},
      "type": "ordinal"
    },
    {
      "reverse": false,
      "name": "y",
      "zero": true,
      "domainMax": 1,
      "domain": {"data": "stats","field": "sum_y"},
      "range": "width",
      "type": "linear",
      "round": false
    },
    {
      "name": "group",
      "range": ["#c7001e","#f6a580","#cccccc","#92c6db","#086fad"],
      "domain": {"data": "table_uquz4","field": "group"},
      "type": "ordinal"
    }
  ],
  "width": 450,
  "legends": [{"title": "Group","fill": "group"}]
}

Roy I

unread,
Jun 25, 2016, 9:11:22 AM6/25/16
to vega-js

Hi Randy,
Here is a working solution based on modifying your Vega spec. The data table values have been corrected so the result matches the bl.ock at http://bl.ocks.org/wpoely86/e285b8e4c7b84710e463



Vega spec
----------------
{
  "name": "Vega Visualization",
  "width": 450,
  "height": 450,
  "padding": "auto",
  
   "data": [
    {
      "name": "table_uquz4",
      "values": [
        {"x": 1,"group": 1,"y": 24},
{"x": 1,"group": 2,"y": 294},
{"x": 1,"group": 3,"y": 594},
{"x": 1,"group": 4,"y": 1927},
{"x": 1,"group": 5,"y": 376},
{"x": 2,"group": 1,"y": 2},
{"x": 2,"group": 2,"y": 2},
{"x": 2,"group": 3,"y": 0},
{"x": 2,"group": 4,"y": 7},
{"x": 2,"group": 5,"y": 0},
{"x": 3,"group": 1,"y": 2},
{"x": 3,"group": 2,"y": 0},
{"x": 3,"group": 3,"y": 2},
{"x": 3,"group": 4,"y": 4},
{"x": 3,"group": 5,"y": 2},
{"x": 4,"group": 1,"y": 0},
{"x": 4,"group": 2,"y": 2},
{"x": 4,"group": 3,"y": 1},
{"x": 4,"group": 4,"y": 7},
{"x": 4,"group": 5,"y": 6},
{"x": 5,"group": 1,"y": 0},
{"x": 5,"group": 2,"y": 1},
{"x": 5,"group": 3,"y": 3},
{"x": 5,"group": 4,"y": 16},
{"x": 5,"group": 5,"y": 4},
{"x": 6,"group": 1,"y": 1},
{"x": 6,"group": 2,"y": 1},
{"x": 6,"group": 3,"y": 2},
{"x": 6,"group": 4,"y": 9},
{"x": 6,"group": 5,"y": 3},
        {"x": 7,"group": 1,"y": 0},
{"x": 7,"group": 2,"y": 0},
{"x": 7,"group": 3,"y": 1},
{"x": 7,"group": 4,"y": 4},
{"x": 7,"group": 5,"y": 0},
{"x": 8,"group": 1,"y": 0},
{"x": 8,"group": 2,"y": 0},
{"x": 8,"group": 3,"y": 0},
{"x": 8,"group": 4,"y": 0},
{"x": 8,"group": 5,"y": 2}
      ]
    },
{
      "name": "bars",
      "source": "table_uquz4",
      "transform": [
          {
            "sortby": ["group"],
            "offset": "normalize",
            "field": "y",
            "groupby": ["x"],
            "type": "stack"
          }
 ]
},
{
      "name": "bars_offset",
      "source": "bars",
      "transform": [
{
"type": "filter",
"test": "datum.group == 3"
}
]
},

{
      "name": "bars_adjusted",
      "source": "bars",
      "transform": [
{
"type": "lookup",
"on": "bars_offset",
"onKey": "x",
"keys": ["x"],
"as": ["obj"],
"default": 0
},
 
        {
            "type": "formula",
"field": "bar_start",
"expr": "datum.layout_start - datum.obj.layout_mid"
        },
{
            "type": "formula",
"field": "bar_end",
"expr": "datum.layout_end - datum.obj.layout_mid"
        }
 ]
}
  ],
  
    "scales": [
    {
      "name": "x",
      "range": "height",
      "domain": {"sort": true,"data": "table_uquz4","field": "x"},
      "type": "ordinal"
    },
    {
      "name": "y",
      "domain": {"data": "bars_adjusted", "field": ["bar_start", "bar_end"] },
      "range": "width",
      "type": "linear",
 "nice": true
    },
    {
      "name": "group",
      "range": ["#c7001e", "#f6a580", "#cccccc", "#92c6db", "#086fad"],
      "domain": {"data": "table_uquz4", "field": "group"},
      "type": "ordinal"
    }
  ],
  
    "axes": [
    {
      "layer": "front",
      "properties": {"title": {"fontSize": {"value": 14}}},
      "title": "y",
      "grid": false,
      "type": "x",
      "scale": "y"
    },
    {
      "layer": "front",
      "properties": {"title": {"fontSize": {"value": 14}}},
      "title": "x",
      "grid": false,
      "type": "y",
      "scale": "x"
    }
  ],
  
  "marks": [
    {
"type": "rect",
"from": {"data": "bars_adjusted"},
"properties": {
"enter": {
"y": {"field": "x", "scale": "x"},
"height": {"offset": -1, "scale": "x", "band": true},
 
"x": {"field": "bar_start", "scale": "y"},
"x2": {"field": "bar_end", "scale": "y"},

"fill": {"field": "group", "scale": "group"}
        }
      }
},
 
{
"type": "rule",
"properties": {
"enter":{
"x": {"scale": "y", "value": 0.0},
"y": {"value": 0},
"y2": {"value": 450},
"strokeWidth": {"value": 1},
"stroke": {"value": "grey"},
"strokeOpacity": {"value": 1.0}
}
}
}
  ],

Randy Zwitch

unread,
Jun 25, 2016, 6:57:47 PM6/25/16
to vega-js

Thank you so much Roy! Your transforms seem so simple in retrospect; I was thinking I needed a single complicated formula, but your method makes perfect sense once you work from the normalized values.

Reply all
Reply to author
Forward
0 new messages