与 d3.js 相比,这更像是一个数据科学问题,但我想其他人也一定也考虑过这一点。
我有一个包含每日更新值的数据集。该集合还包含所有或几天的历史数据。基本上是这样的:
{data: [
"ItemA" : {
"24.10.2020" : 123,
"25.10.2020" : 134,
"26.10.2020" : 145,
"27.10.2020" : 156,
"28.10.2020" : 167
},
"ItemB" : {
"24.10.2020" : 123,
"25.10.2020" : 234,
"26.10.2020" : 456,
"27.10.2020" : 567,
"28.10.2020" : 678
},
"ItemC" : {
"24.10.2020" : 123,
"25.10.2020" : 136,
"26.10.2020" : 149,
"27.10.2020" : 152,
"26.10.2020" : 165,
"28.10.2020" : 178
},
]}
如您所见,ItemB 是一个异常值,其值的增长速度比其他 Item 的值增长得快得多。
只要值以几乎相同的速度增长,为图例设置一个比例来显示随时间的增长就很容易。 d3.scaleLinear().domain([0, upperBoundValues])
很好。当值增长时,用户仍然可以区分较小和较高的值。
由于一项项目增长较快,因此增长较慢的项目会被插入规模的一部分。因此,如果我有一个像 d3.interpolateTurbo 这样的颜色范围,突然大多数值都会显示为接近黑色的颜色,而一个总是显示为红色。
我会手动切换到功率标度或对数标度。特别是因为我必须每天检查发生了什么。
我希望有一个函数可以测试此类开发并自动切换值的比例。更好的是选择一个拟合标度(基本上选择功率标度和/或对数标度的基数的最佳拟合指数)。 如果我的值永远不会增长到超大数字,我就不需要以 10 为底的对数刻度。
是否有任何我可以实现的近似函数/算法可以使选择更容易,或者在我可以选择比例时返回一个值(例如:0...1 -> Linear//1...n -> Log )
最佳答案
作为我的评论的扩展,请考虑以下内容,其中使用 scaleThreshold
和十分位值。我画了5个圆圈,第一个圆圈的值(value)比其他圆圈增长得快得多。但由于阈值比例的原因,您仍然会看到它们之间存在足够的差异。
const data = d3.range(5).map(i => {
let values = [1];
d3.range(50).forEach(() => {
// Either a multiplier [0.9, 1.2], or (if it's the first one, [1.2, 1.5]
const multiplier = (i === 0 ? 1.2 : 0.9) + (Math.random() * 0.3);
values.push(values[values.length - 1] * multiplier);
});
return {
x: 50 + i * 100,
y: 50,
r: 40,
values: values,
};
});
const allValues = data.map(d => d.values).flat().sort((a, b) => a - b);
const colours = d3.scaleThreshold()
.domain([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9].map(i => d3.quantile(allValues, i)))
.range(d3.schemeSpectral[10]);
const svg = d3.select("svg")
.attr("width", 500);
const colourbar = svg.append("g");
colourbar
.selectAll("rect")
.data(colours.range())
.enter()
.append("rect")
.attr("x", (d, i) => i * 50)
.attr("y", 100)
.attr("height", 20)
.attr("width", 50)
.attr("fill", d => d);
colourbar
.selectAll("text")
.data(colours.domain())
.enter()
.append("text")
.attr("x", (d, i) => (i + 1) * 50)
.attr("y", 135)
.text(d => d.toFixed(1));
const circles = svg.append("g")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.r);
const labels = svg
.append("g")
.selectAll("text")
.data(data)
.enter()
.append("text")
.style("fill", "white")
.attr("dy", 5)
.attr("x", d => d.x)
.attr("y", d => d.y);
let counter = -1;
function colour() {
counter = (counter + 1) % 50;
labels.text(d => d.values[counter].toFixed(1));
circles
.transition()
.duration(1000)
.ease(d3.easeLinear)
.attr("fill", d => colours(d.values[counter]))
.filter((d, i) => i === 0)
.on("end", colour);
}
colour();
text {
text-anchor: middle;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
关于algorithm - 自动选择图例的比例(线性、幂、对数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64544680/