折线图的 Vega-Lite 渐变

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

我想用一种颜色为高于某个阈值的线着色,并用另一种颜色为低于阈值的线着色(见下图)。

我该怎么做?

我试过使用渐变,线条的着色效果很好,但我不知道如何设置阈值的位置。在渐变的定义中,色标必须是 0 到 1 之间的数字(偏移分数,表示它在渐变中的位置)。

我想将偏移量指定为 y 轴上的值。

这可能吗?我该怎么做?

使用渐变改变阈值以上线条颜色的图表示例:

渐变定义:

 "color": {
      "x1": 1,
      "y1": 1,
      "x2": 1,
      "y2": 0,
      "gradient": "linear",
      "stops": [
        {
          "offset": 0,
          "color": "red"
        },
        {
          "offset": 0.5,
          "color": "red"
        },
        {
          "offset": 0.5,
          "color": "blue"
        },
        {
          "offset": 1,
          "color": "blue"
        }
      ]
    }

上图的代码和运行规范:https://vega.github.io/editor/#/gist/06c345f6fd61127781e285a227243f4c/spec.json

渐变文档: https://vega.github.io/vega-lite/docs/gradient.html

charts gradient vega-lite vega threshold
2个回答
1
投票

这里有一个 Vega 的 hacky 方法。

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A basic stacked area chart example.",
  "width": 500,
  "height": 200,
  "padding": 5,
  "data": [
    {
      "name": "table",
      "values": [
        {"x": 0, "y": 28},
        {"x": 1, "y": 43},
        {"x": 2, "y": 81},
        {"x": 3, "y": 19},
        {"x": 4, "y": 52},
        {"x": 5, "y": 24},
        {"x": 6, "y": 87},
        {"x": 7, "y": 17},
        {"x": 8, "y": 68},
        {"x": 9, "y": 49}
      ],
      "transform": [{"type": "formula", "as": "y2", "expr": "datum.y-1"}]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "point",
      "range": "width",
      "domain": {"data": "table", "field": "x"}
    },
    {
      "name": "y",
      "type": "linear",
      "range": "height",
      "nice": true,
      "zero": true,
      "domain": {"data": "table", "field": "y"}
    },
    {"name": "color", "type": "sequential", "range": {"scheme": "rainbow"}}
  ],
  "axes": [
    {"orient": "bottom", "scale": "x", "zindex": 1},
    {"orient": "left", "scale": "y", "zindex": 1}
  ],
  "marks": [
    {
      "type": "area",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "interpolate": {"value": "monotone"},
          "x": {"scale": "x", "field": "x"},
          "y": {"scale": "y", "field": "y"},
          "y2": {"scale": "y", "field": "y2"},
          "fill": {"signal": "gradient('color', [0,1], [1,1])"}
        }
      }
    }
  ]
}

0
投票

我一直在思考这个问题,并想出了另一种方法。 我们不需要修复图表,而只需修复数据并在值超过阈值点的地方引入一些假数据行。在我的例子中,这是 200.

所以如果我的数据集是:

{"date": "2022-01-01T00:00:00", "price": 100},
{"date": "2022-01-02T00:00:00", "price": 150},
{"date": "2022-01-03T00:00:00", "price": 180},
{"date": "2022-01-04T00:00:00", "price": 270},
{"date": "2022-01-05T00:00:00", "price": 80}

然后我们添加一些假行:

{"date": "2022-01-03T00:00:00", "price": 200},
{"date": "2022-01-04T00:00:00", "price": 200},

为了让线条完美地工作,我们需要计算线条大约在什么时间越过阈值。我们可以通过查看开始和结束的范围来找到这一点。如果午夜是 180,24 小时后是 270,那么我们知道它在 24 小时内移动了 90。所以我们只需要知道 id 移动 20(180 到 200)用了多长时间。使用 javascript 很容易做到这一点。您还可以在 SQL 中的视图中应用相同的逻辑。

{"date": "2022-01-03T00:00:00", "price": 180},
{"date": "2022-01-04T00:00:00", "price": 270},

我还发现有必要每次添加2个假行。每种颜色一个,否则 vega-lite 会在图表中显示空白。 请使用下面的 javascript 显示超过阈值的多色折线图。

快乐制图!

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vega-Lite Example</title>
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>
<div id="vis"></div>
<script>
// Load the data
const data = [
{"date": "2022-01-01T00:00:00", "price": 100},
{"date": "2022-01-02T00:00:00", "price": 150},
{"date": "2022-01-03T00:00:00", "price": 180},
{"date": "2022-01-04T00:00:00", "price": 270},
{"date": "2022-01-05T00:00:00", "price": 80}
];

const threshold = 200;

// Iterate through the data and add fake rows for values that cross the threshold
const newData = [];
for (let i = 0; i < data.length - 1; i++) {
const d1 = data[i];
const d2 = data[i + 1];

// Check if the price crosses the threshold between these two data points
if ((d1.price < threshold && d2.price >= threshold) || (d1.price >= threshold && d2.price < threshold)) {
// Calculate the interpolated point where the line crosses the threshold
const t = (threshold - d1.price) / (d2.price - d1.price);
const interpolatedPrice = threshold;
const interpolatedTimestamp = new Date(new Date(d1.date).getTime() + t * (new Date(d2.date).getTime() - new Date(d1.date).getTime()));
interpolatedTimestamp.setSeconds(0); // Round to the nearest minute
const interpolatedDate = interpolatedTimestamp.toISOString();

// Add a fake data point for the interpolated value
newData.push({
date: d1.date,
price: d1.price,
color: d1.price < threshold ? 'red' : 'blue'
});

newData.push({
date: interpolatedDate,
price: interpolatedPrice,
color: d1.price < threshold ? 'blue' : 'red'
});

newData.push({
date: interpolatedDate,
price: interpolatedPrice,
color: d1.price < threshold ? 'red' : 'blue'
});

newData.push({
date: d2.date,
price: d2.price,
color: d2.price < threshold ? 'red' : 'blue'
});
} else {
// No interpolation needed, just copy the original data point
newData.push({
date: d1.date,
price: d1.price,
color: d1.price < threshold ? 'red' : 'blue'
});
}
}
// Add the last data point with the color property
const lastDataPoint = data[data.length - 1];
newData.push({
date: lastDataPoint.date,
price: lastDataPoint.price,
color: lastDataPoint.price < threshold ? 'red' : 'blue'
});

const processedData = newData.map(d => {
return {
date: new Date(d.date),
price: d.price,
color: d.color
};
});
// Test out new data source
console.log(processedData);

// Define the Vega-Lite specification
const spec = {
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {"values": processedData},
  "encoding": {
    "x": {"field": "date", "type": "temporal"},
    "y": {"field": "price", "type": "quantitative", "impute": {"value": null}},
    "color": {
      "field": "color",
      "type": "nominal",
      "scale": {"domain": ["red", "blue"], "range": ["red", "blue"]},
      "legend": null
    },
    "tooltip": [
      {"field": "date", "type": "temporal"},
      {"field": "price", "type": "quantitative"}
    ]
  },
  "layer": [
    {
      "mark": {"type": "line", "strokeWidth": 3}
    }
  ],
  "config": {"legend": null}
};

// Render the chart using Vega-Embed
const embedOpt = {"mode": "vega-lite"};
vegaEmbed("#vis", spec, embedOpt);
</script>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.