将整数集分割成图表轴标签的最佳算法?
Optimal algorithm for segmenting set of integers into labels for a chart axis?
假设您从 0 to 1,000,000,000
的任意位置获取值,并且您想要绘制 30 天。所以一个特定的图表可能有一组像:
[ 1, 465, 123, 9, ... ]
虽然另一个图表可以有一组更大的数字:
[ 761010, 418781, ... ]
是否有 "optimal algorithm" 可以将这些值分割成 "clean" 个数字?措辞不当,不知道正确的术语,我会尽力解释。
"optimal algorithm",我指的是最小计算步骤数,因为它创建的标签(比如 y 轴)从人类的角度来看是最简单的。
例如,假设您总是想将 y 轴分成 5 个标签。你可以这样做:
var max = Math.max.apply(Math, values); // 465 (from the first set of values)
var interval = max / 5;
var labels = [ interval * 0, interval * 1, interval * 2, ... ];
但这会创建如下标签:
[ 0, 93, 186, ... ]
人类理解起来会很复杂。更好的(但仍然不理想)是创建如下标签:
[ 0, 125, 250, 375, 500 ]
但这仍然是具体的。它应该以某种方式找出更好的细分是:
[ 0, 200, 400, 600, 800 ]
这样,它被分成更直观的块。
有解决这个问题的标准方法吗?哪种算法效果最好?
一些数学
var getLabelWidth = function(sep, max_value){
var l = (""+max_value).length;
var av = max_value/sep/Math.pow(10,l-2); // get the length max 2 digit
/// 15.22
var width = (Math.ceil(av)*Math.pow(10,l-2)); // do a ceil on the value retrieved
// and apply it to the width of max_value.
// 16 * 10 000
return width;
}
console.log(getLabelWidth(2,59)); // 30 : [0, 30, 60]
console.log(getLabelWidth(2,100)); // 50 : [0, 50, 100]
console.log(getLabelWidth(2,968)); // 490 : [0, 490, 980]
console.log(getLabelWidth(3,368)); // 130 : [0, 130, 260, 390]
console.log(getLabelWidth(3,859)); // 290 : [0, 290, 580, 870]
console.log(getLabelWidth(3,175)); // 60 : [0, 60, 120, 180]
console.log(getLabelWidth(3,580)); // 200 : [0, 200, 400, 600]
console.log(getLabelWidth(3,74)); // 25 : [0, 25, 50, 75]
console.log(getLabelWidth(4,1111)); // 300 :[0, 300, 600, 900, 1200]
console.log(getLabelWidth(4,761010)); // 200 000: [0, 200000, 400000, 600000, 800000]
我想它可以改进一点,
抱歉我的英语不好。
作为参考,这是我最后做的。
function computeLabels(count, max) {
var magnitude = orderOfMagnitude(max);
var multiplier = magnitude * count;
// 1
if (multiplier >= max) return buildLabels(count, multiplier);
// 2
multiplier *= 2;
if (multiplier >= max) return buildLabels(count, multiplier);
// 5
multiplier *= 5;
if (multiplier >= max) return buildLabels(count, multiplier);
// 10, don't think it will ever get here but just in case.
multiplier *= 10;
if (multiplier >= max) return buildLabels(count, multiplier);
}
function buildLabels(count, multiplier) {
var labels = new Array(count);
while (count--) labels[count] = formatLabel(count * multiplier);
return labels;
}
function formatLabel(value) {
if (value > 10e5) return (value / 10e5) + 'M'; // millions
if (value > 10e2) return (value / 10e2) + 'K'; // thousands
return value; // <= hundreds
}
function orderOfMagnitude(val) {
var order = Math.floor(log10(val) + 0.000000001);
return Math.pow(10, order);
}
在纸上绘制后,"desirable" 标签似乎遵循一个简单的模式:
- 在集合中找到
max value
。
- 获得
order of magnitude
。
- 将
order of magnitude
乘以 number of ticks
。
- 迭代:如果之前的计算大于最大值,则使用它。否则,将该值乘以 2 并检查。如果不是,请尝试 5 次。所以模式是 1、2、5。
这会为您提供如下标签:
- 10、20(2 个刻度)
- 20, 40
- 50, 100
- 100, 200
- 200, 400
- 500, 1000
- ...
- 10、20、30(3 个刻度)
- 20、40、60
- 50, 100, 150(不太喜欢这个但是哦好吧)
- 100、200、300
- 10、20、30、40(4 个刻度)
- ...
它似乎可以改进,无论是在生成质量更好的 "human readable" 标签,还是在使用更优化的功能方面,但还没有完全看到。这暂时有效。
很想知道您是否找到了更好的方法!
假设您从 0 to 1,000,000,000
的任意位置获取值,并且您想要绘制 30 天。所以一个特定的图表可能有一组像:
[ 1, 465, 123, 9, ... ]
虽然另一个图表可以有一组更大的数字:
[ 761010, 418781, ... ]
是否有 "optimal algorithm" 可以将这些值分割成 "clean" 个数字?措辞不当,不知道正确的术语,我会尽力解释。
"optimal algorithm",我指的是最小计算步骤数,因为它创建的标签(比如 y 轴)从人类的角度来看是最简单的。
例如,假设您总是想将 y 轴分成 5 个标签。你可以这样做:
var max = Math.max.apply(Math, values); // 465 (from the first set of values)
var interval = max / 5;
var labels = [ interval * 0, interval * 1, interval * 2, ... ];
但这会创建如下标签:
[ 0, 93, 186, ... ]
人类理解起来会很复杂。更好的(但仍然不理想)是创建如下标签:
[ 0, 125, 250, 375, 500 ]
但这仍然是具体的。它应该以某种方式找出更好的细分是:
[ 0, 200, 400, 600, 800 ]
这样,它被分成更直观的块。
有解决这个问题的标准方法吗?哪种算法效果最好?
一些数学
var getLabelWidth = function(sep, max_value){
var l = (""+max_value).length;
var av = max_value/sep/Math.pow(10,l-2); // get the length max 2 digit
/// 15.22
var width = (Math.ceil(av)*Math.pow(10,l-2)); // do a ceil on the value retrieved
// and apply it to the width of max_value.
// 16 * 10 000
return width;
}
console.log(getLabelWidth(2,59)); // 30 : [0, 30, 60]
console.log(getLabelWidth(2,100)); // 50 : [0, 50, 100]
console.log(getLabelWidth(2,968)); // 490 : [0, 490, 980]
console.log(getLabelWidth(3,368)); // 130 : [0, 130, 260, 390]
console.log(getLabelWidth(3,859)); // 290 : [0, 290, 580, 870]
console.log(getLabelWidth(3,175)); // 60 : [0, 60, 120, 180]
console.log(getLabelWidth(3,580)); // 200 : [0, 200, 400, 600]
console.log(getLabelWidth(3,74)); // 25 : [0, 25, 50, 75]
console.log(getLabelWidth(4,1111)); // 300 :[0, 300, 600, 900, 1200]
console.log(getLabelWidth(4,761010)); // 200 000: [0, 200000, 400000, 600000, 800000]
我想它可以改进一点,
抱歉我的英语不好。
作为参考,这是我最后做的。
function computeLabels(count, max) {
var magnitude = orderOfMagnitude(max);
var multiplier = magnitude * count;
// 1
if (multiplier >= max) return buildLabels(count, multiplier);
// 2
multiplier *= 2;
if (multiplier >= max) return buildLabels(count, multiplier);
// 5
multiplier *= 5;
if (multiplier >= max) return buildLabels(count, multiplier);
// 10, don't think it will ever get here but just in case.
multiplier *= 10;
if (multiplier >= max) return buildLabels(count, multiplier);
}
function buildLabels(count, multiplier) {
var labels = new Array(count);
while (count--) labels[count] = formatLabel(count * multiplier);
return labels;
}
function formatLabel(value) {
if (value > 10e5) return (value / 10e5) + 'M'; // millions
if (value > 10e2) return (value / 10e2) + 'K'; // thousands
return value; // <= hundreds
}
function orderOfMagnitude(val) {
var order = Math.floor(log10(val) + 0.000000001);
return Math.pow(10, order);
}
在纸上绘制后,"desirable" 标签似乎遵循一个简单的模式:
- 在集合中找到
max value
。 - 获得
order of magnitude
。 - 将
order of magnitude
乘以number of ticks
。 - 迭代:如果之前的计算大于最大值,则使用它。否则,将该值乘以 2 并检查。如果不是,请尝试 5 次。所以模式是 1、2、5。
这会为您提供如下标签:
- 10、20(2 个刻度)
- 20, 40
- 50, 100
- 100, 200
- 200, 400
- 500, 1000
- ...
- 10、20、30(3 个刻度)
- 20、40、60
- 50, 100, 150(不太喜欢这个但是哦好吧)
- 100、200、300
- 10、20、30、40(4 个刻度)
- ...
它似乎可以改进,无论是在生成质量更好的 "human readable" 标签,还是在使用更优化的功能方面,但还没有完全看到。这暂时有效。
很想知道您是否找到了更好的方法!