图上"漂亮"网格线间隔的算法

cle*_*tus 61 algorithm charts graph

我需要一个相当聪明的算法来为图形(图表)提出"漂亮"的网格线.

例如,假设条形图的值为10,30,72和60.您知道:

最小值:10最大值:72范围:62

第一个问题是:你从什么开始?在这种情况下,0将是直观的值,但这不会阻碍其他数据集,所以我猜:

网格最小值应为0或低于范围内数据最小值的"nice"值.或者,可以指定.

网格最大值应该是该范围内最大值之上的"漂亮"值.或者,可以指定它(例如,如果显示百分比,则可能需要0到100,而不管实际值如何).

应该指定范围内的网格线(刻度线)的数量或给定范围内的数字(例如3-8),使得值"好"(即圆数)并且最大化图表区域的使用.在我们的例子中,80将是一个合理的最大值,因为它将使用90%的图表高度(72/80),而100将产生更多的浪费空间.

有人知道这个算法很好吗?语言是无关紧要的,因为我将按照我的需要实现它.

Mar*_*som 34

我用一种蛮力方法做到了这一点.首先,计算出可以放入空间的最大刻度标记数.将总值范围除以刻度数; 这是刻度线的最小间距.现在计算对数基数10的底数以得到刻度的大小,并除以该值.你应该得到1到10范围内的东西.只需选择大于或等于该值的圆数,然后乘以前面计算的对数.这是你的最终刻度线间距.

Python中的示例:

import math

def BestTick(largest, mostticks):
    minimum = largest / mostticks
    magnitude = 10 ** math.floor(math.log(minimum, 10))
    residual = minimum / magnitude
    if residual > 5:
        tick = 10 * magnitude
    elif residual > 2:
        tick = 5 * magnitude
    elif residual > 1:
        tick = 2 * magnitude
    else:
        tick = magnitude
    return tick
Run Code Online (Sandbox Code Playgroud)

编辑:您可以自由更改"好"间隔的选择.一位评论者似乎对所提供的选项不满意,因为实际的蜱数可能比最大数量少2.5倍.这是一个略微的修改,定义了一个很好的间隔的表.在这个例子中,我扩展了选择范围,使得刻度数不会小于最大值的3/5.

import bisect

def BestTick2(largest, mostticks):
    minimum = largest / mostticks
    magnitude = 10 ** math.floor(math.log(minimum, 10))
    residual = minimum / magnitude
    # this table must begin with 1 and end with 10
    table = [1, 1.5, 2, 3, 5, 7, 10]
    tick = table[bisect.bisect_right(table, residual)] if residual < 10 else 10
    return tick * magnitude
Run Code Online (Sandbox Code Playgroud)


Ada*_*iss 29

这个问题有两个部分:

  1. 确定所涉及的数量级,以及
  2. 圆形到方便的东西.

您可以使用对数处理第一部分:

range = max - min;  
exponent = int(log(range));       // See comment below.
magnitude = pow(10, exponent);
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果您的范围是50 - 1200,则指数为3,幅度为1000.

然后通过决定网格中需要多少个细分来处理第二部分:

value_per_division = magnitude / subdivisions;
Run Code Online (Sandbox Code Playgroud)

这是一个粗略的计算,因为指数已被截断为整数.您可能希望调整指数计算以更好地处理边界条件,例如通过舍入而不是采用int()如果您最终得到太多细分.

  • 自我注意:有关使用base10的javascript的“指数”,请参见以下答案:http://stackoverflow.com/questions/3019278/any-way-to-specify-the-base-of-math-log-in- javascript (2认同)

Dre*_*kes 14

我使用以下算法.它与此处发布的其他内容类似,但它是C#中的第一个示例.

public static class AxisUtil
{
    public static float CalcStepSize(float range, float targetSteps)
    {
        // calculate an initial guess at step size
        var tempStep = range/targetSteps;

        // get the magnitude of the step size
        var mag = (float)Math.Floor(Math.Log10(tempStep));
        var magPow = (float)Math.Pow(10, mag);

        // calculate most significant digit of the new step size
        var magMsd = (int)(tempStep/magPow + 0.5);

        // promote the MSD to either 1, 2, or 5
        if (magMsd > 5)
            magMsd = 10;
        else if (magMsd > 2)
            magMsd = 5;
        else if (magMsd > 1)
            magMsd = 2;

        return magMsd*magPow;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @上面的两个链接现在都失效了。我相信相同的 C# 实现位于 http://erison.blogspot.com/2011/07/algorithm-for-optimal-scaling-on-chart.html (2认同)

Chr*_*egg 9

CPAN 在这里提供了一个实现(参见源代码链接)

另请参见图轴的Tickmark算法

仅供参考,包含您的样本数据:

  • Maple:Min = 8,Max = 74,Labels = 10,20,..,60,70,Ticks = 10,12,14,.. 70,72
  • MATLAB:Min = 10,Max = 80,Labels = 10,20 ,, ..,60,80

  • 一个好的答案应该包括一个解释,而不仅仅是一个链接。链接消失... (2认同)

Dre*_*kes 5

这是JavaScript中的另一个实现:

var calcStepSize = function(range, targetSteps)
{
  // calculate an initial guess at step size
  var tempStep = range / targetSteps;

  // get the magnitude of the step size
  var mag = Math.floor(Math.log(tempStep) / Math.LN10);
  var magPow = Math.pow(10, mag);

  // calculate most significant digit of the new step size
  var magMsd = Math.round(tempStep / magPow + 0.5);

  // promote the MSD to either 1, 2, or 5
  if (magMsd > 5.0)
    magMsd = 10.0;
  else if (magMsd > 2.0)
    magMsd = 5.0;
  else if (magMsd > 1.0)
    magMsd = 2.0;

  return magMsd * magPow;
};
Run Code Online (Sandbox Code Playgroud)