Vega-Lite混合图表问题:分组柱状图样本点异常及API转换需求
问题解决:Vega-Lite混合图表样本点匹配错误及API转换
问题原因
当前配置通过多次fold操作生成了数据的笛卡尔积,每个day + Category组合都关联了所有9个样本点,导致每个柱状图上显示全部样本。需要重新转换数据,让每个样本点和对应的分类(RAMOS/PBMC/RAMOS_PBMC)一一绑定。
修正后的Vega-Lite配置
通过两次fold将嵌套数据展开为扁平结构,再通过过滤区分样本点与中位数:
{ "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": { "values": [ {"key":"Ctrl","RAMOS":{"SAMPLE_1":2382,"SAMPLE_2":1998,"SAMPLE_3":2233,"MEDIAN":2233},"PBMC":{"SAMPLE_1":2,"SAMPLE_2":0,"SAMPLE_3":0,"MEDIAN":0},"RAMOS_PBMC":{"SAMPLE_1":213,"SAMPLE_2":240,"SAMPLE_3":153,"MEDIAN":213},"day":1,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":4151,"SAMPLE_2":4451,"SAMPLE_3":4662,"MEDIAN":4451},"PBMC":{"SAMPLE_1":6,"SAMPLE_2":1,"SAMPLE_3":0,"MEDIAN":1},"RAMOS_PBMC":{"SAMPLE_1":178,"SAMPLE_2":140,"SAMPLE_3":173,"MEDIAN":173},"day":2,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":11195,"SAMPLE_2":12059,"SAMPLE_3":12089,"MEDIAN":12059},"PBMC":{"SAMPLE_1":16,"SAMPLE_2":1,"SAMPLE_3":1,"MEDIAN":1},"RAMOS_PBMC":{"SAMPLE_1":278,"SAMPLE_2":274,"SAMPLE_3":299,"MEDIAN":278},"day":3,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":17931,"SAMPLE_2":21797,"SAMPLE_3":23520,"MEDIAN":21797},"PBMC":{"SAMPLE_1":23,"SAMPLE_2":20,"SAMPLE_3":8,"MEDIAN":20},"RAMOS_PBMC":{"SAMPLE_1":810,"SAMPLE_2":467,"SAMPLE_3":858,"MEDIAN":810},"day":4,"R":4} ] }, "transform": [ { "fold": ["RAMOS", "PBMC", "RAMOS_PBMC"], "as": ["Category", "Data"] }, { "fold": ["Data.SAMPLE_1", "Data.SAMPLE_2", "Data.SAMPLE_3", "Data.MEDIAN"], "as": ["Type", "Value"] }, { "calculate": "split(datum.Type, '.')[1]", "as": "SubType" }, { "calculate": "replace(datum.Category, '_', ' ')", "as": "CategoryLabel" } ], "layer": [ { "mark": "bar", "transform": [{"filter": "datum.SubType === 'MEDIAN'"}], "encoding": { "x": {"field": "day", "type": "ordinal", "axis": {"title": "Day"}}, "y": {"field": "Value", "type": "quantitative", "scale": {"type": "sqrt"}, "axis": {"title": "Cell Count"}}, "color": { "field": "CategoryLabel", "type": "nominal", "scale": {"range": ["#1f77b4", "#ff7f0e", "#2ca02c"]}, "legend": {"title": "Category"} }, "xOffset": {"field": "CategoryLabel"} } }, { "mark": {"type": "point", "filled": true, "size": 80}, "transform": [{"filter": "datum.SubType !== 'MEDIAN'"}], "encoding": { "x": {"field": "day", "type": "ordinal"}, "y": {"field": "Value", "type": "quantitative"}, "color": {"field": "CategoryLabel", "type": "nominal"}, "xOffset": {"field": "CategoryLabel"}, "tooltip": [ {"field": "CategoryLabel", "title": "Category"}, {"field": "SubType", "title": "Sample"}, {"field": "Value", "title": "Cell Count"} ] } } ] }
Vega-Lite API TypeScript实现
以下是适配TypeScript项目的链式调用代码:
import { vl } from 'vega-lite-api'; const chart = vl .data([ {"key":"Ctrl","RAMOS":{"SAMPLE_1":2382,"SAMPLE_2":1998,"SAMPLE_3":2233,"MEDIAN":2233},"PBMC":{"SAMPLE_1":2,"SAMPLE_2":0,"SAMPLE_3":0,"MEDIAN":0},"RAMOS_PBMC":{"SAMPLE_1":213,"SAMPLE_2":240,"SAMPLE_3":153,"MEDIAN":213},"day":1,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":4151,"SAMPLE_2":4451,"SAMPLE_3":4662,"MEDIAN":4451},"PBMC":{"SAMPLE_1":6,"SAMPLE_2":1,"SAMPLE_3":0,"MEDIAN":1},"RAMOS_PBMC":{"SAMPLE_1":178,"SAMPLE_2":140,"SAMPLE_3":173,"MEDIAN":173},"day":2,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":11195,"SAMPLE_2":12059,"SAMPLE_3":12089,"MEDIAN":12059},"PBMC":{"SAMPLE_1":16,"SAMPLE_2":1,"SAMPLE_3":1,"MEDIAN":1},"RAMOS_PBMC":{"SAMPLE_1":278,"SAMPLE_2":274,"SAMPLE_3":299,"MEDIAN":278},"day":3,"R":4}, {"key":"Ctrl","RAMOS":{"SAMPLE_1":17931,"SAMPLE_2":21797,"SAMPLE_3":23520,"MEDIAN":21797},"PBMC":{"SAMPLE_1":23,"SAMPLE_2":20,"SAMPLE_3":8,"MEDIAN":20},"RAMOS_PBMC":{"SAMPLE_1":810,"SAMPLE_2":467,"SAMPLE_3":858,"MEDIAN":810},"day":4,"R":4} ]) .transform( vl.fold(["RAMOS", "PBMC", "RAMOS_PBMC"]).as(["Category", "Data"]), vl.fold(["Data.SAMPLE_1", "Data.SAMPLE_2", "Data.SAMPLE_3", "Data.MEDIAN"]).as(["Type", "Value"]), vl.calculate("split(datum.Type, '.')[1]").as("SubType"), vl.calculate("replace(datum.Category, '_', ' ')").as("CategoryLabel") ) .layer( vl.markBar() .transform(vl.filter("datum.SubType === 'MEDIAN'")) .encode( vl.x().fieldOrdinal("day").axis({ title: "Day" }), vl.y().fieldQ("Value").scale({ type: "sqrt" }).axis({ title: "Cell Count" }), vl.color().fieldN("CategoryLabel").scale({ range: ["#1f77b4", "#ff7f0e", "#2ca02c"] }).legend({ title: "Category" }), vl.xOffset().fieldN("CategoryLabel") ), vl.markPoint({ filled: true, size: 80 }) .transform(vl.filter("datum.SubType !== 'MEDIAN'")) .encode( vl.x().fieldOrdinal("day"), vl.y().fieldQ("Value"), vl.color().fieldN("CategoryLabel"), vl.xOffset().fieldN("CategoryLabel"), vl.tooltip() .fieldN("CategoryLabel", "Category") .fieldN("SubType", "Sample") .fieldQ("Value", "Cell Count") ) ) .width(600) .height(400); // 渲染到指定DOM元素 chart.render('#vis');
关键修改说明
- 数据结构优化:两次
fold操作将嵌套数据展开为扁平结构,每个条目包含分类、数据类型(样本/中位数)和对应数值,避免生成笛卡尔积。 - 精准过滤:对柱状图和散点图分别设置过滤条件,确保仅显示对应类型的数据。
- 可读性优化:将
RAMOS_PBMC转换为RAMOS PBMC,提升图表标签可读性。 - API语法对齐:使用Vega-Lite API的链式调用语法,符合TypeScript项目的编码习惯。
内容的提问来源于stack exchange,提问作者cSharp
相关产品推荐
相关产品推荐

