我想用一种颜色为高于某个阈值的线着色,并用另一种颜色为低于阈值的线着色(见下图)。
我该怎么做?
我试过使用渐变,线条的着色效果很好,但我不知道如何设置阈值的位置。在渐变的定义中,色标必须是 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
这里有一个 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])"}
}
}
}
]
}
我一直在思考这个问题,并想出了另一种方法。 我们不需要修复图表,而只需修复数据并在值超过阈值点的地方引入一些假数据行。在我的例子中,这是 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>