动态列数中的 Vega 图中心栏

问题描述 投票:0回答:1

我正在尝试在 Vega 中制作一个聚集柱形图,其中如果类别中的值是正数和负数,它们将堆叠在一列中,但如果类别中的值都是正数或都是负数,那么它们必须并排显示而不是堆叠显示。

但是,列定位并不总是居中。设置明确的偏移量是可行的,但如果列数发生变化,则它将不会再次居中。有谁知道我如何让它变得动态..可能需要计算列宽

更多列 - 使用偏移几乎居中

少列 - 条不居中

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "width": 700,
  "height": 370,
  "autosize": {"type": "fit", "contains": "padding"},
  "padding": {"bottom": 2, "right": 9, "left": 5},

  "config": {
    "axis": {
      "labelFont": "HelveticaNeueLTW01-55Roman",
      "titleFont": "HelveticaNeueLTW01-55Roman"
    }
  },
  "data": [
    {
      "name": "income_and_expenses",
      "values": [
        {"label": "2012", "value1": 208.91, "value2": 500},
        {"label": "2013", "value1": 115.61, "value2": -458.76},
        {"label": "2014", "value1": 135.7, "value2": -335.23},
        {"label": "2015", "value1": 148.75, "value2": -83.61},
        {"label": "2016", "value1": 152.57, "value2": 0.36},
        {"label": "2017", "value1": 198.23, "value2": 0.29},
        {"label": "2018", "value1": 911.81, "value2": 0.58},
        {"label": "2019", "value1": 342.44, "value2": 14.48},
        {"label": "2020", "value1": 664.46, "value2": 11.51},

 
      ],
      "transform": [
        {"type": "formula", "expr": "split(datum.label, ' ')", "as": "label"}
      ]
    },
    {
      "name": "parsed",
      "source": ["income_and_expenses"],
      "transform": [
        {
          "type": "aggregate",
          "fields": ["value1", "value1", "value2", "value2"],
          "ops": ["min", "max", "min", "max"],
          "as": ["min_value1", "max_value1", "min_value2", "max_value2"]
        },
        {
          "type": "formula",
          "expr": "datum.max_value1 > datum.max_value2 ? abs(datum.max_value1) : abs(datum.max_value2)",
          "as": "mergedMax"
        },
        {
          "type": "formula",
          "expr": "datum.min_value1 < datum.min_value2 ? abs(datum.min_value1) : abs(datum.min_value2)",
          "as": "mergedMin"
        },
        {
          "type": "formula",
          "expr": "datum.mergedMax > datum.mergedMin ? datum.mergedMax : datum.mergedMin",
          "as": "max"
        },
        {"type": "formula", "expr": "-(datum.max)", "as": "min"}
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "band",
      "range": "width",
      "domain": {"data": "income_and_expenses", "field": "label"}
    },
    {
      "name": "y",
      "type": "linear",
      "range": "height",
      "nice": true,
      "zero": true,
      "domain": {"data": "parsed", "field": "limit"},
      "domainMin": {"signal": "pluck(data('parsed'), 'min')"},
      "domainMax": {"signal": "pluck(data('parsed'), 'max')"}
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "x",
      "zindex": 1,
      "labelFontSize": 14,
      "labelColor": "#85868C",
      "labelAlign": "center",
      "labelLineHeight": 19.6,
      "labelFontWeight": "normal",
      "labelPadding": 8,
      "labelOpacity": 1,
      "domain": false,
      "domainColor": "#e5e5e5",
      "domainWidth": 1,
      "ticks": false,
      "grid": true
    },
    {
      "orient": "left",
      "scale": "y",
      "zindex": 0,
      "labelFontSize": 14,
      "labelColor": "#85868C",
      "labelLineHeight": 19.6,
      "labelFontWeight": "normal",
      "labelPadding": 8,
      "labelOpacity": 1,
      "domain": false,
      "domainColor": "#e5e5e5",
      "grid": true,
      "gridOpacity": 0.5,
      "ticks": false,
      "tickCount": 4,
      "gridDash": {"signal": "datum.value == 0 ? [] : [4,4]"},
      "encode": {
        "labels": {
          "update": {
            "text": {
              "signal": "if(abs(datum.value) >= 1e9, format(datum.value/1e9, '$,.1~f') + 'B', if(abs(datum.value) >= 1e6, format(datum.value/1e6, '$,.1~f') + 'M', if(abs(datum.value) >= 1e3, format(datum.value/1e3, '$,.1~f') + 'K', format(datum.value, '$,.2~f'))))"
            }
          }
        }
      }
    }
  ],
  "marks": [
    {
      "name": "income",
      "type": "rect",
      "from": {"data": "income_and_expenses"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "label", "band": 0.34},
          "width": {"value": 16},
          "y": {"scale": "y", "field": "value1"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "#68abf2"},
          "cornerRadiusTopLeft": {"signal": "datum.value1 > 0 ? '2' : '0'"},
          "cornerRadiusTopRight": {"signal": "datum.value1 > 0 ? '2' : '0'"},
          "cornerRadiusBottomLeft": {"signal": "datum.value1 > 0 ? '0' : '2'"},
          "cornerRadiusBottomRight": {"signal": "datum.value1 > 0 ? '0' : '2'"}
        }
      }
    },
    {
      "name": "expenses",
      "type": "rect",
      "from": {"data": "income_and_expenses"},
      "encode": {
        "enter": {
          "x": {
            "scale": "x",
            "field": "label",
            "band": 0.34,
            "offset": {
              "signal": "datum.value1 > 0 && datum.value2 > 0  || datum.value1 < 0 && datum.value2 < 0 ? 20 : 0"
            }
          },
          "width": {"value": 16},
          "y": {"scale": "y", "field": "value2"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "#f7615c"},
          "cornerRadiusTopLeft": {"signal": "datum.value2 > 0 ? '2' : '0'"},
          "cornerRadiusTopRight": {"signal": "datum.value2 > 0 ? '2' : '0'"},
          "cornerRadiusBottomLeft": {"signal": "datum.value2 > 0 ? '0' : '2'"},
          "cornerRadiusBottomRight": {"signal": "datum.value2 > 0 ? '0' : '2'"}
        }
      }
    }
  ]
}
json charts visualization vega-lite vega
1个回答
0
投票

你的意思是这样吗?

enter image description here

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "width": 700,
  "height": 370,
  "autosize": {"type": "fit", "contains": "padding"},
  "padding": {"bottom": 2, "right": 9, "left": 5},
  "config": {
    "axis": {
      "labelFont": "HelveticaNeueLTW01-55Roman",
      "titleFont": "HelveticaNeueLTW01-55Roman"
    }
  },
  "data": [
    {
      "name": "income_and_expenses",
      "values": [
        {"label": "2012", "value1": 208.91, "value2": 500},
        {"label": "2013", "value1": 115.61, "value2": -458.76},
        {"label": "2014", "value1": 135.7, "value2": -335.23},
        {"label": "2015", "value1": 148.75, "value2": -83.61},
        {"label": "2016", "value1": 152.57, "value2": 0.36},
        {"label": "2017", "value1": 198.23, "value2": 0.29},
        {"label": "2018", "value1": 911.81, "value2": 0.58},
        {"label": "2019", "value1": 342.44, "value2": 14.48},
        {"label": "2020", "value1": 664.46, "value2": 11.51}
      ],
      "transform": [
        {"type": "formula", "expr": "split(datum.label, ' ')", "as": "label"}
      ]
    },
    {
      "name": "parsed",
      "source": ["income_and_expenses"],
      "transform": [
        {
          "type": "aggregate",
          "fields": ["value1", "value1", "value2", "value2"],
          "ops": ["min", "max", "min", "max"],
          "as": ["min_value1", "max_value1", "min_value2", "max_value2"]
        },
        {
          "type": "formula",
          "expr": "datum.max_value1 > datum.max_value2 ? abs(datum.max_value1) : abs(datum.max_value2)",
          "as": "mergedMax"
        },
        {
          "type": "formula",
          "expr": "datum.min_value1 < datum.min_value2 ? abs(datum.min_value1) : abs(datum.min_value2)",
          "as": "mergedMin"
        },
        {
          "type": "formula",
          "expr": "datum.mergedMax > datum.mergedMin ? datum.mergedMax : datum.mergedMin",
          "as": "max"
        },
        {"type": "formula", "expr": "-(datum.max)", "as": "min"}
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "band",
      "range": "width",
      "domain": {"data": "income_and_expenses", "field": "label"}
    },
    {
      "name": "y",
      "type": "linear",
      "range": "height",
      "nice": true,
      "zero": true,
      "domain": {"data": "parsed", "field": "limit"},
      "domainMin": {"signal": "pluck(data('parsed'), 'min')"},
      "domainMax": {"signal": "pluck(data('parsed'), 'max')"}
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "x",
      "zindex": 1,
      "labelFontSize": 14,
      "labelColor": "#85868C",
      "labelAlign": "center",
      "labelLineHeight": 19.6,
      "labelFontWeight": "normal",
      "labelPadding": 8,
      "labelOpacity": 1,
      "domain": false,
      "domainColor": "#e5e5e5",
      "domainWidth": 1,
      "ticks": false,
      "grid": true
    },
    {
      "orient": "left",
      "scale": "y",
      "zindex": 0,
      "labelFontSize": 14,
      "labelColor": "#85868C",
      "labelLineHeight": 19.6,
      "labelFontWeight": "normal",
      "labelPadding": 8,
      "labelOpacity": 1,
      "domain": false,
      "domainColor": "#e5e5e5",
      "grid": true,
      "gridOpacity": 0.5,
      "ticks": false,
      "tickCount": 4,
      "gridDash": {"signal": "datum.value == 0 ? [] : [4,4]"},
      "encode": {
        "labels": {
          "update": {
            "text": {
              "signal": "if(abs(datum.value) >= 1e9, format(datum.value/1e9, '$,.1~f') + 'B', if(abs(datum.value) >= 1e6, format(datum.value/1e6, '$,.1~f') + 'M', if(abs(datum.value) >= 1e3, format(datum.value/1e3, '$,.1~f') + 'K', format(datum.value, '$,.2~f'))))"
            }
          }
        }
      }
    }
  ],
  "marks": [
    {
      "name": "income",
      "type": "rect",
      "from": {"data": "income_and_expenses"},
      "encode": {
        "enter": {
          "xc": {
            "scale": "x",
            "field": "label",
            "band": 0.5,
            "offset": {
              "signal": "datum.value1 > 0 && datum.value2 > 0  || datum.value1 < 0 && datum.value2 < 0 ? -8 : 0"
            }
          },
          "width": {"value": 16},
          "y": {"scale": "y", "field": "value1"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "#68abf2"},
          "cornerRadiusTopLeft": {"signal": "datum.value1 > 0 ? '2' : '0'"},
          "cornerRadiusTopRight": {"signal": "datum.value1 > 0 ? '2' : '0'"},
          "cornerRadiusBottomLeft": {"signal": "datum.value1 > 0 ? '0' : '2'"},
          "cornerRadiusBottomRight": {"signal": "datum.value1 > 0 ? '0' : '2'"}
        }
      }
    },
    {
      "name": "expenses",
      "type": "rect",
      "from": {"data": "income_and_expenses"},
      "encode": {
        "enter": {
          "x": {
            "scale": "x",
            "field": "label",
            "band": 0.5,
            "offset": {
              "signal": "datum.value1 > 0 && datum.value2 > 0  || datum.value1 < 0 && datum.value2 < 0 ? 0 : -8"
            }
          },
          "width": {"value": 16},
          "y": {"scale": "y", "field": "value2"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "#f7615c"},
          "cornerRadiusTopLeft": {"signal": "datum.value2 > 0 ? '2' : '0'"},
          "cornerRadiusTopRight": {"signal": "datum.value2 > 0 ? '2' : '0'"},
          "cornerRadiusBottomLeft": {"signal": "datum.value2 > 0 ? '0' : '2'"},
          "cornerRadiusBottomRight": {"signal": "datum.value2 > 0 ? '0' : '2'"}
        }
      }
    }
  ]
}
© www.soinside.com 2019 - 2024. All rights reserved.