将整数集合分割成图表轴标签的最优算法?

时间:2015-03-02 22:49:04

标签: javascript algorithm plot charts aggregate-functions

假设您从0 to 1,000,000,000获取值,并且您希望绘制30天。因此,一个特定的图表可能有一组如下:

[ 1, 465, 123, 9, ... ]

虽然另一张图表的数字可以大得多:

[ 761010, 418781, ... ]

是否存在"最佳算法"这可以采取这些价值观并将其细分为"清洁"号码?对于措辞不好,不知道正确的术语,我会尝试解释。

通过"最佳算法",我指的是计算步骤的最小数量,因为它创建了从人类角度来看最简单的标签(比如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 ]

这样,它被分成更直观的块。

是否有解决此问题的标准方法?什么算法效果最好?

2 个答案:

答案 0 :(得分:1)

一些数学

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]

我猜可能会有所改善,

抱歉我的英语不好。

答案 1 :(得分:0)

作为参考,这是我最终做的事情。

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);
}

在纸上画完后,&#34;理想的&#34;标签似乎遵循一个简单的模式:

  1. 在集合中找到max value
  2. 获取order of magnitude
  3. order of magnitude乘以number of ticks
  4. 迭代:如果之前的计算大于最大值,则使用它。否则,将值乘以2并检查。如果没有,请尝试5次。因此模式为1,2,5。
  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个蜱)
    • ...

    似乎可以改进,无论是在产生更好的质量还是人类可读的#34;标签,以及使用更多优化的功能,但还没有看到它。这暂时有效。

    很想知道你是否找到了更好的方法!