合理优化的图表缩放

时间:2009-03-04 18:37:22

标签: algorithm math charts

我需要制作一个具有优化的 y 轴最大值的图表。

我制作图表的当前方法只是使用所有图形的最大值,然后将其除以10,并将其用作网格线。我没有写。

更新注意:这些图表已更改。一旦我修复了代码,我的动态图就开始工作了,这个问题没有意义(因为这些例子不再有任何错误)。我用静态图像更新了这些,但是一些答案引用了不同的值。记在脑子里。 Old Chart 到目前为止,2月份有12003到14003个入境电话。信息性,但丑陋。

我想避免看起来像猴子的图表提出 y -axis数字。

使用谷歌图表API有点帮助,但它仍然不是我想要的。 Google API Chart 数字很​​干净,但y值的顶部始终与图表上的最大值相同。此图表从0到1357缩放。我需要计算1400的正确值,有问题


我在rbobby中摒弃了一个“好”的数字,因为它解释得很好。

  • “nice”数字是指具有3个或更少非零数字的数字(例如1230000)
  • “漂亮”的数字与零数字相同或几个非零数字(例如1230不好,1200就不错)
  • 最好的数字是3个零的倍数(例如“1,000”,“1,000,000”)
  • 第二个最好的数字是多个3个零加2个零的数量(例如“1,500,000”,“1,200”)

解决方案

New Chart

我找到了使用Mark Ransom想法的修改版本来获得我想要的结果的方法。

Fist,Mark Ransom的代码确定了刻度线之间的最佳间距,给出了刻度数。有时这个数字最终会超过图表中最高值的两倍,具体取决于您想要的网格线数量。

我正在做的是我用5,6,7,8,9和10个网格线(刻度线)运行Mark的代码,以找出哪些是最低的。值为23时,图表的高度为25,网格线为5,10,15,20和25.值为26时,图表的高度为30,网格线为5,10 ,15,20,25和30.网格线之间的间距相同,但它们有更多。

所以这里只是简单地复制Excel所做的一些步骤,以使图表更加流行。

  1. 暂时将图表的最高值提高约5%(因此图表的最高点和图表区域的顶部之间总会有一些空格。我们希望99.9向上舍入到120)
  2. 找到最佳的网格线位置 适用于5,6,7,8,9和10格 线。
  3. 选出这些数字中最低的数字。记住获得该值所需的网格线数。
  4. 现在您拥有最佳图表高度。线条/条形图永远不会与图表顶部相对应,并且您有最佳的刻度数。
  5. PHP:

    function roundUp($maxValue){
        $optiMax = $maxValue * 2;
        for ($i = 5; $i <= 10; $i++){
            $tmpMaxValue = bestTick($maxValue,$i);
            if (($optiMax > $tmpMaxValue) and ($tmpMaxValue > ($maxValue + $maxValue * 0.05))){
                $optiMax = $tmpMaxValue;
                $optiTicks = $i;
            }
        }
        return $optiMax;
    }
    function bestTick($maxValue, $mostTicks){
        $minimum = $maxValue / $mostTicks;
        $magnitude = pow(10,floor(log($minimum) / log(10)));
        $residual = $minimum / $magnitude;
        if ($residual > 5){
            $tick = 10 * $magnitude;
        } elseif ($residual > 2) {
            $tick = 5 * $magnitude;
        } elseif ($residual > 1){
            $tick = 2 * $magnitude;
        } else {
            $tick = $magnitude;
        }
        return ($tick * $mostTicks);
    }
    

    的Python:

    import math
    
    def BestTick(largest, mostticks):
        minimum = largest / mostticks
        magnitude = 10 ** math.floor(math.log(minimum) / math.log(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
    
    value = int(input(""))
    optMax = value * 2
    for i in range(5,11):
        maxValue = BestTick(value,i) * i
        print maxValue
        if (optMax > maxValue) and (maxValue > value  + (value*.05)):
            optMax = maxValue
            optTicks = i
    print "\nTest Value: " + str(value + (value * .05)) + "\n\nChart Height: " + str(optMax) + " Ticks: " + str(optTicks)
    

6 个答案:

答案 0 :(得分:5)

这是来自之前的类似问题:

Algorithm for "nice" grid line intervals on a graph

  

我用粗野的方式做到了这一点   力法。首先,搞清楚   你能得到的最大刻度数   适合空间。除以总数   数值范围的数量   蜱;这是最低   蜱的间距。现在计算   对数基数为10的地板   得到刻度的大小,和   除以这个值。你应该结束   在1到1的范围内   10.只需选择大于或等于值的圆数   乘以对数   早先算过。这是你的   最后的刻度线间距。

     

Python中的示例:

import math

def BestTick(largest, mostticks):
    minimum = largest / mostticks
    magnitude = 10 ** math.floor(math.log(minimum) / math.log(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

答案 1 :(得分:3)

过去我用蛮力的方式做到了这一点。这里有一大块C ++代码可以很好地工作......但对于硬编码的下限和上限(0和5000):

int PickYUnits()
{
    int MinSize[8] = {20, 20, 20, 20, 20, 20, 20, 20};
    int ItemsPerUnit[8] = {5, 10, 20, 25, 50, 100, 250, 500};
    int ItemLimits[8] = {20, 50, 100, 250, 500, 1000, 2500, 5000};
    int MaxNumUnits = 8;
    double PixelsPerY;
    int PixelsPerAxis;
    int Units;

    //
    // Figure out the max from the dataset
    //  - Min is always 0 for a bar chart
    //
    m_MinY = 0;
    m_MaxY = -9999999;
    m_TotalY = 0;
    for (int j = 0; j < m_DataPoints.GetSize(); j++) {
        if (m_DataPoints[j].m_y > m_MaxY) {
            m_MaxY = m_DataPoints[j].m_y;
        }

        m_TotalY += m_DataPoints[j].m_y;
    }

    //
    // Give some space at the top
    //
    m_MaxY = m_MaxY + 1;


    //
    // Figure out the size of the range
    //
    double yRange = (m_MaxY - m_MinY);

    //
    // Pick the initial size
    //
    Units = MaxNumUnits;
    for (int k = 0; k < MaxNumUnits; k++)
    {
        if (yRange < ItemLimits[k])
        {
            Units = k;
            break;
        }
    }

    //
    // Adjust it upwards based on the space available
    //
    PixelsPerY = m_rcGraph.Height() / yRange;
    PixelsPerAxis = (int)(PixelsPerY * ItemsPerUnit[Units]);

    while (PixelsPerAxis < MinSize[Units]){
        Units += 1;
        PixelsPerAxis = (int)(PixelsPerY * ItemsPerUnit[Units]);
        if (Units == 5)
            break;
    }


    return ItemsPerUnit[Units];
}

然而,你所说的内容调整了我。要选择漂亮的轴编号,“漂亮编号”的定义会有所帮助:

  • “nice”数字是指具有3个或更少非零数字的数字(例如1230000)
  • “漂亮”的数字与零数字相同或几个非零数字(例如1230不好,1200就不错)
  • 最好的数字是3个零的倍数(例如“1,000”,“1,000,000”)
  • 第二个最好的数字是多个3个零加2个零的数量(例如“1,500,000”,“1,200”)

不确定上述定义是否“正确”或实际上是否有用(但如果手头有定义,那么设计算法就变得更简单了。)

答案 2 :(得分:3)

你最多可以得到两个有效数字。以下伪代码应该有效:

// maxValue is the largest value in your chart
magnitude = floor(log10(maxValue))
base = 10^(magnitude - 1)
chartHeight = ceiling(maxValue / base) * base

例如,如果maxValue为1357,则幅度为3,基数为100.除以100,向上舍入,再乘以100,得到舍入到下一个100的倍数的结果,即舍入最多两个重要数字。在这种情况下,结果如果1400(1357⇒13.57⇒14⇒1400)。

答案 3 :(得分:3)

稍微改进并测试......(适用于单位的分数而不仅仅是整数)

public void testNumbers() {
        double test = 0.20000;

        double multiple = 1;
        int scale = 0;
        String[] prefix = new String[]{"", "m", "u", "n"};
        while (Math.log10(test) < 0) {
            multiple = multiple * 1000;
            test = test * 1000;
            scale++;
        }

        double tick;
        double minimum = test / 10;
        double magnitude = 100000000;
        while (minimum <= magnitude){
            magnitude = magnitude / 10;
        }

        double residual = test / (magnitude * 10);
        if (residual > 5) {
            tick = 10 * magnitude;
        } else if (residual > 2) {
            tick = 5 * magnitude;
        } else if (residual > 1) {
            tick = 2 * magnitude;
        } else {
            tick = magnitude;
        }

        double curAmt = 0;

        int ticks = (int) Math.ceil(test / tick);

        for (int ix = 0; ix < ticks; ix++) {
            curAmt += tick;
            BigDecimal bigDecimal = new BigDecimal(curAmt);
            bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP);
            System.out.println(bigDecimal.stripTrailingZeros().toPlainString() + prefix[scale] + "s");
        }

        System.out.println("Value = " + test + prefix[scale] + "s");
        System.out.println("Tick = " + tick + prefix[scale] + "s");
        System.out.println("Ticks = " + ticks);
        System.out.println("Scale = " +  multiple + " : " + scale);


    }

答案 4 :(得分:1)

如果你想要1400在顶部,如何将最后两个参数调整为1400而不是1357:

alt text

答案 5 :(得分:0)

你可以使用div和mod。例如。

假设你希望你的图表以20的增量向上舍入(只是为了使它更像是一个比典型的“10”值更多的数字)。

所以我认为1,11,18全部可以达到20个。但是21,33,38会变成40个。

要想出正确的值,请执行以下操作:

Where divisor = your rounding increment.

divisor = 20
multiple = maxValue / divisor;  // Do an integer divide here. 
if (maxValue modulus divisor > 0)
   multiple++;

graphMax = multiple * maxValue;

现在让我们插入实数:

divisor = 20;
multiple = 33 / 20; (integer divide)
so multiple = 1
if (33 modulus 20 > 0)  (it is.. it equals 13) 
   multiple++;

so multiple = 2;
graphMax = multiple (2) * maxValue (20);
graphMax = 40;