Skip to content

Commit

Permalink
fix: support stack bar with reverse order (#8910)
Browse files Browse the repository at this point in the history
Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com>
  • Loading branch information
kanitw and GitHub Actions Bot authored May 18, 2023
1 parent 5a6eb70 commit 055e32f
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 4 deletions.
19 changes: 19 additions & 0 deletions build/vega-lite-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4990,6 +4990,9 @@
},
{
"$ref": "#/definitions/OrderValueDef"
},
{
"$ref": "#/definitions/OrderOnlyDef"
}
],
"description": "Order of the marks.\n- For stacked marks, this `order` channel encodes [stack order](https://vega.github.io/vega-lite/docs/stack.html#order).\n- For line and trail marks, this `order` channel encodes order of data points in the lines. This can be useful for creating [a connected scatterplot](https://vega.github.io/vega-lite/examples/connected_scatterplot.html). Setting `order` to `{\"value\": null}` makes the line marks use the original order in the data sources.\n- Otherwise, this `order` channel encodes layer order of the marks.\n\n__Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating additional aggregation grouping."
Expand Down Expand Up @@ -9076,6 +9079,9 @@
},
{
"$ref": "#/definitions/OrderValueDef"
},
{
"$ref": "#/definitions/OrderOnlyDef"
}
],
"description": "Order of the marks.\n- For stacked marks, this `order` channel encodes [stack order](https://vega.github.io/vega-lite/docs/stack.html#order).\n- For line and trail marks, this `order` channel encodes order of data points in the lines. This can be useful for creating [a connected scatterplot](https://vega.github.io/vega-lite/examples/connected_scatterplot.html). Setting `order` to `{\"value\": null}` makes the line marks use the original order in the data sources.\n- Otherwise, this `order` channel encodes layer order of the marks.\n\n__Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating additional aggregation grouping."
Expand Down Expand Up @@ -17972,6 +17978,16 @@
},
"type": "object"
},
"OrderOnlyDef": {
"additionalProperties": false,
"properties": {
"sort": {
"$ref": "#/definitions/SortOrder",
"description": "The sort order. One of `\"ascending\"` (default) or `\"descending\"`."
}
},
"type": "object"
},
"OrderValueDef": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -24211,6 +24227,9 @@
},
{
"$ref": "#/definitions/OrderValueDef"
},
{
"$ref": "#/definitions/OrderOnlyDef"
}
],
"description": "Order of the marks.\n- For stacked marks, this `order` channel encodes [stack order](https://vega.github.io/vega-lite/docs/stack.html#order).\n- For line and trail marks, this `order` channel encodes order of data points in the lines. This can be useful for creating [a connected scatterplot](https://vega.github.io/vega-lite/examples/connected_scatterplot.html). Setting `order` to `{\"value\": null}` makes the line marks use the original order in the data sources.\n- Otherwise, this `order` channel encodes layer order of the marks.\n\n__Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating additional aggregation grouping."
Expand Down
Binary file added examples/compiled/stacked_bar_v_ascending.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/compiled/stacked_bar_v_ascending.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions examples/compiled/stacked_bar_v_ascending.vg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"background": "white",
"padding": 5,
"height": 200,
"style": "cell",
"data": [
{
"name": "source_0",
"url": "data/barley.json",
"format": {"type": "json"},
"transform": [
{
"type": "aggregate",
"groupby": ["variety", "site"],
"ops": ["sum"],
"fields": ["yield"],
"as": ["sum_yield"]
},
{
"type": "stack",
"groupby": ["variety"],
"field": "sum_yield",
"sort": {"field": ["site"], "order": ["ascending"]},
"as": ["sum_yield_start", "sum_yield_end"],
"offset": "zero"
},
{
"type": "filter",
"expr": "isValid(datum[\"sum_yield\"]) && isFinite(+datum[\"sum_yield\"])"
}
]
}
],
"signals": [
{"name": "x_step", "value": 20},
{
"name": "width",
"update": "bandspace(domain('x').length, 0.1, 0.05) * x_step"
}
],
"marks": [
{
"name": "marks",
"type": "rect",
"style": ["bar"],
"from": {"data": "source_0"},
"encode": {
"update": {
"fill": {"scale": "color", "field": "site"},
"ariaRoleDescription": {"value": "bar"},
"description": {
"signal": "\"variety: \" + (isValid(datum[\"variety\"]) ? datum[\"variety\"] : \"\"+datum[\"variety\"]) + \"; Sum of yield: \" + (format(datum[\"sum_yield\"], \"\")) + \"; site: \" + (isValid(datum[\"site\"]) ? datum[\"site\"] : \"\"+datum[\"site\"])"
},
"x": {"scale": "x", "field": "variety"},
"width": {"signal": "max(0.25, bandwidth('x'))"},
"y": {"scale": "y", "field": "sum_yield_end"},
"y2": {"scale": "y", "field": "sum_yield_start"}
}
}
}
],
"scales": [
{
"name": "x",
"type": "band",
"domain": {"data": "source_0", "field": "variety", "sort": true},
"range": {"step": {"signal": "x_step"}},
"paddingInner": 0.1,
"paddingOuter": 0.05
},
{
"name": "y",
"type": "linear",
"domain": {
"data": "source_0",
"fields": ["sum_yield_start", "sum_yield_end"]
},
"range": [{"signal": "height"}, 0],
"nice": true,
"zero": true
},
{
"name": "color",
"type": "ordinal",
"domain": {"data": "source_0", "field": "site", "sort": true},
"range": "category"
}
],
"axes": [
{
"scale": "y",
"orient": "left",
"gridScale": "x",
"grid": true,
"tickCount": {"signal": "ceil(height/40)"},
"domain": false,
"labels": false,
"aria": false,
"maxExtent": 0,
"minExtent": 0,
"ticks": false,
"zindex": 0
},
{
"scale": "x",
"orient": "bottom",
"grid": false,
"title": "variety",
"labelAlign": "right",
"labelAngle": 270,
"labelBaseline": "middle",
"zindex": 0
},
{
"scale": "y",
"orient": "left",
"grid": false,
"title": "Sum of yield",
"labelOverlap": true,
"tickCount": {"signal": "ceil(height/40)"},
"zindex": 0
}
],
"legends": [{"fill": "color", "symbolType": "square", "title": "site"}]
}
11 changes: 11 additions & 0 deletions examples/specs/stacked_bar_v_ascending.vl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {"url": "data/barley.json"},
"mark": "bar",
"encoding": {
"x": {"field": "variety"},
"y": {"aggregate": "sum", "field": "yield"},
"color": {"field": "site"},
"order": {"sort": "ascending"}
}
}
10 changes: 9 additions & 1 deletion src/channeldef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,13 +633,21 @@ export interface LegendMixins {

// Order Path have no scale

export interface OrderFieldDef<F extends Field> extends FieldDefWithoutScale<F> {
export type OrderFieldDef<F extends Field> = FieldDefWithoutScale<F> & OrderOnlyDef;

export interface OrderOnlyDef {
/**
* The sort order. One of `"ascending"` (default) or `"descending"`.
*/
sort?: SortOrder;
}

export function isOrderOnlyDef<F extends Field>(
orderDef: OrderFieldDef<F> | OrderFieldDef<F>[] | OrderValueDef | OrderOnlyDef
): orderDef is OrderOnlyDef {
return orderDef && !!(orderDef as OrderOnlyDef).sort && !orderDef['field'];
}

export type OrderValueDef = ConditionValueDefMixins<number> & NumericValueDef;

export interface StringFieldDef<F extends Field> extends FieldDefWithoutScale<F, StandardType>, FormatMixins {}
Expand Down
5 changes: 3 additions & 2 deletions src/compile/data/stack.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Transforms as VgTransform} from 'vega';
import {isArray, isString} from 'vega-util';
import {FieldDef, FieldName, getFieldDef, isFieldDef, vgField} from '../../channeldef';
import {FieldDef, FieldName, getFieldDef, isFieldDef, isOrderOnlyDef, vgField} from '../../channeldef';
import {SortFields, SortOrder} from '../../sort';
import {StackOffset} from '../../stack';
import {StackTransform} from '../../transform';
Expand Down Expand Up @@ -141,12 +141,13 @@ export class StackNode extends DataFlowNode {
if (isArray(orderDef) || isFieldDef(orderDef)) {
sort = sortParams(orderDef);
} else {
const sortOrder = isOrderOnlyDef(orderDef) ? orderDef.sort : fieldChannel === 'y' ? 'descending' : 'ascending';
// default = descending by stackFields
// FIXME is the default here correct for binned fields?
sort = stackby.reduce(
(s, field) => {
s.field.push(field);
s.order.push(fieldChannel === 'y' ? 'descending' : 'ascending');
s.order.push(sortOrder);
return s;
},
{field: [], order: []}
Expand Down
11 changes: 10 additions & 1 deletion src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ import {
isConditionalDef,
isDatumDef,
isFieldDef,
isOrderOnlyDef,
isTypedFieldDef,
isValueDef,
LatLongDef,
NumericArrayMarkPropDef,
NumericMarkPropDef,
OffsetDef,
OrderFieldDef,
OrderOnlyDef,
OrderValueDef,
PolarDef,
Position2Def,
Expand Down Expand Up @@ -321,7 +323,7 @@ export interface Encoding<F extends Field> {
*
* __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating additional aggregation grouping.
*/
order?: OrderFieldDef<F> | OrderFieldDef<F>[] | OrderValueDef;
order?: OrderFieldDef<F> | OrderFieldDef<F>[] | OrderValueDef | OrderOnlyDef;
}

export interface EncodingWithFacet<F extends Field> extends Encoding<F>, EncodingFacetMapping<F> {}
Expand Down Expand Up @@ -594,6 +596,13 @@ export function initEncoding(
(channel === TOOLTIP && isArray(channelDef))
) {
if (channelDef) {
if (channel === ORDER) {
const def = encoding[channel];
if (isOrderOnlyDef(def)) {
normalizedEncoding[channel] = def;
continue;
}
}
// Array of fieldDefs for detail channel (or production rule)
(normalizedEncoding[channel] as any) = array(channelDef).reduce(
(defs: FieldDef<string>[], fieldDef: FieldDef<string>) => {
Expand Down

0 comments on commit 055e32f

Please sign in to comment.