You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Vega小提琴图坐标轴转换失败解决方案咨询

Vega小提琴图坐标轴转换实现方案

问题背景

尝试转换Vega小提琴图的坐标轴(将横向分布改为纵向),直接交换xscale与hscale后图表无法显示或画布为空,原始代码如下:

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A violin plot example showing distributions for pengiun body mass.",
  "width": 500,
  "padding": 5,

  "config": {
    "axisBand": {
      "bandPosition": 1,
      "tickExtra": true,
      "tickOffset": 0
    }
  },

  "signals": [
    { "name": "plotWidth", "value": 60 },
    { "name": "height", "update": "(plotWidth + 10) * 3"},
    { "name": "trim", "value": true,
      "bind": {"input": "checkbox"} },
    { "name": "bandwidth", "value": 0,
      "bind": {"input": "range", "min": 0, "max": 200, "step": 1} }
  ],

  "data": [
    {
      "name": "penguins",
      "url": "data/penguins.json",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.Species != null && datum['Body Mass (g)'] != null"
        }
      ]
    },
    {
      "name": "density",
      "source": "penguins",
      "transform": [
        {
          "type": "kde",
          "field": "Body Mass (g)",
          "groupby": ["Species"],
          "bandwidth": {"signal": "bandwidth"},
          "extent": {"signal": "trim ? null : [2000, 6500]"}
        }
      ]
    },
    {
      "name": "stats",
      "source": "penguins",
      "transform": [
        {
          "type": "aggregate",
          "groupby": ["Species"],
          "fields": ["Body Mass (g)", "Body Mass (g)", "Body Mass (g)"],
          "ops": ["q1", "median", "q3"],
          "as": ["q1", "median", "q3"]
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "layout",
      "type": "band",
      "range": "height",
      "domain": {"data": "penguins", "field": "Species"}
    },
    {
      "name": "xscale",
      "type": "linear",
      "range": "width", "round": true,
      "domain": {"data": "penguins", "field": "Body Mass (g)"},
      "domainMin": 2000,
      "zero": false, "nice": true
    },
    {
      "name": "hscale",
      "type": "linear",
      "range": [0, {"signal": "plotWidth"}],
      "domain": {"data": "density", "field": "density"}
    },
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "penguins", "field": "Species"},
      "range": "category"
    }
  ],

  "axes": [
    {"orient": "bottom", "scale": "xscale", "zindex": 1},
    {"orient": "left", "scale": "layout", "tickCount": 5, "zindex": 1}
  ],

  "marks": [
    {
      "type": "group",
      "from": {
        "facet": {
          "data": "density",
          "name": "violin",
          "groupby": "Species"
        }
      },

      "encode": {
        "enter": {
          "yc": {"scale": "layout", "field": "Species", "band": 0.5},
          "height": {"signal": "plotWidth"},
          "width": {"signal": "width"}
        }
      },

      "data": [
        {
          "name": "summary",
          "source": "stats",
          "transform": [
            {
              "type": "filter",
              "expr": "datum.Species === parent.Species"
            }
          ]
        }
      ],

      "marks": [
        {
          "type": "area",
          "from": {"data": "violin"},
          "encode": {
            "enter": {
              "fill": {"scale": "color", "field": {"parent": "Species"}}
            },
            "update": {
              "x": {"scale": "xscale", "field": "value"},
              "yc": {"signal": "plotWidth / 2"},
              "height": {"scale": "hscale", "field": "density"}
            }
          }
        },
        {
          "type": "rect",
          "from": {"data": "summary"},
          "encode": {
            "enter": {
              "fill": {"value": "black"},
              "height": {"value": 2}
            },
            "update": {
              "x": {"scale": "xscale", "field": "q1"},
              "x2": {"scale": "xscale", "field": "q3"},
              "yc": {"signal": "plotWidth / 2"}
            }
          }
        },
        {
          "type": "rect",
          "from": {"data": "summary"},
          "encode": {
            "enter": {
              "fill": {"value": "black"},
              "width": {"value": 2},
              "height": {"value": 8}
            },
            "update": {
              "x": {"scale": "xscale", "field": "median"},
              "yc": {"signal": "plotWidth / 2"}
            }
          }
        }
      ]
    }
  ]
}

解决方案

坐标轴转换不能仅交换刻度,需要同步调整比例尺范围、标记坐标映射、容器尺寸等多个环节,以下是修改后的完整代码:

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "Vertical violin plot showing penguin body mass distributions.",
  "height": 500,
  "padding": 5,

  "config": {
    "axisBand": {
      "bandPosition": 1,
      "tickExtra": true,
      "tickOffset": 0
    }
  },

  "signals": [
    { "name": "plotHeight", "value": 60 },
    { "name": "width", "update": "(plotHeight + 10) * 3"},
    { "name": "trim", "value": true,
      "bind": {"input": "checkbox"} },
    { "name": "bandwidth", "value": 0,
      "bind": {"input": "range", "min": 0, "max": 200, "step": 1} }
  ],

  "data": [
    {
      "name": "penguins",
      "url": "data/penguins.json",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.Species != null && datum['Body Mass (g)'] != null"
        }
      ]
    },
    {
      "name": "density",
      "source": "penguins",
      "transform": [
        {
          "type": "kde",
          "field": "Body Mass (g)",
          "groupby": ["Species"],
          "bandwidth": {"signal": "bandwidth"},
          "extent": {"signal": "trim ? null : [2000, 6500]"}
        }
      ]
    },
    {
      "name": "stats",
      "source": "penguins",
      "transform": [
        {
          "type": "aggregate",
          "groupby": ["Species"],
          "fields": ["Body Mass (g)", "Body Mass (g)", "Body Mass (g)"],
          "ops": ["q1", "median", "q3"],
          "as": ["q1", "median", "q3"]
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "layout",
      "type": "band",
      "range": "width",
      "domain": {"data": "penguins", "field": "Species"}
    },
    {
      "name": "yscale",
      "type": "linear",
      "range": "height", "round": true,
      "domain": {"data": "penguins", "field": "Body Mass (g)"},
      "domainMin": 2000,
      "zero": false, "nice": true
    },
    {
      "name": "wscale",
      "type": "linear",
      "range": [0, {"signal": "plotHeight"}],
      "domain": {"data": "density", "field": "density"}
    },
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "penguins", "field": "Species"},
      "range": "category"
    }
  ],

  "axes": [
    {"orient": "left", "scale": "yscale", "zindex": 1},
    {"orient": "bottom", "scale": "layout", "tickCount": 5, "zindex": 1}
  ],

  "marks": [
    {
      "type": "group",
      "from": {
        "facet": {
          "data": "density",
          "name": "violin",
          "groupby": "Species"
        }
      },

      "encode": {
        "enter": {
          "xc": {"scale": "layout", "field": "Species", "band": 0.5},
          "width": {"signal": "plotHeight"},
          "height": {"signal": "height"}
        }
      },

      "data": [
        {
          "name": "summary",
          "source": "stats",
          "transform": [
            {
              "type": "filter",
              "expr": "datum.Species === parent.Species"
            }
          ]
        }
      ],

      "marks": [
        {
          "type": "area",
          "from": {"data": "violin"},
          "encode": {
            "enter": {
              "fill": {"scale": "color", "field": {"parent": "Species"}}
            },
            "update": {
              "y": {"scale": "yscale", "field": "value"},
              "xc": {"signal": "plotHeight / 2"},
              "width": {"scale": "wscale", "field": "density"}
            }
          }
        },
        {
          "type": "rect",
          "from": {"data": "summary"},
          "encode": {
            "enter": {
              "fill": {"value": "black"},
              "width": {"value": 2}
            },
            "update": {
              "y": {"scale": "yscale", "field": "q1"},
              "y2": {"scale": "yscale", "field": "q3"},
              "xc": {"signal": "plotHeight / 2"}
            }
          }
        },
        {
          "type": "rect",
          "from": {"data": "summary"},
          "encode": {
            "enter": {
              "fill": {"value": "black"},
              "height": {"value": 2},
              "width": {"value": 8}
            },
            "update": {
              "y": {"scale": "yscale", "field": "median"},
              "xc": {"signal": "plotHeight / 2"}
            }
          }
        }
      ]
    }
  ]
}

关键修改点说明

  • 信号与尺寸:将plotWidth改为plotHeight,同步调整width的计算逻辑,交换画布初始的width和height值
  • 布局比例尺:layout的range从height改为width,实现物种标签横向排列
  • 数值比例尺:将xscale重命名为yscale,range改为height,对应纵向的体重轴;将hscale重命名为wscale,range对应横向的密度宽度
  • 坐标轴方向:交换axes的orient和对应比例尺,让体重轴在左侧、物种轴在底部
  • 标记映射:所有x/height属性替换为y/width,yc替换为xc,确保密度区域和统计线的坐标方向正确

Vega坐标轴管理指南要点

Vega的坐标轴与比例尺强绑定,核心规则包括:

  • 每个坐标轴必须关联一个比例尺,比例尺的range决定了坐标轴的空间方向
  • 坐标轴的orient属性控制显示位置(top/bottom/left/right),需与比例尺的range匹配(如range: width对应水平轴,range: height对应垂直轴)
  • 调整坐标轴方向时,必须同步修改:
    • 对应比例尺的range范围
    • 标记图形的坐标映射(x/y、width/height等属性)
    • 画布或分组容器的尺寸逻辑
  • 官方文档中关于坐标轴的核心内容涵盖:比例尺类型与坐标轴的适配、坐标轴样式配置、动态坐标轴绑定等,可通过Vega官方手册的「Axes」章节查阅详细规则

内容的提问来源于stack exchange,提问作者Goldjunge3010

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:27:31