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

如何在Vega-Lite中实现季度环比(QoQ)叠加折线图?

Vega-Lite 季度同期对比双折线图配置方案

由于季度天数不固定,无法通过固定窗口偏移实现上一季度同期数据匹配,我们可以通过日期逻辑计算+关联匹配的方式解决,核心思路是为每个数据点计算对应的上一季度同期日期,再通过lookup获取对应数值。

核心实现步骤

  1. 解析日期并提取年、季度信息
  2. 计算当前日期在所属季度内的相对天数
  3. 推导上一季度的年份、季度及起始日期
  4. 根据相对天数匹配上一季度的对应日期(处理跨年度、季度天数差异问题)
  5. 通过lookup关联上一季度的数值
  6. 绘制双折线图

完整配置示例

长格式数据方案(推荐,便于统一管理图例)

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {"date": "2024-01-05", "value": 100},
      {"date": "2024-01-12", "value": 120},
      {"date": "2024-01-19", "value": 110},
      {"date": "2024-02-02", "value": 130},
      {"date": "2024-02-09", "value": 140},
      {"date": "2024-02-16", "value": 135},
      {"date": "2024-03-01", "value": 150},
      {"date": "2024-03-08", "value": 160},
      {"date": "2024-03-15", "value": 155},
      {"date": "2024-04-05", "value": 170},
      {"date": "2024-04-12", "value": 180},
      {"date": "2024-04-19", "value": 175},
      {"date": "2024-05-03", "value": 190},
      {"date": "2024-05-10", "value": 200},
      {"date": "2024-05-17", "value": 195},
      {"date": "2024-06-07", "value": 210},
      {"date": "2024-06-14", "value": 220},
      {"date": "2024-06-21", "value": 215}
    ]
  },
  "transform": [
    // 解析日期,提取年、季度
    {"calculate": "datetime(datum.date)", "as": "datetime"},
    {"calculate": "year(datum.datetime)", "as": "year"},
    {"calculate": "quarter(datum.datetime)", "as": "quarter"},
    // 计算本季度起始日期
    {"calculate": "datetime(datum.year, (datum.quarter - 1) * 3, 1)", "as": "quarter_start"},
    // 计算当前日期在本季度的天数偏移
    {"calculate": "floor((datum.datetime - datum.quarter_start) / (1000 * 60 * 60 * 24))", "as": "day_in_quarter"},
    // 推导上一季度的年份和季度(处理Q1对应去年Q4的情况)
    {"calculate": "datum.quarter === 1 ? datum.year - 1 : datum.year", "as": "prev_quarter_year"},
    {"calculate": "datum.quarter === 1 ? 4 : datum.quarter - 1", "as": "prev_quarter"},
    // 计算上一季度起始日期
    {"calculate": "datetime(datum.prev_quarter_year, (datum.prev_quarter - 1) * 3, 1)", "as": "prev_quarter_start"},
    // 计算上一季度总天数,避免无效日期
    {"calculate": "floor((datetime(datum.prev_quarter_year, datum.prev_quarter * 3, 1) - datum.prev_quarter_start) / (1000 * 60 * 60 * 24))", "as": "prev_quarter_total_days"},
    // 计算上一季度对应日期:若当前天数超过上一季度总天数,取上一季度最后一天
    {"calculate": "datum.day_in_quarter < datum.prev_quarter_total_days ? datum.prev_quarter_start + datum.day_in_quarter * 1000 * 60 * 60 * 24 : datetime(datum.prev_quarter_year, datum.prev_quarter * 3, 0)", "as": "prev_quarter_date"},
    // 格式化为统一字符串用于匹配
    {"calculate": "timeFormat(datum.prev_quarter_date, '%Y-%m-%d')", "as": "prev_quarter_date_str"},
    // 关联上一季度同期数值
    {
      "lookup": "prev_quarter_date_str",
      "from": {"data": {"values": [/* 替换为你的原始数据,或引用外部数据源 */]}, "key": "date", "fields": ["value"]},
      "as": "prev_quarter_value"
    },
    // 转换为长格式,方便绘制双折线
    {"fold": ["value", "prev_quarter_value"], "as": ["series", "value"]}
  ],
  "mark": "line",
  "encoding": {
    "x": {"field": "datetime", "type": "temporal", "title": "日期"},
    "y": {"field": "value", "type": "quantitative", "title": "数值"},
    "color": {
      "field": "series",
      "type": "nominal",
      "title": "数据类型",
      "scale": {"domain": ["value", "prev_quarter_value"], "range": ["#1f77b4", "#ff7f0e"]},
      "legend": {"labelExpr": "datum.value === 'value' ? '实际数据' : '上一季度同期'"}
    }
  }
}

层叠折线方案(适合自定义每条线样式)

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {"values": [/* 你的原始数据 */]},
  "transform": [
    {"calculate": "datetime(datum.date)", "as": "datetime"},
    {"calculate": "year(datum.datetime)", "as": "year"},
    {"calculate": "quarter(datum.datetime)", "as": "quarter"},
    {"calculate": "datetime(datum.year, (datum.quarter - 1) * 3, 1)", "as": "quarter_start"},
    {"calculate": "floor((datum.datetime - datum.quarter_start) / (1000 * 60 * 60 * 24))", "as": "day_in_quarter"},
    {"calculate": "datum.quarter === 1 ? datum.year - 1 : datum.year", "as": "prev_quarter_year"},
    {"calculate": "datum.quarter === 1 ? 4 : datum.quarter - 1", "as": "prev_quarter"},
    {"calculate": "datetime(datum.prev_quarter_year, (datum.prev_quarter - 1) * 3, 1)", "as": "prev_quarter_start"},
    {"calculate": "floor((datetime(datum.prev_quarter_year, datum.prev_quarter * 3, 1) - datum.prev_quarter_start) / (1000 * 60 * 60 * 24))", "as": "prev_quarter_total_days"},
    {"calculate": "datum.day_in_quarter < datum.prev_quarter_total_days ? datum.prev_quarter_start + datum.day_in_quarter * 1000 * 60 * 60 * 24 : datetime(datum.prev_quarter_year, datum.prev_quarter * 3, 0)", "as": "prev_quarter_date"},
    {"calculate": "timeFormat(datum.prev_quarter_date, '%Y-%m-%d')", "as": "prev_quarter_date_str"},
    {
      "lookup": "prev_quarter_date_str",
      "from": {"data": {"values": [/* 你的原始数据 */]}, "key": "date", "fields": ["value"]},
      "as": "prev_quarter_value"
    }
  ],
  "layer": [
    {
      "mark": {"type": "line", "color": "#1f77b4", "strokeWidth": 2},
      "encoding": {
        "x": {"field": "datetime", "type": "temporal"},
        "y": {"field": "value", "type": "quantitative"}
      }
    },
    {
      "mark": {"type": "line", "color": "#ff7f0e", "strokeWidth": 2, "strokeDash": [5, 5]},
      "encoding": {
        "x": {"field": "datetime", "type": "temporal"},
        "y": {"field": "prev_quarter_value", "type": "quantitative"}
      }
    }
  ],
  "resolve": {"scale": {"y": "shared"}},
  "legend": {
    "title": "数据类型",
    "symbolType": "line",
    "entries": [
      {"name": "实际数据", "symbolColor": "#1f77b4", "symbolStrokeWidth": 2},
      {"name": "上一季度同期", "symbolColor": "#ff7f0e", "symbolStrokeWidth": 2, "symbolStrokeDash": [5, 5]}
    ]
  }
}

关键细节说明

  • 跨年度处理:针对Q1的上一季度是去年Q4的情况,通过判断季度值自动调整年份。
  • 无效日期规避:计算上一季度总天数,若当前日期在本季度的天数超过上一季度总天数,自动取上一季度最后一天作为匹配日期。
  • 数据源适配:如果使用外部数据源(如CSV),将data.values替换为data.url,并确保lookup的from.data引用同一数据源。
  • 空值处理:若某数据点无对应上一季度数据,prev_quarter_value会为null,可通过calculate添加默认值(如{"calculate": "datum.prev_quarter_value ?? 0", "as": "prev_quarter_value"})。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:14:53