문제

최적화 된 차트를 만들어야합니다 와이 축 최대 값.

차트를 만드는 현재 방법은 단순히 모든 그래프의 최대 값을 사용한 다음 10으로 나누고이를 그리드 라인으로 사용합니다. 나는 그것을 쓰지 않았다.

업데이트 참고 : 이 그래프가 변경되었습니다. 코드를 수정하자마자 동적 그래프가 작동하기 시작 하여이 질문이 무의미하게 만들었습니다 (예제는 더 이상 오류가 없었기 때문에). 정적 이미지로이를 업데이트했지만 일부 답변은 다른 값을 굴절시킵니다. 그것을 명심하십시오.Old Chart2 월까지 지금까지 12003에서 14003 개의 인바운드 콜이있었습니다. 유익하지만 추악한.

원숭이처럼 보이는 차트를 피하고 싶습니다. 와이-축 숫자.

Google 차트 API를 사용하면 약간 도움이되지만 여전히 내가 원하는 것은 아닙니다.Google API Chart숫자는 깨끗하지만 y 값의 상단은 항상 차트의 최대 값과 동일합니다. 이 차트는 0에서 1357 사이입니다. 1400의 적절한 값을 계산해야합니다. 문제로.


나는 던지고있다 rbobby'좋은'숫자가 여기에서 너무 잘 설명되기 때문에 여기에서 '좋은'숫자의 방어가 여기에 있습니다.

  • "좋은"숫자는 0 이외의 숫자가 3 이하로 숫자입니다 (예 : 1230000)
  • "좋은"숫자는 0 자리 숫자와 0이 아닌 숫자와 동일하거나 적은 숫자를 가지고 있습니다 (예 : 1230은 좋지 않습니다. 1200은 좋음)
  • 가장 좋은 숫자는 3 개의 0 인 숫자입니다 (예 : "1,000", "1,000,000")
  • 두 번째로 좋은 숫자는 3 개의 0과 2 개의 0의 멀티가있는 onces입니다 (예 : "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. 이제 최적의 차트 높이가 있습니다. 라인/바는 차트의 상단에 맞지 않으며 최적의 진드기 수가 있습니다.

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)
도움이 되었습니까?

해결책

이것은 이전의 유사한 질문에서 나온 것입니다.

그래프의 "좋은"그리드 라인 간격에 대한 알고리즘

나는 일종의 무자비한 힘으로 이것을했다. 먼저, 공간에 맞출 수있는 최대 점령 마크 수를 파악하십시오. 총 값의 총 범위를 진드기 수로 나눕니다. 이것이 최저한의진드기의 간격. 이제 로그베이스 10의 바닥을 계산하여 진드기의 크기를 얻고이 값으로 나눕니다. 1 ~ 10 범위의 무언가로 끝나야합니다. 단순히 값보다 큰 원형 번호를 선택하고 이전에 계산 된 로그로 곱하십시오. 이것은 마지막 진드기 간격입니다.

파이썬의 예 :

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

다른 팁

과거에 나는 이것을 무자비한 힘으로 한 종류의 방식으로 해냈다. 다음은 잘 작동하는 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];
}

그러나 당신이 말한 것에서 무언가가 나를 조정했습니다. 멋진 축 번호를 선택하려면 "좋은 숫자"의 정의가 도움이 될 것입니다.

  • "좋은"숫자는 0 이외의 숫자가 3 이하로 숫자입니다 (예 : 1230000)
  • "좋은"숫자는 0 자리 숫자와 0이 아닌 숫자와 동일하거나 적은 숫자를 가지고 있습니다 (예 : 1230은 좋지 않습니다. 1200은 좋음)
  • 가장 좋은 숫자는 3 개의 0 인 숫자입니다 (예 : "1,000", "1,000,000")
  • 두 번째로 좋은 숫자는 3 개의 0과 2 개의 0의 멀티가있는 onces입니다 (예 : "1,500,000", "1,200")

위의 정의가 "올바른"상태인지 또는 실제로 도움이되는지 확실하지 않지만 (정의가 있으면 알고리즘을 고안하는 간단한 작업이됩니다).

두 가지 중요한 수치를 반올림 할 수 있습니다. 다음 유사 코드가 작동해야합니다.

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

예를 들어, if maxValue 1357, 크기는 3이고베이스는 100입니다. 100으로 나누고, 반올림하고, 곱하면 100의 다음 배수로 반올림 한 결과, 즉 두 개의 중요한 수치까지 반올림됩니다. 이 경우 결과 1400 (1357 ⇒ 13.57 ⇒ 14 ⇒ 1400).

약간의 개선 및 테스트 ... (정수뿐만 아니라 단위의 일부를 위해 작동합니다)

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


    }

상단에서 1400을 원한다면 마지막 두 매개 변수를 1357 대신 1400으로 조정하는 것은 어떻습니까?

alt text

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;
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top