如何用下拉框切换Vega可视化中的分组与着色字段?
问题描述
需要在Vega中创建堆叠面积图,通过下拉框切换用于着色的分组字段,可在「Cylinders」「Horsepower」「Acceleration」三者间切换。尝试将fill字段绑定到下拉框信号但未生效,同时需要同步更新颜色比例尺和聚合字段,询问实现方法。
原代码如下:
{ "$schema": "https://vega.github.io/schema/vega/v6.json", "autosize": {"type": "fit-x", "contains": "padding"}, "background": "white", "padding": 5, "height": 250, "style": "cell", "data": [ {"name": "Group_By_store"}, { "name": "source_0", "url": "data/cars.json", "format": {"type": "json"}, "transform": [ { "type": "aggregate", "groupby": ["Origin", "Cylinders"], "ops": ["count"], "fields": [null], "as": ["__count"] }, { "type": "stack", "groupby": ["Origin"], "field": "__count", "sort": {"field": ["Cylinders"], "order": ["descending"]}, "as": ["__count_start", "__count_end"], "offset": "zero" } ] } ], "signals": [ { "name": "width", "init": "isFinite(containerSize()[0]) ? containerSize()[0] : 300", "on": [ { "update": "isFinite(containerSize()[0]) ? containerSize()[0] : 300", "events": "window:resize" } ] }, { "name": "unit", "value": {}, "on": [ {"events": "pointermove", "update": "isTuple(group()) ? group() : unit"} ] }, { "name": "Group_By", "value": null, "bind": { "input": "select", "options": ["Cylinders", "Horsepower", "Acceleration"], "labels": ["Cylinders", "Horsepower", "Acceleration"] } } ], "marks": [ { "name": "marks", "type": "rect", "style": ["bar"], "interactive": true, "from": {"data": "source_0"}, "encode": { "update": { "fill": { "scale": "color_by_cylinders", "field": {"signal": "Group_By"} }, "ariaRoleDescription": {"value": "bar"}, "description": { "signal": "\"Origin: \" + (isValid(datum[\"Origin\"]) ? datum[\"Origin\"] : \"\"+datum[\"Origin\"]) + \"; Number of Cars: \" + (format(datum[\"__count\"], \"\")) + \"; Cylinders: \" + (isValid(datum[\"Cylinders\"]) ? datum[\"Cylinders\"] : \"\"+datum[\"Cylinders\"])", }, "x": {"scale": "x", "field": "Origin"}, "width": {"signal": "max(0.25, bandwidth('x'))"}, "y": {"scale": "y", "field": "__count_end"}, "y2": {"scale": "y", "field": "__count_start"} } } } ], "scales": [ { "name": "x", "type": "band", "domain": {"data": "source_0", "field": "Origin", "sort": true}, "range": [0, {"signal": "width"}], "paddingInner": 0.1, "paddingOuter": 0.05 }, { "name": "y", "type": "linear", "domain": { "data": "source_0", "fields": ["__count_start", "__count_end"] }, "range": [{"signal": "height"}, 0], "nice": true, "zero": true }, { "name": "color_by_horsepower", "type": "ordinal", "domain": {"data": "source_0", "field": "Cylinders", "sort": true}, "range": "category" }, { "name": "color_by_acceleration", "type": "ordinal", "domain": {"data": "source_0", "field": "Cylinders", "sort": true}, "range": "category" }, { "name": "color_by_cylinders", "type": "ordinal", "domain": {"data": "source_0", "field": "Cylinders", "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": "Origin", "labelAlign": "right", "labelAngle": 270, "labelBaseline": "middle", "zindex": 0 }, { "scale": "y", "orient": "left", "grid": false, "title": "Number of Cars", "labelOverlap": true, "tickCount": {"signal": "ceil(height/40)"}, "zindex": 0 } ], "legends": [ {"fill": "color_by_cylinders", "symbolType": "square", "title": "Cylinders"} ], "config": {} }
解决方案
要实现动态切换分组字段,核心是让聚合、堆叠、颜色比例尺、图例都响应下拉框信号的变化,具体修改点如下:
1. 动态聚合与堆叠
将数据转换中的groupby、sort字段替换为信号引用,让聚合和堆叠逻辑随Group_By信号变化:
- 在
aggregate变换中,把groupby改为["Origin", {"signal": "Group_By"}] - 在
stack变换中,把sort.field改为[{"signal": "Group_By"}]
2. 统一颜色比例尺
无需创建多个颜色比例尺,只保留一个动态比例尺,其domain字段绑定到Group_By信号:
{ "name": "color", "type": "ordinal", "domain": {"data": "source_0", "field": {"signal": "Group_By"}, "sort": true}, "range": "category" }
3. 同步图例与标记填充
- 标记的
fill属性使用统一的color比例尺,字段引用Group_By信号 - 图例的
fill绑定到color比例尺,标题通过信号动态生成:{"signal": "Group_By"}
4. 更新交互描述
将description信号中的固定字段改为动态引用Group_By,确保 tooltip 内容准确:
"description": { "signal": "\"Origin: \" + (isValid(datum[\"Origin\"]) ? datum[\"Origin\"] : \"\"+datum[\"Origin\"]) + \"; Number of Cars: \" + (format(datum[\"__count\"], \"\")) + \"; \" + Group_By + \": \" + (isValid(datum[Group_By]) ? datum[Group_By] : \"\"+datum[Group_By])" }
修改后的完整代码
{ "$schema": "https://vega.github.io/schema/vega/v6.json", "autosize": {"type": "fit-x", "contains": "padding"}, "background": "white", "padding": 5, "height": 250, "style": "cell", "data": [ { "name": "source_0", "url": "data/cars.json", "format": {"type": "json"}, "transform": [ { "type": "aggregate", "groupby": ["Origin", {"signal": "Group_By"}], "ops": ["count"], "fields": [null], "as": ["__count"] }, { "type": "stack", "groupby": ["Origin"], "field": "__count", "sort": {"field": [{"signal": "Group_By"}], "order": ["descending"]}, "as": ["__count_start", "__count_end"], "offset": "zero" } ] } ], "signals": [ { "name": "width", "init": "isFinite(containerSize()[0]) ? containerSize()[0] : 300", "on": [ { "update": "isFinite(containerSize()[0]) ? containerSize()[0] : 300", "events": "window:resize" } ] }, { "name": "unit", "value": {}, "on": [ {"events": "pointermove", "update": "isTuple(group()) ? group() : unit"} ] }, { "name": "Group_By", "value": "Cylinders", "bind": { "input": "select", "options": ["Cylinders", "Horsepower", "Acceleration"], "labels": ["Cylinders", "Horsepower", "Acceleration"] } } ], "marks": [ { "name": "marks", "type": "rect", "style": ["bar"], "interactive": true, "from": {"data": "source_0"}, "encode": { "update": { "fill": { "scale": "color", "field": {"signal": "Group_By"} }, "ariaRoleDescription": {"value": "bar"}, "description": { "signal": "\"Origin: \" + (isValid(datum[\"Origin\"]) ? datum[\"Origin\"] : \"\"+datum[\"Origin\"]) + \"; Number of Cars: \" + (format(datum[\"__count\"], \"\")) + \"; \" + Group_By + \": \" + (isValid(datum[Group_By]) ? datum[Group_By] : \"\"+datum[Group_By])" }, "x": {"scale": "x", "field": "Origin"}, "width": {"signal": "max(0.25, bandwidth('x'))"}, "y": {"scale": "y", "field": "__count_end"}, "y2": {"scale": "y", "field": "__count_start"} } } } ], "scales": [ { "name": "x", "type": "band", "domain": {"data": "source_0", "field": "Origin", "sort": true}, "range": [0, {"signal": "width"}], "paddingInner": 0.1, "paddingOuter": 0.05 }, { "name": "y", "type": "linear", "domain": { "data": "source_0", "fields": ["__count_start", "__count_end"] }, "range": [{"signal": "height"}, 0], "nice": true, "zero": true }, { "name": "color", "type": "ordinal", "domain": {"data": "source_0", "field": {"signal": "Group_By"}, "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": "Origin", "labelAlign": "right", "labelAngle": 270, "labelBaseline": "middle", "zindex": 0 }, { "scale": "y", "orient": "left", "grid": false, "title": "Number of Cars", "labelOverlap": true, "tickCount": {"signal": "ceil(height/40)"}, "zindex": 0 } ], "legends": [ {"fill": "color", "symbolType": "square", "title": {"signal": "Group_By"}} ], "config": {} }
内容的提问来源于stack exchange,提问作者Jay Askren
相关产品推荐
相关产品推荐

