代码之家  ›  专栏  ›  技术社区  ›  Grant

合理优化的图表缩放

  •  31
  • Grant  · 技术社区  · 17 年前

    我需要做一个优化的图表 Y 轴最大值。

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

    更新说明: 这些图表已经改变了。一旦我修复了代码,我的动态图就开始工作,使得这个问题变得毫无意义(因为示例中不再有任何错误)。我已经用静态图像更新了这些,但是有些答案引用了不同的值。记住这一点。 alt text http://i42.tinypic.com/nwzr5s.jpg 到目前为止,2月份有1200到14003个入境电话。信息丰富,但丑陋。

    我想避开那些看起来像猴子的图表。 Y 轴数。

    使用谷歌图表API有点帮助,但这仍然不是我想要的。 alt text 数字是干净的,但Y值的顶部始终与图表上的最大值相同。这个图表从0到1357。我需要计算出1400的适当值, 有问题地 .


    我投掷 rbobby 因为它很好地解释了这个“好”数字。

    • 一个“不错”的数字是一个有3个或更少的非零数字(例如1230000)
    • “尼斯”数字与零位数字具有相同或少数非零位数字(例如1230不好,1200好)
    • 最好的数字是3个零的倍数(例如“1000”,“1000000”)。
    • 第二个最好的数字是3个零加2个零(例如“1500000”、“1200”)的多个数。

    解决方案

    alt text http://i43.tinypic.com/21jc0no.png 我找到了一种方法来获得我想要的结果,使用马克·兰索姆的想法的修改版本。

    首先,当给定节拍数时,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. 现在您有了最佳图表高度。线条/条形图永远不会与图表顶部对齐,并且您拥有最佳刻度数。

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

    蟒蛇:

    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 回复  |  直到 11 年前
        1
  •  5
  •   Community Mohan Dere    9 年前

    这是前面一个类似的问题:

    Algorithm for "nice" grid line intervals on a graph

    我做这件事有点残忍 力法。首先,找出 最大刻度线数 融入空间。除以总数 数值范围(按数字) 滴答声;这是 最低限度 刻度线的间距。现在计算 对数底10到 获取刻度的大小,以及 除以这个值。你应该结束 在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
    
        2
  •  3
  •   user53794    17 年前

    在过去,我用一种蛮力的方式来做这件事。这里有一大块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];
    }
    

    然而,你所说的话让我很不舒服。要选择尼斯轴编号,“尼斯编号”的定义将有助于:

    • 一个“不错”的数字是一个有3个或更少的非零数字(例如1230000)
    • “尼斯”数字与零位数字具有相同或少数非零位数字(例如1230不好,1200好)
    • 最好的数字是3个零的倍数(例如“1000”,“1000000”)。
    • 第二个最好的数字是3个零加2个零(例如“1500000”、“1200”)的多个数。

    不确定上面的定义是“正确的”还是实际有用的(但是,有了这个定义,设计一个算法就变得更简单了)。

        3
  •  3
  •   Adam Rosenfield    17 年前

    你可以四舍五入到两个重要数字。应使用以下伪代码:

    // 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)。

        4
  •  3
  •   Craig    15 年前

    一点精致和测试…(适用于单位分数而不仅仅是整数)

    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);
    
    
        }
    
        5
  •  1
  •   Community Mohan Dere    9 年前

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

    alt text

        6
  •  0
  •   Macon Pegram    17 年前

    你可以使用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;