vega / vega-lite

A concise grammar of interactive graphics, built on Vega.
https://vega.github.io/vega-lite/
BSD 3-Clause "New" or "Revised" License
4.62k stars 598 forks source link

Allow specifying specific order of stack in stacked charts #1734

Open kanitw opened 7 years ago

kanitw commented 7 years ago

We currently do not have a way to specify manual order of stacking group (e.g., color or detail channel) in stacked charts.

Note: As seen in https://github.com/vega/vega-lite/issues/1732#issuecomment-263454554, users might expect that color's scale domain also affect how stack is ordered.

For example, in the following spec.

{
  "data": {
    "url": "https://raw.githubusercontent.com/dhimmel/biorxiv-licenses/4ad9659e4fc823f4c491e13be4505248df5c1ab6/figure/license-vs-time/vega-lite-data.json"
  },
  "config": {
    "mark": {
      "stacked": "normalize"
    }
  },
  "encoding": {
    "color": {
      "field": "license",
      "scale": {
        "domain": [
          "CC BY",
          "CC BY-ND",
          "CC BY-NC",
          "CC BY-NC-ND",
          "None"
        ],
        "type": "ordinal"
      },
      "type": "nominal"
    },
    "x": {
      "axis": {
        "axisWidth": 0.0,
        "format": "%Y",
        "labelAngle": 0.0,
        "tickSize": 0.0
      },
      "field": "date",
      "scale": {
        "nice": "month"
      },
      "timeUnit": "yearmonth",
      "title": " ",
      "type": "temporal"
    },
    "y": {
      "aggregate": "count",
      "axis": {
        "axisWidth": 0.0,
        "grid": false,
        "labels": false,
        "ticks": 0.0
      },
      "field": "*",
      "title": "Proportion of Preprints",
      "type": "quantitative"
    }
  },
  "mark": "area"
}

The scale domain only determines how the domain values are mapped to first 5 color in the default color palette, but does not affect order of the stacking.

vega

TODOs:

cc: @dhimmel

kanitw commented 7 years ago

Workaround

A workaround for now would be to use calculate to derive a new field and map the field to order channel. See https://vega.github.io/vega-lite/docs/stack.html#order

kanitw commented 7 years ago

Potential Syntax

One potential syntax would be to make stack grouping channel (color/opacity/size/detail) supports sort as array of values for custom ordering and then make stack order depends on the grouping channel's order too.

cc: @domoritz, @arvind

samuelcolvin commented 5 years ago

Trying to do this and still having trouble, stack transform isn't working for me.

Is there currently any way of achieving this?

kanitw commented 5 years ago

Have you tried the workaround above?

samuelcolvin commented 5 years ago

Hi, I'm afraid with a stacked area chart that gives us a (very pretty) but completely messed up chart:

visualization (1)

kanitw commented 5 years ago

Please include a specification (or link to editor) so one can help you debug

samuelcolvin commented 5 years ago

That is a link to the editor...

kanitw commented 5 years ago

Oh i just realize your image has a link. Thanks!

(Normally people put link on a text so it’s clearer as the text will be blue)

mattijn commented 5 years ago

Took me a long time to figure out that there only exist a workaround for sorting area order.

Also sorting by encoded channel, where the channel eg. color has a custom scale domain would make sense (to me).

Such as:

    "y": {
      "aggregate": "sum",
      "field": "count",
      "type": "quantitative",
      "sort": {"encoding": "color"}
    },
    "color": {
      "field": "series",
      "type": "nominal",
      "scale": {
        "domain": [
          "Construction",
          "Government",
          "Mining and Extraction",
          "Manufacturing"
        ]
      }
    }

editor-spec (it starts with an order-channel included, but upon removal I hoped the order is preserved as it is defined in the color-channel)

mattijn commented 4 years ago

I found another way of doing this:

image

using color_license_sort_index for order.

{
  "data": {
    "url": "https://raw.githubusercontent.com/dhimmel/biorxiv-licenses/4ad9659e4fc823f4c491e13be4505248df5c1ab6/figure/license-vs-time/vega-lite-data.json"
  },
  "encoding": {
    "color": {
      "field": "license",
      "type": "nominal",
      "sort": [
        "CC BY",
        "CC BY-ND",
        "CC BY-NC",
        "CC BY-NC-ND",
        "None"
      ]
    },
    "x": {
      "axis": {
        "axisWidth": 0.0,
        "format": "%Y",
        "labelAngle": 0.0,
        "tickSize": 0.0
      },
      "field": "date",
      "scale": {
        "nice": "month"
      },
      "timeUnit": "yearmonth",
      "title": "",
      "type": "temporal"
    },
    "order": {
      "field": "color_license_sort_index",
      "type": "quantitative"
    },
    "y": {
      "aggregate": "count",
      "axis": null,
      "type": "quantitative",
      "stack": "normalize"
    }
  },
  "mark": "area"
}

editor-url

marcprux commented 4 years ago

Since the order of the color's domain affect the order of the legend, I think it would make sense for it to also affect the order of the stacking. Take this chart for example:

Screen Shot 2020-06-29 at 10 41 22
{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.13.1.json",
  "description": "Stack bar chart.",
  "data": {
    "values": [
      {"a": "A", "b": 28, "c": "X"},
      {"a": "A", "b": 55, "c": "Y"},
      {"a": "B", "b": 55, "c": "Y"},
      {"a": "B", "b": 91, "c": "Z"}
    ]
  },
  "mark": "bar",
  "encoding": {
    "x": {"field": "a", "type": "ordinal"},
    "y": {"field": "b", "type": "quantitative"},
    "color": {
      "field": "c",
      "type": "nominal",
      "scale": {
        "domain": ["X", "Y", "Z"],
        "range": ["darkgray", "orange", "lightgray"]
      }
    }
  }
}

Since the orange bars represent the same value, I decide that I'd like the orange bars to line up at the bottom. Re-ordering the domain & range will make the orange item appear at the bottom of the legend:

Screen Shot 2020-06-29 at 10 43 49
{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.13.1.json",
  "description": "Stack bar chart.",
  "data": {
    "values": [
      {"a": "A", "b": 28, "c": "X"},
      {"a": "A", "b": 55, "c": "Y"},
      {"a": "B", "b": 55, "c": "Y"},
      {"a": "B", "b": 91, "c": "Z"}
    ]
  },
  "mark": "bar",
  "encoding": {
    "x": {"field": "a", "type": "ordinal"},
    "y": {"field": "b", "type": "quantitative"},
    "color": {
      "field": "c",
      "type": "nominal",
      "scale": {
        "domain": ["X", "Z", "Y"],
        "range": ["darkgray", "lightgray", "orange"]
      }
    }
  }
}

It might be intuitive that this scale ordering also affect the order of where the orange bars appear in the stack as well, so that orange being bottom-most item in the legend also results in orange being the bottom-most mark in the stacked bars.

joelostblom commented 3 years ago

I agree with @marcprux , it would be intuitive if changing the legend order also changed the stacking order, maybe by automatically doing the calculate transform linked above? Ideally with an optional argument in case the color legend order sometimes needs to be changed independently of the stacking order.

joelostblom commented 3 years ago

Another good reason for automatically ordering according the a custom color sort, is that this is the current behavior if the color sort is set to 'ascending' or 'descending' and it would be intuitive to be consistent with this for custom color sorts. Vega Editor Example

rben01 commented 3 years ago

@mattijn

using color_license_sort_index for order.

It took me embarrassingly long for me to realize that license is not a keyword, but rather the name of the color encoding's field