From 4e558369eecc5d84fbccd0fd2bc84be19f5f5e35 Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Mon, 8 Apr 2024 10:20:27 -0700 Subject: [PATCH] fix(#8338,#8126): make boxplot work with single value per group (#8339) Fixes #8338, fixes #8126 --------- Co-authored-by: GitHub Actions Bot --- .../compiled/boxplot_1D_horizontal.vg.json | 8 +- .../boxplot_1D_horizontal_custom_mark.vg.json | 8 +- .../boxplot_1D_horizontal_explicit.vg.json | 8 +- examples/compiled/boxplot_1D_invalid.vg.json | 8 +- examples/compiled/boxplot_1D_vertical.vg.json | 8 +- .../compiled/boxplot_2D_horizontal.vg.json | 8 +- .../boxplot_2D_horizontal_color_size.vg.json | 8 +- examples/compiled/boxplot_2D_vertical.vg.json | 12 +- .../boxplot_2D_vertical_single_per_group.png | Bin 0 -> 3678 bytes .../boxplot_2D_vertical_single_per_group.svg | 1 + ...xplot_2D_vertical_single_per_group.vg.json | 297 ++++++++++++++++ ...lot_2D_vertical_single_per_group_color.png | Bin 0 -> 4987 bytes ...lot_2D_vertical_single_per_group_color.svg | 1 + ...2D_vertical_single_per_group_color.vg.json | 320 ++++++++++++++++++ examples/compiled/boxplot_groupped.vg.json | 12 +- .../boxplot_minmax_2D_horizontal.vg.json | 8 +- ...2D_horizontal_custom_midtick_color.vg.json | 8 +- .../boxplot_minmax_2D_vertical.vg.json | 17 +- .../boxplot_tooltip_aggregate.vg.json | 8 +- .../boxplot_tooltip_not_aggregate.vg.json | 8 +- .../compiled/layer_boxplot_circle.vg.json | 8 +- examples/compiled/vega_version | 2 +- ...xplot_2D_vertical_single_per_group.vl.json | 14 + ...2D_vertical_single_per_group_color.vl.json | 15 + ..._horizontal_custom_mark_normalized.vl.json | 6 + ..._1D_horizontal_explicit_normalized.vl.json | 6 + .../boxplot_1D_horizontal_normalized.vl.json | 6 + .../boxplot_1D_invalid_normalized.vl.json | 6 + .../boxplot_1D_vertical_normalized.vl.json | 6 + ...D_horizontal_color_size_normalized.vl.json | 6 + .../boxplot_2D_horizontal_normalized.vl.json | 6 + .../boxplot_2D_vertical_normalized.vl.json | 8 + ..._single_per_group_color_normalized.vl.json | 203 +++++++++++ ...rtical_single_per_group_normalized.vl.json | 200 +++++++++++ .../boxplot_groupped_normalized.vl.json | 7 + ...al_custom_midtick_color_normalized.vl.json | 6 + ...ot_minmax_2D_horizontal_normalized.vl.json | 6 + ...plot_minmax_2D_vertical_normalized.vl.json | 8 + ...xplot_tooltip_aggregate_normalized.vl.json | 6 + ...t_tooltip_not_aggregate_normalized.vl.json | 6 + .../layer_boxplot_circle_normalized.vl.json | 6 + src/compositemark/boxplot.ts | 12 +- 42 files changed, 1277 insertions(+), 20 deletions(-) create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group.png create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group.svg create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group.vg.json create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group_color.png create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group_color.svg create mode 100644 examples/compiled/boxplot_2D_vertical_single_per_group_color.vg.json create mode 100644 examples/specs/boxplot_2D_vertical_single_per_group.vl.json create mode 100644 examples/specs/boxplot_2D_vertical_single_per_group_color.vl.json create mode 100644 examples/specs/normalized/boxplot_2D_vertical_single_per_group_color_normalized.vl.json create mode 100644 examples/specs/normalized/boxplot_2D_vertical_single_per_group_normalized.vl.json diff --git a/examples/compiled/boxplot_1D_horizontal.vg.json b/examples/compiled/boxplot_1D_horizontal.vg.json index b7dd924888..989dcbfbf3 100644 --- a/examples/compiled/boxplot_1D_horizontal.vg.json +++ b/examples/compiled/boxplot_1D_horizontal.vg.json @@ -219,7 +219,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\")}" }, diff --git a/examples/compiled/boxplot_1D_horizontal_custom_mark.vg.json b/examples/compiled/boxplot_1D_horizontal_custom_mark.vg.json index 37c52929e7..b46b4f4f66 100644 --- a/examples/compiled/boxplot_1D_horizontal_custom_mark.vg.json +++ b/examples/compiled/boxplot_1D_horizontal_custom_mark.vg.json @@ -269,7 +269,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "red"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "red"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\")}" }, diff --git a/examples/compiled/boxplot_1D_horizontal_explicit.vg.json b/examples/compiled/boxplot_1D_horizontal_explicit.vg.json index b7dd924888..989dcbfbf3 100644 --- a/examples/compiled/boxplot_1D_horizontal_explicit.vg.json +++ b/examples/compiled/boxplot_1D_horizontal_explicit.vg.json @@ -219,7 +219,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\")}" }, diff --git a/examples/compiled/boxplot_1D_invalid.vg.json b/examples/compiled/boxplot_1D_invalid.vg.json index 9bca1863b7..cdaf4a8f35 100644 --- a/examples/compiled/boxplot_1D_invalid.vg.json +++ b/examples/compiled/boxplot_1D_invalid.vg.json @@ -207,7 +207,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of b\": format(datum[\"max_b\"], \"\"), \"Q3 of b\": format(datum[\"upper_box_b\"], \"\"), \"Median of b\": format(datum[\"mid_box_b\"], \"\"), \"Q1 of b\": format(datum[\"lower_box_b\"], \"\"), \"Min of b\": format(datum[\"min_b\"], \"\"), \"a\": format(datum[\"a\"], \"\")}" }, diff --git a/examples/compiled/boxplot_1D_vertical.vg.json b/examples/compiled/boxplot_1D_vertical.vg.json index fcf60d6c8f..4e5ceedd6f 100644 --- a/examples/compiled/boxplot_1D_vertical.vg.json +++ b/examples/compiled/boxplot_1D_vertical.vg.json @@ -219,7 +219,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\")}" }, diff --git a/examples/compiled/boxplot_2D_horizontal.vg.json b/examples/compiled/boxplot_2D_horizontal.vg.json index 657e0434dd..5ae134dabb 100644 --- a/examples/compiled/boxplot_2D_horizontal.vg.json +++ b/examples/compiled/boxplot_2D_horizontal.vg.json @@ -222,7 +222,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, diff --git a/examples/compiled/boxplot_2D_horizontal_color_size.vg.json b/examples/compiled/boxplot_2D_horizontal_color_size.vg.json index aa06950bc5..8bc85b5725 100644 --- a/examples/compiled/boxplot_2D_horizontal_color_size.vg.json +++ b/examples/compiled/boxplot_2D_horizontal_color_size.vg.json @@ -222,7 +222,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "teal" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, diff --git a/examples/compiled/boxplot_2D_vertical.vg.json b/examples/compiled/boxplot_2D_vertical.vg.json index d79f793f82..7dbbd0d072 100644 --- a/examples/compiled/boxplot_2D_vertical.vg.json +++ b/examples/compiled/boxplot_2D_vertical.vg.json @@ -222,7 +222,14 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "scale": "color", + "field": "Species" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, @@ -277,7 +284,8 @@ "domain": { "fields": [ {"data": "data_1", "field": "Species"}, - {"data": "data_6", "field": "Species"} + {"data": "data_6", "field": "Species"}, + {"data": "data_7", "field": "Species"} ], "sort": true }, diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group.png b/examples/compiled/boxplot_2D_vertical_single_per_group.png new file mode 100644 index 0000000000000000000000000000000000000000..2f990f9293ac83ff35ba9122bdba6a0d139cd076 GIT binary patch literal 3678 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 XiVMV%F}Ul33nQeitgTdxGzt0#@lw=5 literal 0 HcmV?d00001 diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group.svg b/examples/compiled/boxplot_2D_vertical_single_per_group.svg new file mode 100644 index 0000000000..f43395584f --- /dev/null +++ b/examples/compiled/boxplot_2D_vertical_single_per_group.svg @@ -0,0 +1 @@ +ABa01020304050b \ No newline at end of file diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group.vg.json b/examples/compiled/boxplot_2D_vertical_single_per_group.vg.json new file mode 100644 index 0000000000..0ea2889ea3 --- /dev/null +++ b/examples/compiled/boxplot_2D_vertical_single_per_group.vg.json @@ -0,0 +1,297 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "A simple bar chart with embedded data.", + "background": "white", + "padding": 5, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "values": [{"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55}] + }, + { + "name": "data_1", + "source": "source_0", + "transform": [ + { + "type": "joinaggregate", + "as": ["lower_box_b", "upper_box_b"], + "ops": ["q1", "q3"], + "fields": ["b", "b"], + "groupby": ["a"] + } + ] + }, + { + "name": "data_2", + "source": "data_1", + "transform": [ + { + "type": "filter", + "expr": "(datum[\"b\"] < datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"])) || (datum[\"b\"] > datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "type": "filter", + "expr": "isValid(datum[\"b\"]) && isFinite(+datum[\"b\"])" + } + ] + }, + { + "name": "data_3", + "source": "data_1", + "transform": [ + { + "type": "filter", + "expr": "(datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]) <= datum[\"b\"]) && (datum[\"b\"] <= datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "type": "aggregate", + "groupby": ["a"], + "ops": ["min", "max", "min", "max"], + "fields": ["b", "b", "lower_box_b", "upper_box_b"], + "as": [ + "lower_whisker_b", + "upper_whisker_b", + "lower_box_b", + "upper_box_b" + ] + } + ] + }, + { + "name": "data_4", + "source": "data_3", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"lower_whisker_b\"]) && isFinite(+datum[\"lower_whisker_b\"])" + } + ] + }, + { + "name": "data_5", + "source": "data_3", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"upper_box_b\"]) && isFinite(+datum[\"upper_box_b\"])" + } + ] + }, + { + "name": "data_6", + "source": "source_0", + "transform": [ + { + "type": "aggregate", + "groupby": ["a"], + "ops": ["q1", "q3", "median", "min", "max"], + "fields": ["b", "b", "b", "b", "b"], + "as": ["lower_box_b", "upper_box_b", "mid_box_b", "min_b", "max_b"] + } + ] + }, + { + "name": "data_7", + "source": "data_6", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"lower_box_b\"]) && isFinite(+datum[\"lower_box_b\"])" + } + ] + }, + { + "name": "data_8", + "source": "data_6", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"mid_box_b\"]) && isFinite(+datum[\"mid_box_b\"])" + } + ] + } + ], + "signals": [ + {"name": "x_step", "value": 20}, + {"name": "width", "update": "bandspace(domain('x').length, 0, 0) * x_step"} + ], + "marks": [ + { + "name": "layer_0_layer_0_marks", + "type": "symbol", + "style": ["point", "boxplot-outliers"], + "from": {"data": "data_2"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "fill": {"value": "transparent"}, + "stroke": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "point"}, + "description": { + "signal": "\"a: \" + (isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]) + \"; b: \" + (format(datum[\"b\"], \"\"))" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "b"} + } + } + }, + { + "name": "layer_0_layer_1_layer_0_marks", + "type": "rule", + "style": ["rule", "boxplot-rule"], + "aria": false, + "from": {"data": "data_4"}, + "encode": { + "update": { + "stroke": {"value": "black"}, + "tooltip": { + "signal": "{\"Upper Whisker of b\": format(datum[\"upper_whisker_b\"], \"\"), \"Lower Whisker of b\": format(datum[\"lower_whisker_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "lower_whisker_b"}, + "y2": {"scale": "y", "field": "lower_box_b"} + } + } + }, + { + "name": "layer_0_layer_1_layer_1_marks", + "type": "rule", + "style": ["rule", "boxplot-rule"], + "aria": false, + "from": {"data": "data_5"}, + "encode": { + "update": { + "stroke": {"value": "black"}, + "tooltip": { + "signal": "{\"Upper Whisker of b\": format(datum[\"upper_whisker_b\"], \"\"), \"Lower Whisker of b\": format(datum[\"lower_whisker_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "upper_box_b"}, + "y2": {"scale": "y", "field": "upper_whisker_b"} + } + } + }, + { + "name": "layer_1_layer_0_marks", + "type": "rect", + "style": ["bar", "boxplot-box"], + "from": {"data": "data_7"}, + "encode": { + "update": { + "ariaRoleDescription": {"value": "box"}, + "fill": {"value": "#4c78a8"}, + "tooltip": { + "signal": "{\"Max of b\": format(datum[\"max_b\"], \"\"), \"Q3 of b\": format(datum[\"upper_box_b\"], \"\"), \"Median of b\": format(datum[\"mid_box_b\"], \"\"), \"Q1 of b\": format(datum[\"lower_box_b\"], \"\"), \"Min of b\": format(datum[\"min_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "description": { + "signal": "\"a: \" + (isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]) + \"; b: \" + (format(datum[\"lower_box_b\"], \"\")) + \"; upper_box_b: \" + (format(datum[\"upper_box_b\"], \"\")) + \"; Max of b: \" + (format(datum[\"max_b\"], \"\")) + \"; Q3 of b: \" + (format(datum[\"upper_box_b\"], \"\")) + \"; Median of b: \" + (format(datum[\"mid_box_b\"], \"\")) + \"; Q1 of b: \" + (format(datum[\"lower_box_b\"], \"\")) + \"; Min of b: \" + (format(datum[\"min_b\"], \"\"))" + }, + "xc": {"scale": "x", "field": "a", "band": 0.5}, + "width": {"value": 14}, + "y": {"scale": "y", "field": "lower_box_b"}, + "y2": {"scale": "y", "field": "upper_box_b"} + } + } + }, + { + "name": "layer_1_layer_1_marks", + "type": "rect", + "style": ["tick", "boxplot-median"], + "aria": false, + "from": {"data": "data_8"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "fill": [ + { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "value": "#4c78a8" + }, + {"value": "white"} + ], + "tooltip": { + "signal": "{\"Max of b\": format(datum[\"max_b\"], \"\"), \"Q3 of b\": format(datum[\"upper_box_b\"], \"\"), \"Median of b\": format(datum[\"mid_box_b\"], \"\"), \"Q1 of b\": format(datum[\"lower_box_b\"], \"\"), \"Min of b\": format(datum[\"min_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "xc": {"scale": "x", "field": "a", "band": 0.5}, + "yc": {"scale": "y", "field": "mid_box_b"}, + "width": {"value": 14}, + "height": {"value": 1} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "band", + "domain": { + "fields": [ + {"data": "data_2", "field": "a"}, + {"data": "data_4", "field": "a"}, + {"data": "data_5", "field": "a"}, + {"data": "data_7", "field": "a"}, + {"data": "data_8", "field": "a"} + ], + "sort": true + }, + "range": {"step": {"signal": "x_step"}}, + "paddingInner": 0, + "paddingOuter": 0 + }, + { + "name": "y", + "type": "linear", + "domain": { + "fields": [ + {"data": "data_2", "field": "b"}, + {"data": "data_4", "field": "lower_whisker_b"}, + {"data": "data_4", "field": "lower_box_b"}, + {"data": "data_5", "field": "upper_box_b"}, + {"data": "data_5", "field": "upper_whisker_b"}, + {"data": "data_7", "field": "lower_box_b"}, + {"data": "data_7", "field": "upper_box_b"}, + {"data": "data_8", "field": "mid_box_b"} + ] + }, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "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": "a", + "labelAngle": 0, + "labelBaseline": "top", + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "b", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group_color.png b/examples/compiled/boxplot_2D_vertical_single_per_group_color.png new file mode 100644 index 0000000000000000000000000000000000000000..5abbf21f7d099dc9430457a8f66960f05ea2e404 GIT binary patch literal 4987 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 literal 0 HcmV?d00001 diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group_color.svg b/examples/compiled/boxplot_2D_vertical_single_per_group_color.svg new file mode 100644 index 0000000000..96a2e7ca0c --- /dev/null +++ b/examples/compiled/boxplot_2D_vertical_single_per_group_color.svg @@ -0,0 +1 @@ +ABa01020304050bABa \ No newline at end of file diff --git a/examples/compiled/boxplot_2D_vertical_single_per_group_color.vg.json b/examples/compiled/boxplot_2D_vertical_single_per_group_color.vg.json new file mode 100644 index 0000000000..d1313bdca4 --- /dev/null +++ b/examples/compiled/boxplot_2D_vertical_single_per_group_color.vg.json @@ -0,0 +1,320 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "A simple bar chart with embedded data.", + "background": "white", + "padding": 5, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "values": [{"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55}] + }, + { + "name": "data_1", + "source": "source_0", + "transform": [ + { + "type": "joinaggregate", + "as": ["lower_box_b", "upper_box_b"], + "ops": ["q1", "q3"], + "fields": ["b", "b"], + "groupby": ["a", "a"] + } + ] + }, + { + "name": "data_2", + "source": "data_1", + "transform": [ + { + "type": "filter", + "expr": "(datum[\"b\"] < datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"])) || (datum[\"b\"] > datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "type": "filter", + "expr": "isValid(datum[\"b\"]) && isFinite(+datum[\"b\"])" + } + ] + }, + { + "name": "data_3", + "source": "data_1", + "transform": [ + { + "type": "filter", + "expr": "(datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]) <= datum[\"b\"]) && (datum[\"b\"] <= datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "type": "aggregate", + "groupby": ["a"], + "ops": ["min", "max", "min", "max"], + "fields": ["b", "b", "lower_box_b", "upper_box_b"], + "as": [ + "lower_whisker_b", + "upper_whisker_b", + "lower_box_b", + "upper_box_b" + ] + } + ] + }, + { + "name": "data_4", + "source": "data_3", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"lower_whisker_b\"]) && isFinite(+datum[\"lower_whisker_b\"])" + } + ] + }, + { + "name": "data_5", + "source": "data_3", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"upper_box_b\"]) && isFinite(+datum[\"upper_box_b\"])" + } + ] + }, + { + "name": "data_6", + "source": "source_0", + "transform": [ + { + "type": "aggregate", + "groupby": ["a"], + "ops": ["q1", "q3", "median", "min", "max"], + "fields": ["b", "b", "b", "b", "b"], + "as": ["lower_box_b", "upper_box_b", "mid_box_b", "min_b", "max_b"] + } + ] + }, + { + "name": "data_7", + "source": "data_6", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"lower_box_b\"]) && isFinite(+datum[\"lower_box_b\"])" + } + ] + }, + { + "name": "data_8", + "source": "data_6", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"mid_box_b\"]) && isFinite(+datum[\"mid_box_b\"])" + } + ] + } + ], + "signals": [ + {"name": "x_step", "value": 20}, + {"name": "width", "update": "bandspace(domain('x').length, 0, 0) * x_step"} + ], + "marks": [ + { + "name": "layer_0_layer_0_marks", + "type": "symbol", + "style": ["point", "boxplot-outliers"], + "from": {"data": "data_2"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "fill": {"value": "transparent"}, + "stroke": {"scale": "color", "field": "a"}, + "ariaRoleDescription": {"value": "point"}, + "description": { + "signal": "\"a: \" + (isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]) + \"; b: \" + (format(datum[\"b\"], \"\"))" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "b"} + } + } + }, + { + "name": "layer_0_layer_1_layer_0_marks", + "type": "rule", + "style": ["rule", "boxplot-rule"], + "aria": false, + "from": {"data": "data_4"}, + "encode": { + "update": { + "stroke": {"value": "black"}, + "tooltip": { + "signal": "{\"Upper Whisker of b\": format(datum[\"upper_whisker_b\"], \"\"), \"Lower Whisker of b\": format(datum[\"lower_whisker_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "lower_whisker_b"}, + "y2": {"scale": "y", "field": "lower_box_b"} + } + } + }, + { + "name": "layer_0_layer_1_layer_1_marks", + "type": "rule", + "style": ["rule", "boxplot-rule"], + "aria": false, + "from": {"data": "data_5"}, + "encode": { + "update": { + "stroke": {"value": "black"}, + "tooltip": { + "signal": "{\"Upper Whisker of b\": format(datum[\"upper_whisker_b\"], \"\"), \"Lower Whisker of b\": format(datum[\"lower_whisker_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "x": {"scale": "x", "field": "a", "band": 0.5}, + "y": {"scale": "y", "field": "upper_box_b"}, + "y2": {"scale": "y", "field": "upper_whisker_b"} + } + } + }, + { + "name": "layer_1_layer_0_marks", + "type": "rect", + "style": ["bar", "boxplot-box"], + "from": {"data": "data_7"}, + "encode": { + "update": { + "ariaRoleDescription": {"value": "box"}, + "fill": {"scale": "color", "field": "a"}, + "tooltip": { + "signal": "{\"Max of b\": format(datum[\"max_b\"], \"\"), \"Q3 of b\": format(datum[\"upper_box_b\"], \"\"), \"Median of b\": format(datum[\"mid_box_b\"], \"\"), \"Q1 of b\": format(datum[\"lower_box_b\"], \"\"), \"Min of b\": format(datum[\"min_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "description": { + "signal": "\"a: \" + (isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]) + \"; b: \" + (format(datum[\"lower_box_b\"], \"\")) + \"; upper_box_b: \" + (format(datum[\"upper_box_b\"], \"\")) + \"; Max of b: \" + (format(datum[\"max_b\"], \"\")) + \"; Q3 of b: \" + (format(datum[\"upper_box_b\"], \"\")) + \"; Median of b: \" + (format(datum[\"mid_box_b\"], \"\")) + \"; Q1 of b: \" + (format(datum[\"lower_box_b\"], \"\")) + \"; Min of b: \" + (format(datum[\"min_b\"], \"\"))" + }, + "xc": {"scale": "x", "field": "a", "band": 0.5}, + "width": {"value": 14}, + "y": {"scale": "y", "field": "lower_box_b"}, + "y2": {"scale": "y", "field": "upper_box_b"} + } + } + }, + { + "name": "layer_1_layer_1_marks", + "type": "rect", + "style": ["tick", "boxplot-median"], + "aria": false, + "from": {"data": "data_8"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "fill": [ + { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "scale": "color", + "field": "a" + }, + {"value": "white"} + ], + "tooltip": { + "signal": "{\"Max of b\": format(datum[\"max_b\"], \"\"), \"Q3 of b\": format(datum[\"upper_box_b\"], \"\"), \"Median of b\": format(datum[\"mid_box_b\"], \"\"), \"Q1 of b\": format(datum[\"lower_box_b\"], \"\"), \"Min of b\": format(datum[\"min_b\"], \"\"), \"a\": isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]}" + }, + "xc": {"scale": "x", "field": "a", "band": 0.5}, + "yc": {"scale": "y", "field": "mid_box_b"}, + "width": {"value": 14}, + "height": {"value": 1} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "band", + "domain": { + "fields": [ + {"data": "data_2", "field": "a"}, + {"data": "data_4", "field": "a"}, + {"data": "data_5", "field": "a"}, + {"data": "data_7", "field": "a"}, + {"data": "data_8", "field": "a"} + ], + "sort": true + }, + "range": {"step": {"signal": "x_step"}}, + "paddingInner": 0, + "paddingOuter": 0 + }, + { + "name": "y", + "type": "linear", + "domain": { + "fields": [ + {"data": "data_2", "field": "b"}, + {"data": "data_4", "field": "lower_whisker_b"}, + {"data": "data_4", "field": "lower_box_b"}, + {"data": "data_5", "field": "upper_box_b"}, + {"data": "data_5", "field": "upper_whisker_b"}, + {"data": "data_7", "field": "lower_box_b"}, + {"data": "data_7", "field": "upper_box_b"}, + {"data": "data_8", "field": "mid_box_b"} + ] + }, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + }, + { + "name": "color", + "type": "ordinal", + "domain": { + "fields": [ + {"data": "data_2", "field": "a"}, + {"data": "data_7", "field": "a"}, + {"data": "data_8", "field": "a"} + ], + "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": "a", + "labelAngle": 0, + "labelBaseline": "top", + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "b", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ], + "legends": [ + { + "stroke": "color", + "symbolType": "circle", + "title": "a", + "encode": {"symbols": {"update": {"opacity": {"value": 0.7}}}}, + "fill": "color" + } + ] +} diff --git a/examples/compiled/boxplot_groupped.vg.json b/examples/compiled/boxplot_groupped.vg.json index 71cd079b7d..2b0b85159b 100644 --- a/examples/compiled/boxplot_groupped.vg.json +++ b/examples/compiled/boxplot_groupped.vg.json @@ -240,7 +240,14 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Acceleration'] >= datum['upper_box_Acceleration']", + "scale": "color", + "field": "Origin" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Acceleration\": format(datum[\"max_Acceleration\"], \"\"), \"Q3 of Acceleration\": format(datum[\"upper_box_Acceleration\"], \"\"), \"Median of Acceleration\": format(datum[\"mid_box_Acceleration\"], \"\"), \"Q1 of Acceleration\": format(datum[\"lower_box_Acceleration\"], \"\"), \"Min of Acceleration\": format(datum[\"min_Acceleration\"], \"\"), \"Cylinders\": isValid(datum[\"Cylinders\"]) ? datum[\"Cylinders\"] : \"\"+datum[\"Cylinders\"], \"Origin\": isValid(datum[\"Origin\"]) ? datum[\"Origin\"] : \"\"+datum[\"Origin\"]}" }, @@ -314,7 +321,8 @@ "domain": { "fields": [ {"data": "data_1", "field": "Origin"}, - {"data": "data_6", "field": "Origin"} + {"data": "data_6", "field": "Origin"}, + {"data": "data_7", "field": "Origin"} ], "sort": true }, diff --git a/examples/compiled/boxplot_minmax_2D_horizontal.vg.json b/examples/compiled/boxplot_minmax_2D_horizontal.vg.json index b6a8859e67..1cfb790cf9 100644 --- a/examples/compiled/boxplot_minmax_2D_horizontal.vg.json +++ b/examples/compiled/boxplot_minmax_2D_horizontal.vg.json @@ -145,7 +145,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"upper_whisker_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"lower_whisker_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, diff --git a/examples/compiled/boxplot_minmax_2D_horizontal_custom_midtick_color.vg.json b/examples/compiled/boxplot_minmax_2D_horizontal_custom_midtick_color.vg.json index cbbf3ea904..3be74d5c6a 100644 --- a/examples/compiled/boxplot_minmax_2D_horizontal_custom_midtick_color.vg.json +++ b/examples/compiled/boxplot_minmax_2D_horizontal_custom_midtick_color.vg.json @@ -145,7 +145,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "orange"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "orange"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"upper_whisker_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"lower_whisker_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, diff --git a/examples/compiled/boxplot_minmax_2D_vertical.vg.json b/examples/compiled/boxplot_minmax_2D_vertical.vg.json index ebff85d15d..f8f5dfbde6 100644 --- a/examples/compiled/boxplot_minmax_2D_vertical.vg.json +++ b/examples/compiled/boxplot_minmax_2D_vertical.vg.json @@ -145,7 +145,14 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "scale": "color", + "field": "Species" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"upper_whisker_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"lower_whisker_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, @@ -195,7 +202,13 @@ { "name": "color", "type": "ordinal", - "domain": {"data": "data_2", "field": "Species", "sort": true}, + "domain": { + "fields": [ + {"data": "data_2", "field": "Species"}, + {"data": "data_3", "field": "Species"} + ], + "sort": true + }, "range": "category" } ], diff --git a/examples/compiled/boxplot_tooltip_aggregate.vg.json b/examples/compiled/boxplot_tooltip_aggregate.vg.json index 3639739c61..f2597fbe5b 100644 --- a/examples/compiled/boxplot_tooltip_aggregate.vg.json +++ b/examples/compiled/boxplot_tooltip_aggregate.vg.json @@ -220,7 +220,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": {"signal": "format(datum[\"mean_Body Mass (g)\"], \"\")"}, "xc": {"scale": "x", "field": "mid_box_Body Mass (g)"}, "yc": {"scale": "y", "field": "Species", "band": 0.5}, diff --git a/examples/compiled/boxplot_tooltip_not_aggregate.vg.json b/examples/compiled/boxplot_tooltip_not_aggregate.vg.json index fdc830314a..f845845978 100644 --- a/examples/compiled/boxplot_tooltip_not_aggregate.vg.json +++ b/examples/compiled/boxplot_tooltip_not_aggregate.vg.json @@ -223,7 +223,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of Body Mass (g)\": format(datum[\"max_Body Mass (g)\"], \"\"), \"Q3 of Body Mass (g)\": format(datum[\"upper_box_Body Mass (g)\"], \"\"), \"Median of Body Mass (g)\": format(datum[\"mid_box_Body Mass (g)\"], \"\"), \"Q1 of Body Mass (g)\": format(datum[\"lower_box_Body Mass (g)\"], \"\"), \"Min of Body Mass (g)\": format(datum[\"min_Body Mass (g)\"], \"\"), \"Species\": isValid(datum[\"Species\"]) ? datum[\"Species\"] : \"\"+datum[\"Species\"]}" }, diff --git a/examples/compiled/layer_boxplot_circle.vg.json b/examples/compiled/layer_boxplot_circle.vg.json index bf1b80bc52..3186d3f671 100644 --- a/examples/compiled/layer_boxplot_circle.vg.json +++ b/examples/compiled/layer_boxplot_circle.vg.json @@ -187,7 +187,13 @@ "encode": { "update": { "opacity": {"value": 0.7}, - "fill": {"value": "white"}, + "fill": [ + { + "test": "datum['lower_box_people'] >= datum['upper_box_people']", + "value": "#4c78a8" + }, + {"value": "white"} + ], "tooltip": { "signal": "{\"Max of population\": format(datum[\"max_people\"], \"\"), \"Q3 of population\": format(datum[\"upper_box_people\"], \"\"), \"Median of population\": format(datum[\"mid_box_people\"], \"\"), \"Q1 of population\": format(datum[\"lower_box_people\"], \"\"), \"Min of population\": format(datum[\"min_people\"], \"\"), \"age\": isValid(datum[\"age\"]) ? datum[\"age\"] : \"\"+datum[\"age\"]}" }, diff --git a/examples/compiled/vega_version b/examples/compiled/vega_version index 79b12df42d..d3f82c401a 100644 --- a/examples/compiled/vega_version +++ b/examples/compiled/vega_version @@ -1 +1 @@ -vega: 5.27.0 +vega: 5.28.0 diff --git a/examples/specs/boxplot_2D_vertical_single_per_group.vl.json b/examples/specs/boxplot_2D_vertical_single_per_group.vl.json new file mode 100644 index 0000000000..1c00ad7159 --- /dev/null +++ b/examples/specs/boxplot_2D_vertical_single_per_group.vl.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [ + {"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55} + ] + }, + "mark": "boxplot", + "encoding": { + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "y": {"field": "b", "type": "quantitative"} + } +} diff --git a/examples/specs/boxplot_2D_vertical_single_per_group_color.vl.json b/examples/specs/boxplot_2D_vertical_single_per_group_color.vl.json new file mode 100644 index 0000000000..793cffb7ff --- /dev/null +++ b/examples/specs/boxplot_2D_vertical_single_per_group_color.vl.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [ + {"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55} + ] + }, + "mark": "boxplot", + "encoding": { + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "color": {"field": "a", "type": "nominal"}, + "y": {"field": "b", "type": "quantitative"} + } +} diff --git a/examples/specs/normalized/boxplot_1D_horizontal_custom_mark_normalized.vl.json b/examples/specs/normalized/boxplot_1D_horizontal_custom_mark_normalized.vl.json index c7f8120a72..fecd2fe25e 100644 --- a/examples/specs/normalized/boxplot_1D_horizontal_custom_mark_normalized.vl.json +++ b/examples/specs/normalized/boxplot_1D_horizontal_custom_mark_normalized.vl.json @@ -269,6 +269,12 @@ "title": "Body Mass (g)", "scale": {"zero": false} }, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_1D_horizontal_explicit_normalized.vl.json b/examples/specs/normalized/boxplot_1D_horizontal_explicit_normalized.vl.json index 7f5e5ba217..3346ac339f 100644 --- a/examples/specs/normalized/boxplot_1D_horizontal_explicit_normalized.vl.json +++ b/examples/specs/normalized/boxplot_1D_horizontal_explicit_normalized.vl.json @@ -209,6 +209,12 @@ "title": "Body Mass (g)", "scale": {"zero": false} }, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_1D_horizontal_normalized.vl.json b/examples/specs/normalized/boxplot_1D_horizontal_normalized.vl.json index 7f5e5ba217..3346ac339f 100644 --- a/examples/specs/normalized/boxplot_1D_horizontal_normalized.vl.json +++ b/examples/specs/normalized/boxplot_1D_horizontal_normalized.vl.json @@ -209,6 +209,12 @@ "title": "Body Mass (g)", "scale": {"zero": false} }, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_1D_invalid_normalized.vl.json b/examples/specs/normalized/boxplot_1D_invalid_normalized.vl.json index ff48fdcb03..dce2524e3d 100644 --- a/examples/specs/normalized/boxplot_1D_invalid_normalized.vl.json +++ b/examples/specs/normalized/boxplot_1D_invalid_normalized.vl.json @@ -196,6 +196,12 @@ "type": "quantitative", "axis": {"labelAngle": 0} }, + "color": { + "condition": { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "value": "#4c78a8" + } + }, "tooltip": [ {"field": "max_b", "type": "quantitative", "title": "Max of b"}, { diff --git a/examples/specs/normalized/boxplot_1D_vertical_normalized.vl.json b/examples/specs/normalized/boxplot_1D_vertical_normalized.vl.json index 32161bd1e8..6a0640cc08 100644 --- a/examples/specs/normalized/boxplot_1D_vertical_normalized.vl.json +++ b/examples/specs/normalized/boxplot_1D_vertical_normalized.vl.json @@ -209,6 +209,12 @@ "title": "Body Mass (g)", "scale": {"zero": false} }, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_2D_horizontal_color_size_normalized.vl.json b/examples/specs/normalized/boxplot_2D_horizontal_color_size_normalized.vl.json index 427ef196b8..e7c86be774 100644 --- a/examples/specs/normalized/boxplot_2D_horizontal_color_size_normalized.vl.json +++ b/examples/specs/normalized/boxplot_2D_horizontal_color_size_normalized.vl.json @@ -221,6 +221,12 @@ }, "y": {"field": "Species", "type": "nominal"}, "size": {"value": 10}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "teal" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_2D_horizontal_normalized.vl.json b/examples/specs/normalized/boxplot_2D_horizontal_normalized.vl.json index 204779636f..ccd9dc76e3 100644 --- a/examples/specs/normalized/boxplot_2D_horizontal_normalized.vl.json +++ b/examples/specs/normalized/boxplot_2D_horizontal_normalized.vl.json @@ -217,6 +217,12 @@ "scale": {"zero": false} }, "y": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_2D_vertical_normalized.vl.json b/examples/specs/normalized/boxplot_2D_vertical_normalized.vl.json index f8e447b54a..6acebbfc1b 100644 --- a/examples/specs/normalized/boxplot_2D_vertical_normalized.vl.json +++ b/examples/specs/normalized/boxplot_2D_vertical_normalized.vl.json @@ -219,6 +219,14 @@ "scale": {"zero": false} }, "x": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "field": "Species", + "type": "nominal", + "legend": null + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_2D_vertical_single_per_group_color_normalized.vl.json b/examples/specs/normalized/boxplot_2D_vertical_single_per_group_color_normalized.vl.json new file mode 100644 index 0000000000..f33ab5ebd9 --- /dev/null +++ b/examples/specs/normalized/boxplot_2D_vertical_single_per_group_color_normalized.vl.json @@ -0,0 +1,203 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [{"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55}] + }, + "layer": [ + { + "transform": [ + { + "joinaggregate": [ + {"op": "q1", "field": "b", "as": "lower_box_b"}, + {"op": "q3", "field": "b", "as": "upper_box_b"} + ], + "groupby": ["a", "a"] + } + ], + "layer": [ + { + "transform": [ + { + "filter": "(datum[\"b\"] < datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"])) || (datum[\"b\"] > datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + } + ], + "mark": {"type": "point", "style": "boxplot-outliers"}, + "encoding": { + "y": {"field": "b", "type": "quantitative", "title": "b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "color": {"field": "a", "type": "nominal"} + } + }, + { + "transform": [ + { + "filter": "(datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]) <= datum[\"b\"]) && (datum[\"b\"] <= datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "aggregate": [ + {"op": "min", "field": "b", "as": "lower_whisker_b"}, + {"op": "max", "field": "b", "as": "upper_whisker_b"}, + {"op": "min", "field": "lower_box_b", "as": "lower_box_b"}, + {"op": "max", "field": "upper_box_b", "as": "upper_box_b"} + ], + "groupby": ["a", "a"] + } + ], + "layer": [ + { + "mark": {"type": "rule", "aria": false, "style": "boxplot-rule"}, + "encoding": { + "y": { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "b" + }, + "y2": {"field": "lower_box_b"}, + "x": { + "field": "a", + "type": "nominal", + "axis": {"labelAngle": 0} + }, + "tooltip": [ + { + "field": "upper_whisker_b", + "type": "quantitative", + "title": "Upper Whisker of b" + }, + { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "Lower Whisker of b" + }, + {"field": "a", "type": "nominal"} + ] + } + }, + { + "mark": {"type": "rule", "aria": false, "style": "boxplot-rule"}, + "encoding": { + "y": { + "field": "upper_box_b", + "type": "quantitative", + "title": "b" + }, + "y2": {"field": "upper_whisker_b"}, + "x": { + "field": "a", + "type": "nominal", + "axis": {"labelAngle": 0} + }, + "tooltip": [ + { + "field": "upper_whisker_b", + "type": "quantitative", + "title": "Upper Whisker of b" + }, + { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "Lower Whisker of b" + }, + {"field": "a", "type": "nominal"} + ] + } + } + ] + } + ] + }, + { + "transform": [ + { + "aggregate": [ + {"op": "q1", "field": "b", "as": "lower_box_b"}, + {"op": "q3", "field": "b", "as": "upper_box_b"}, + {"op": "median", "field": "b", "as": "mid_box_b"}, + {"op": "min", "field": "b", "as": "min_b"}, + {"op": "max", "field": "b", "as": "max_b"} + ], + "groupby": ["a", "a"] + } + ], + "layer": [ + { + "mark": { + "type": "bar", + "size": 14, + "orient": "vertical", + "ariaRoleDescription": "box", + "style": "boxplot-box" + }, + "encoding": { + "y": {"field": "lower_box_b", "type": "quantitative", "title": "b"}, + "y2": {"field": "upper_box_b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "color": {"field": "a", "type": "nominal"}, + "tooltip": [ + {"field": "max_b", "type": "quantitative", "title": "Max of b"}, + { + "field": "upper_box_b", + "type": "quantitative", + "title": "Q3 of b" + }, + { + "field": "mid_box_b", + "type": "quantitative", + "title": "Median of b" + }, + { + "field": "lower_box_b", + "type": "quantitative", + "title": "Q1 of b" + }, + {"field": "min_b", "type": "quantitative", "title": "Min of b"}, + {"field": "a", "type": "nominal"} + ] + } + }, + { + "mark": { + "color": "white", + "type": "tick", + "size": 14, + "orient": "horizontal", + "aria": false, + "style": "boxplot-median" + }, + "encoding": { + "y": {"field": "mid_box_b", "type": "quantitative", "title": "b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "color": { + "condition": { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "field": "a", + "type": "nominal" + } + }, + "tooltip": [ + {"field": "max_b", "type": "quantitative", "title": "Max of b"}, + { + "field": "upper_box_b", + "type": "quantitative", + "title": "Q3 of b" + }, + { + "field": "mid_box_b", + "type": "quantitative", + "title": "Median of b" + }, + { + "field": "lower_box_b", + "type": "quantitative", + "title": "Q1 of b" + }, + {"field": "min_b", "type": "quantitative", "title": "Min of b"}, + {"field": "a", "type": "nominal"} + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/specs/normalized/boxplot_2D_vertical_single_per_group_normalized.vl.json b/examples/specs/normalized/boxplot_2D_vertical_single_per_group_normalized.vl.json new file mode 100644 index 0000000000..dc2969b67a --- /dev/null +++ b/examples/specs/normalized/boxplot_2D_vertical_single_per_group_normalized.vl.json @@ -0,0 +1,200 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [{"a": "A", "b": 28}, {"a": "A", "b": 30}, {"a": "B", "b": 55}] + }, + "layer": [ + { + "transform": [ + { + "joinaggregate": [ + {"op": "q1", "field": "b", "as": "lower_box_b"}, + {"op": "q3", "field": "b", "as": "upper_box_b"} + ], + "groupby": ["a"] + } + ], + "layer": [ + { + "transform": [ + { + "filter": "(datum[\"b\"] < datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"])) || (datum[\"b\"] > datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + } + ], + "mark": {"type": "point", "style": "boxplot-outliers"}, + "encoding": { + "y": {"field": "b", "type": "quantitative", "title": "b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}} + } + }, + { + "transform": [ + { + "filter": "(datum[\"lower_box_b\"] - 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]) <= datum[\"b\"]) && (datum[\"b\"] <= datum[\"upper_box_b\"] + 1.5 * (datum[\"upper_box_b\"] - datum[\"lower_box_b\"]))" + }, + { + "aggregate": [ + {"op": "min", "field": "b", "as": "lower_whisker_b"}, + {"op": "max", "field": "b", "as": "upper_whisker_b"}, + {"op": "min", "field": "lower_box_b", "as": "lower_box_b"}, + {"op": "max", "field": "upper_box_b", "as": "upper_box_b"} + ], + "groupby": ["a"] + } + ], + "layer": [ + { + "mark": {"type": "rule", "aria": false, "style": "boxplot-rule"}, + "encoding": { + "y": { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "b" + }, + "y2": {"field": "lower_box_b"}, + "x": { + "field": "a", + "type": "nominal", + "axis": {"labelAngle": 0} + }, + "tooltip": [ + { + "field": "upper_whisker_b", + "type": "quantitative", + "title": "Upper Whisker of b" + }, + { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "Lower Whisker of b" + }, + {"field": "a", "type": "nominal"} + ] + } + }, + { + "mark": {"type": "rule", "aria": false, "style": "boxplot-rule"}, + "encoding": { + "y": { + "field": "upper_box_b", + "type": "quantitative", + "title": "b" + }, + "y2": {"field": "upper_whisker_b"}, + "x": { + "field": "a", + "type": "nominal", + "axis": {"labelAngle": 0} + }, + "tooltip": [ + { + "field": "upper_whisker_b", + "type": "quantitative", + "title": "Upper Whisker of b" + }, + { + "field": "lower_whisker_b", + "type": "quantitative", + "title": "Lower Whisker of b" + }, + {"field": "a", "type": "nominal"} + ] + } + } + ] + } + ] + }, + { + "transform": [ + { + "aggregate": [ + {"op": "q1", "field": "b", "as": "lower_box_b"}, + {"op": "q3", "field": "b", "as": "upper_box_b"}, + {"op": "median", "field": "b", "as": "mid_box_b"}, + {"op": "min", "field": "b", "as": "min_b"}, + {"op": "max", "field": "b", "as": "max_b"} + ], + "groupby": ["a"] + } + ], + "layer": [ + { + "mark": { + "type": "bar", + "size": 14, + "orient": "vertical", + "ariaRoleDescription": "box", + "style": "boxplot-box" + }, + "encoding": { + "y": {"field": "lower_box_b", "type": "quantitative", "title": "b"}, + "y2": {"field": "upper_box_b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "tooltip": [ + {"field": "max_b", "type": "quantitative", "title": "Max of b"}, + { + "field": "upper_box_b", + "type": "quantitative", + "title": "Q3 of b" + }, + { + "field": "mid_box_b", + "type": "quantitative", + "title": "Median of b" + }, + { + "field": "lower_box_b", + "type": "quantitative", + "title": "Q1 of b" + }, + {"field": "min_b", "type": "quantitative", "title": "Min of b"}, + {"field": "a", "type": "nominal"} + ] + } + }, + { + "mark": { + "color": "white", + "type": "tick", + "size": 14, + "orient": "horizontal", + "aria": false, + "style": "boxplot-median" + }, + "encoding": { + "y": {"field": "mid_box_b", "type": "quantitative", "title": "b"}, + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "color": { + "condition": { + "test": "datum['lower_box_b'] >= datum['upper_box_b']", + "value": "#4c78a8" + } + }, + "tooltip": [ + {"field": "max_b", "type": "quantitative", "title": "Max of b"}, + { + "field": "upper_box_b", + "type": "quantitative", + "title": "Q3 of b" + }, + { + "field": "mid_box_b", + "type": "quantitative", + "title": "Median of b" + }, + { + "field": "lower_box_b", + "type": "quantitative", + "title": "Q1 of b" + }, + {"field": "min_b", "type": "quantitative", "title": "Min of b"}, + {"field": "a", "type": "nominal"} + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/specs/normalized/boxplot_groupped_normalized.vl.json b/examples/specs/normalized/boxplot_groupped_normalized.vl.json index b8e359d43d..668b0f1257 100644 --- a/examples/specs/normalized/boxplot_groupped_normalized.vl.json +++ b/examples/specs/normalized/boxplot_groupped_normalized.vl.json @@ -222,6 +222,13 @@ }, "x": {"field": "Cylinders", "type": "nominal"}, "xOffset": {"field": "Origin", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Acceleration'] >= datum['upper_box_Acceleration']", + "field": "Origin", + "type": "nominal" + } + }, "tooltip": [ { "field": "max_Acceleration", diff --git a/examples/specs/normalized/boxplot_minmax_2D_horizontal_custom_midtick_color_normalized.vl.json b/examples/specs/normalized/boxplot_minmax_2D_horizontal_custom_midtick_color_normalized.vl.json index 2e4c5232b2..3033c4bf40 100644 --- a/examples/specs/normalized/boxplot_minmax_2D_horizontal_custom_midtick_color_normalized.vl.json +++ b/examples/specs/normalized/boxplot_minmax_2D_horizontal_custom_midtick_color_normalized.vl.json @@ -174,6 +174,12 @@ "scale": {"zero": false} }, "y": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "upper_whisker_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_minmax_2D_horizontal_normalized.vl.json b/examples/specs/normalized/boxplot_minmax_2D_horizontal_normalized.vl.json index 0276d49ba5..2dc34b86b3 100644 --- a/examples/specs/normalized/boxplot_minmax_2D_horizontal_normalized.vl.json +++ b/examples/specs/normalized/boxplot_minmax_2D_horizontal_normalized.vl.json @@ -173,6 +173,12 @@ "scale": {"zero": false} }, "y": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "upper_whisker_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_minmax_2D_vertical_normalized.vl.json b/examples/specs/normalized/boxplot_minmax_2D_vertical_normalized.vl.json index 2e73bc3735..fbea4bbf68 100644 --- a/examples/specs/normalized/boxplot_minmax_2D_vertical_normalized.vl.json +++ b/examples/specs/normalized/boxplot_minmax_2D_vertical_normalized.vl.json @@ -174,6 +174,14 @@ "scale": {"zero": false} }, "x": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "field": "Species", + "type": "nominal", + "legend": null + } + }, "tooltip": [ { "field": "upper_whisker_Body Mass (g)", diff --git a/examples/specs/normalized/boxplot_tooltip_aggregate_normalized.vl.json b/examples/specs/normalized/boxplot_tooltip_aggregate_normalized.vl.json index a98791a2ab..3180a7c526 100644 --- a/examples/specs/normalized/boxplot_tooltip_aggregate_normalized.vl.json +++ b/examples/specs/normalized/boxplot_tooltip_aggregate_normalized.vl.json @@ -192,6 +192,12 @@ "title": "Mean of Body Mass (g)", "type": "quantitative", "field": "mean_Body Mass (g)" + }, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } } } } diff --git a/examples/specs/normalized/boxplot_tooltip_not_aggregate_normalized.vl.json b/examples/specs/normalized/boxplot_tooltip_not_aggregate_normalized.vl.json index 6284901903..e5259d3b63 100644 --- a/examples/specs/normalized/boxplot_tooltip_not_aggregate_normalized.vl.json +++ b/examples/specs/normalized/boxplot_tooltip_not_aggregate_normalized.vl.json @@ -218,6 +218,12 @@ "scale": {"zero": false} }, "y": {"field": "Species", "type": "nominal"}, + "color": { + "condition": { + "test": "datum['lower_box_Body Mass (g)'] >= datum['upper_box_Body Mass (g)']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_Body Mass (g)", diff --git a/examples/specs/normalized/layer_boxplot_circle_normalized.vl.json b/examples/specs/normalized/layer_boxplot_circle_normalized.vl.json index e389345385..4b9038e964 100644 --- a/examples/specs/normalized/layer_boxplot_circle_normalized.vl.json +++ b/examples/specs/normalized/layer_boxplot_circle_normalized.vl.json @@ -165,6 +165,12 @@ "title": "population" }, "y": {"field": "age", "type": "ordinal"}, + "color": { + "condition": { + "test": "datum['lower_box_people'] >= datum['upper_box_people']", + "value": "#4c78a8" + } + }, "tooltip": [ { "field": "max_people", diff --git a/src/compositemark/boxplot.ts b/src/compositemark/boxplot.ts index fde30dfa8c..d6e37c9efd 100644 --- a/src/compositemark/boxplot.ts +++ b/src/compositemark/boxplot.ts @@ -136,7 +136,17 @@ export function normalizeBoxPlot( const makeBoxPlotExtent = makeBoxPlotPart(encodingWithoutSizeColorAndContinuousAxis); const makeBoxPlotBox = makeBoxPlotPart(encodingWithoutContinuousAxis); - const makeBoxPlotMidTick = makeBoxPlotPart({...encodingWithoutSizeColorAndContinuousAxis, ...(size ? {size} : {})}); + const defaultBoxColor = (isObject(config.boxplot.box) ? config.boxplot.box.color : config.mark.color) || '#4c78a8'; + const makeBoxPlotMidTick = makeBoxPlotPart({ + ...encodingWithoutSizeColorAndContinuousAxis, + ...(size ? {size} : {}), + color: { + condition: { + test: `datum['lower_box_${continuousAxisChannelDef.field}'] >= datum['upper_box_${continuousAxisChannelDef.field}']`, + ...(color || {value: defaultBoxColor}) + } + } + }); const fiveSummaryTooltipEncoding: Encoding = getCompositeMarkTooltip( [