尝试将文本标记放置在条形图上的同一级别

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

使用此规范,我尝试创建一个实际与预算图表,其中值打印在条形上方的同一级别。我怀疑更好的方法会涉及图层,但目前我已经尝试添加最大 y 值的倍数并将文本标记放在那里。当您只有 2 个标记但第三个标记无法与顶部/底部对齐并因此永远无法正确渲染时,它可以正常工作。我唯一取得的部分成功是明确设置文本标记的 y 坐标。似乎偏移量也被忽略了。

我的最终目标是制作类似于本视频中的子弹图的东西 https://www.youtube.com/watch?v=Tc0D0THMI78 但对于条形上方的文本值...非常感谢任何指导。

  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "Overlapping bar chart showing monthly actual vs budget values for 2024.",
  "width": 550,
  "height": {"signal": "chartHeight"},
  "padding": 5,
  "signals": [
    {
      "name": "maxY3",
      "update": "max(actualExtent[1], budgetExtent[1])*1.4"
    },
    {
      "name": "chartHeight",
      "update": "200"
    }
  ],
  "data": [
    {
      "name": "table",
      "values": [
        {"date": "2024-01-01", "actual": 100000000, "budget": 150000000},
        {"date": "2024-02-01", "actual": 120000000, "budget": 120000000},
        {"date": "2024-03-01", "actual": 140000000, "budget": 130000000},
        {"date": "2024-04-01", "actual": 160000000, "budget": 170000000},
        {"date": "2024-05-01", "actual": 180000000, "budget": 110000000},
        {"date": "2024-06-01", "actual": 220000000, "budget": 210000000},
        {"date": "2024-07-01", "actual": 240000000, "budget": 230000000},
        {"date": "2024-08-01", "actual": 260000000, "budget": 250000000},
        {"date": "2024-09-01", "actual": 280000000, "budget": 220000000},
        {"date": "2024-10-01", "actual": 300000000, "budget": 320000000},
        {"date": "2024-11-01", "actual": 350000000, "budget": 340000000},
        {"date": "2024-12-01", "actual": 370000000, "budget": 360000000}
      ],
      "transform": [
        {
          "type": "formula",
          "expr": "timeParse(datum.date, '%Y-%m-%d')",
          "as": "parsedDate"
        },{
          "type": "formula",
          "expr": "datum.actual-datum.budget",
          "as": "variance"
        },{
          "type": "formula",
          "expr": "datum.budget!=0?datum.variance/datum.budget:0",
          "as": "variancepct"
        },

        {
          "type": "extent",
          "field": "actual",
          "signal": "actualExtent"
        },
        {
          "type": "extent",
          "field": "budget",
          "signal": "budgetExtent"
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "time",
      "domain": {"data": "table", "field": "parsedDate"},
      "range": "width"
    },
    {
      "name": "y",
      "type": "linear",
      "domain": [0, {"signal": "maxY3"}],
      "range": "height",
      "nice": true
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "x",
      "format": "%b",
      "title": "Month",
      "ticks": true,
      "labelAlign": "center",
      "tickOffset": 20
    },
    {
      "orient": "left",
      "scale": "y",
      "title": "",
      "domain": false,
      "labels": false,
      "grid": false,
      "ticks":false
    }
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 0},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "actual"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "grey"},
          "tooltip": {"signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Actual': format(datum.actual, ',')}"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 10},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "budget"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "lightblue"},
          "tooltip": {"signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Budget': format(datum.budget, ',')}"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "value": {"signal": "maxY3"}, "offset":0}, 
          "text": {"signal": "[format(datum.actual,'.4s')]"},
          "fontSize": {"value": 14},
          "fill": {"value": "black"},
          "align": {"value": "center"},
          "baseline": {"value": "bottom"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset":20},
          "y": {"scale": "y", "value": {"signal": "maxY3"}, "offset":0}, 
          "text": {"signal": "[format(datum.budget,'.4s')]"},
          "fontSize": {"value": 14},
          "fill": {"value": "black"},
          "align": {"value": "center"},
          "baseline": {"value": "top"} 
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset":20},
          "y": {"scale": "y", "value": {"signal": "maxY3"}, "offset":0}, 
          "text": {"signal": "[format(datum.variancepct,'.2p')]"},
          "fontSize": {"value": 14},
          "fill": {"value": "black"},
          "align": {"value": "center"},
          "baseline": {"value": "top"} 
        }
      }
    }
  ]
}

在发布问题并在下面做出有关最佳实践的评论后,我进行了以下编辑,这些编辑几乎让我达到了此时我想去的地方,但我仍然想对该方法进行批评

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "Overlapping bar chart showing monthly actual vs budget values for 2024.",
  "width": 550,
  "height": {"signal": "chartHeight"},
  "title": "my generic chart title",
  "padding": 5,
  "signals": [{
      "name": "maxY4",
      "update": "max(actualExtent[1], budgetExtent[1])*1.4"
    },{
      "name": "maxY1",
      "update": "max(actualExtent[1], budgetExtent[1])*1.3"
    },
    {
      "name": "maxY2",
      "update": "max(actualExtent[1], budgetExtent[1])*1.2"
    }, {
      "name": "maxY3",
      "update": "max(actualExtent[1], budgetExtent[1])*1.1"
    },
    {
      "name": "chartHeight",
      "update": "200"
    }
  ],
  "data": [
    {
      "name": "table",
      "values": [
        {"date": "2024-01-01", "actual": 100000000, "budget": 150000000},
        {"date": "2024-02-01", "actual": 120000000, "budget": 120000000},
        {"date": "2024-03-01", "actual": 140000000, "budget": 130000000},
        {"date": "2024-04-01", "actual": 160000000, "budget": 170000000},
        {"date": "2024-05-01", "actual": 180000000, "budget": 110000000},
        {"date": "2024-06-01", "actual": 220000000, "budget": 210000000},
        {"date": "2024-07-01", "actual": 240000000, "budget": 230000000},
        {"date": "2024-08-01", "actual": 260000000, "budget": 250000000},
        {"date": "2024-09-01", "actual": 280000000, "budget": 220000000},
        {"date": "2024-10-01", "actual": 300000000, "budget": 320000000},
        {"date": "2024-11-01", "actual": 350000000, "budget": 340000000},
        {"date": "2024-12-01", "actual": 370000000, "budget": 360000000}
      ],
      "transform": [
        {
          "type": "formula",
          "expr": "timeParse(datum.date, '%Y-%m-%d')",
          "as": "parsedDate"
        },{
          "type": "formula",
          "expr": "datum.actual-datum.budget",
          "as": "variance"
        },{
          "type": "formula",
          "expr": "datum.budget!=0?datum.variance/datum.budget:0",
          "as": "variancepct"
        },{
          "type": "formula",
          "expr": "datum.variance>=0 ?1:0",
          "as": "isgreen"
        },

        {
          "type": "extent",
          "field": "actual",
          "signal": "actualExtent"
        },
        {
          "type": "extent",
          "field": "budget",
          "signal": "budgetExtent"
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "time",
      "domain": {"data": "table", "field": "parsedDate"},
      "range": "width"
    },
    {
      "name": "y",
      "type": "linear",
      "domain": [0, {"signal": "maxY3"}],
      "range": "height",
      "nice": true
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "x",
      "format": "%b",
      "title": "Month",
      "ticks": true,
      "labelAlign": "center",
      "tickOffset": 20
    },
    {
      "orient": "left",
      "scale": "y",
      "title": "",
      "domain": false,
      "labels": false,
      "grid": false,
      "ticks":false
    }
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 0},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "budget"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "grey"},
          "tooltip": {"signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Actual': format(datum.actual, ',')}"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 10},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "actual"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "lightblue"},
          "tooltip": {"signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Budget': format(datum.budget, ',')}"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "signal": "maxY1", "offset": 0}, 
          "text": {"signal": "[format(datum.actual,'.4s')]"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset":20},
           "y": {"scale": "y", "signal": "maxY2", "offset": 0},
          "text": {"signal": "[format(datum.budget,'.4s')]"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset":20},
          "y": {"scale": "y", "signal": "maxY3", "offset": 0},
          "text": {"signal": "[format(datum.variancepct,'.2p')]"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "center"} ,
          "fill": {
            "signal":  "datum.isgreen==1?'green':'red'"
          }
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset":20},
          "y": {"scale": "y", "signal": "maxY4", "offset": 0},
          "text": {"signal": "[timeFormat(datum.parsedDate, '%b')]"},
          "fontSize": {"value": 12},
          "fontWeight":{"value":"bold"},
          "fill": {"value": "black"},
          "align": {"value": "center"} 
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value": -10},
          "y": {"scale": "y", "signal": "maxY1", "offset": 0}, 
          "text": {"value": "Actual"},
          "fontSize": {"value": 12},
          "fontWeight":{"value":"bold"},
          "fill": {"value": "black"},
          "align": {"value": "right"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value": -10},
          "y": {"scale": "y", "signal": "maxY2", "offset": 0}, 
          "text": {"value": "Budget"},
          "fontSize": {"value": 12},
          "fontWeight":{"value":"bold"},
          "fill": {"value": "black"},
          "align": {"value": "right"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value": -10},
          "y": {"scale": "y", "signal": "maxY3", "offset": 0}, 
          "text": {"value": "Variance %"},
          "fontWeight":{"value":"bold"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "right"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value": -10},
          "y": {"scale": "y", "signal": "maxY4", "offset": 0}, 
          "text": {"value": "Month"},
          "fontSize": {"value": 12},
          "fontWeight":{"value":"bold"},
          "fill": {"value": "black"},
          "align": {"value": "right"}
        }
      }
    }
  ]
}

一般来说有更好的方法吗?

json charts visualization vega
1个回答
0
投票

通过制作数据集,您可以减少文本标记,但在其他方面看起来不错。

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "Overlapping bar chart showing monthly actual vs budget values for 2024.",
  "width": 550,
  "height": {"signal": "chartHeight"},
  "title": "my generic chart title",
  "padding": 5,
  "signals": [
    {"name": "maxY4", "update": "max(actualExtent[1], budgetExtent[1])*1.4"},
    {"name": "maxY1", "update": "max(actualExtent[1], budgetExtent[1])*1.3"},
    {"name": "maxY2", "update": "max(actualExtent[1], budgetExtent[1])*1.2"},
    {"name": "maxY3", "update": "max(actualExtent[1], budgetExtent[1])*1.1"},
    {"name": "chartHeight", "update": "200"}
  ],
  "data": [
    {
      "name": "table",
      "values": [
        {"date": "2024-01-01", "actual": 100000000, "budget": 150000000},
        {"date": "2024-02-01", "actual": 120000000, "budget": 120000000},
        {"date": "2024-03-01", "actual": 140000000, "budget": 130000000},
        {"date": "2024-04-01", "actual": 160000000, "budget": 170000000},
        {"date": "2024-05-01", "actual": 180000000, "budget": 110000000},
        {"date": "2024-06-01", "actual": 220000000, "budget": 210000000},
        {"date": "2024-07-01", "actual": 240000000, "budget": 230000000},
        {"date": "2024-08-01", "actual": 260000000, "budget": 250000000},
        {"date": "2024-09-01", "actual": 280000000, "budget": 220000000},
        {"date": "2024-10-01", "actual": 300000000, "budget": 320000000},
        {"date": "2024-11-01", "actual": 350000000, "budget": 340000000},
        {"date": "2024-12-01", "actual": 370000000, "budget": 360000000}
      ],
      "transform": [
        {
          "type": "formula",
          "expr": "timeParse(datum.date, '%Y-%m-%d')",
          "as": "parsedDate"
        },
        {
          "type": "formula",
          "expr": "datum.actual-datum.budget",
          "as": "variance"
        },
        {
          "type": "formula",
          "expr": "datum.budget!=0?datum.variance/datum.budget:0",
          "as": "variancepct"
        },
        {"type": "formula", "expr": "datum.variance>=0 ?1:0", "as": "isgreen"},
        {"type": "extent", "field": "actual", "signal": "actualExtent"},
        {"type": "extent", "field": "budget", "signal": "budgetExtent"}
      ]
    },
    {
      "name": "headers",
      "values": [
        {"id": 1, "val": "Month"},
        {"id": 2, "val": "Actual"},
        {"id": 3, "val": "Budget"},
        {"id": 4, "val": "Variance %"}
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "time",
      "domain": {"data": "table", "field": "parsedDate"},
      "range": "width"
    },
    {
      "name": "y",
      "type": "linear",
      "domain": [0, {"signal": "maxY3"}],
      "range": "height",
      "nice": true
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "x",
      "format": "%b",
      "title": "Month",
      "ticks": true,
      "labelAlign": "center",
      "tickOffset": 20
    },
    {
      "orient": "left",
      "scale": "y",
      "title": "",
      "domain": false,
      "labels": false,
      "grid": false,
      "ticks": false
    }
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 0},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "budget"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "grey"},
          "tooltip": {
            "signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Actual': format(datum.actual, ',')}"
          }
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 10},
          "width": {"value": 30},
          "y": {"scale": "y", "field": "actual"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "lightblue"},
          "tooltip": {
            "signal": "{'Date': timeFormat(datum.parsedDate, '%B'), 'Budget': format(datum.budget, ',')}"
          }
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "signal": "maxY1", "offset": 0},
          "text": {"signal": "[format(datum.actual,'.4s')]"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "signal": "maxY2", "offset": 0},
          "text": {"signal": "[format(datum.budget,'.4s')]"},
          "fontSize": {"value": 12},
          "fill": {"value": "black"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "signal": "maxY3", "offset": 0},
          "text": {"signal": "[format(datum.variancepct,'.2p')]"},
          "fontSize": {"value": 12},
          "fill": {"signal": "datum.isgreen==1?'green':'red'"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "parsedDate", "offset": 20},
          "y": {"scale": "y", "signal": "maxY4", "offset": 0},
          "text": {"signal": "[timeFormat(datum.parsedDate, '%b')]"},
          "fontSize": {"value": 12},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "black"},
          "align": {"value": "center"}
        }
      }
    },
    {
      "type": "text",
      "name": "aa",
      "from": {"data": "headers"},
      "encode": {
        "update": {
          "x": {"value": -10},
          "y": {
            "scale": "y",
            "signal": "datum.id == 1?maxY4:datum.id == 2?maxY1:datum.id == 3?maxY2:maxY3"
          },
          "text": {"field": "val"},
          "fontSize": {"value": 12},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "black"},
          "align": {"value": "right"}
        }
      }
    }
  ]
}
© www.soinside.com 2019 - 2024. All rights reserved.