Подтвердить что ты не робот

Разумное оптимизированное масштабирование диаграммы

Мне нужно составить диаграмму с максимальным значением максимальной оси y.

В текущем методе создания диаграмм просто используется максимальное значение всех графиков, затем делит его на десять и использует это как линии сетки. Я не писал это.

Обновить Примечание: Эти графики были изменены. Как только я исправил код, мои динамические графики начали работать, делая этот вопрос бессмысленным (потому что в примерах больше не было ошибок). Я обновил их со статическими изображениями, но некоторые ответы подтверждают разные значения. Запомни. alt text http://i42.tinypic.com/nwzr5s.jpg В феврале между входящими звонками было 12003 и 14003. Информативный, но уродливый.

Я бы хотел избежать диаграмм, которые выглядят так, как обезьяна придумала номера оси y.

Использование API диаграмм Google помогает немного, но это все еще не совсем то, что я хочу. alt text Числа чистые, но верхняя часть значения y всегда совпадает с максимальным значением на графике. Эта диаграмма масштабируется от 0 до 1357. Мне нужно вычислить правильное значение 1400, проблематично.


Я забрасываю rbobby из-за "хорошего" номера здесь, потому что он так хорошо объясняет.

  • "Хорошее" число - это число, которое содержит 3 или менее ненулевых цифр (например, 1230000).
  • "Хорошее" число имеет одинаковые или несколько ненулевых цифр, чем нулевые цифры (например, 1230 не очень хорошо, 1200 - приятный).
  • Самые красивые цифры - это те, у которых кратные 3 нули (например, "1,000", "1,000,000" ).
  • Вторые самые приятные числа: onces с множеством из 3 нулей плюс 2 нуля (например, "1500 000", "1200" ).

Решение

alt text http://i43.tinypic.com/21jc0no.png Я нашел способ получить результаты, которые я хочу использовать модифицированную версию идеи Марка Рэнсом.

Fist, код Mark Ransom определяет оптимальное расстояние между тиками при задании количества тиков. Иногда это число больше, чем в два раза больше, чем самое высокое значение на графике, в зависимости от того, сколько строк сетки вы хотите.

Что я делаю, так это то, что я запускаю Mark с 5, 6, 7, 8, 9 и 10 сетками (тиками), чтобы определить, какая из них самая низкая. При значении 23 высота диаграммы составляет 25, а линия сетки - 5, 10, 15, 20 и 25. При значении 26 высота диаграммы составляет 30, а линии сетки - 5, 10, 15, 20, 25 и 30. Он имеет такое же расстояние между линиями сетки, но их больше.

Итак, вот шаги, которые нужно сделать, чтобы скопировать то, что делает Excel, чтобы все графики выглядели.

  • Временно поднимите максимальное значение графика примерно на 5% (так что между верхней точкой графика и верхней частью области графика всегда есть пробел. Мы хотим, чтобы 99.9 округлялось до 120).
  • Найдите оптимальное размещение сетки для сетки 5, 6, 7, 8, 9 и 10 линии.
  • Выберите самый низкий из этих чисел. Помните количество линий сетки, которые потребовались для получения этого значения.
  • Теперь у вас есть оптимальная высота диаграммы. Строки/бар никогда не будут опираться на верхнюю часть диаграммы, и у вас будет оптимальное количество тиков.

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)
4b9b3361

Ответ 1

Это из предыдущего аналогичного вопроса:

Алгоритм для "приятного" интервалы сетки на графике

Я сделал это с видом грубых метод силы. Во-первых, выясните максимальное количество отметок, которые вы можете вписывается в пространство. Разделить общее количество диапазон значений по числу тики; это минимуминтервал тика. Теперь вычислим пол логарифмической базы 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

В прошлом я делал это в грубой силовой форме. Вот кусок кода на С++, который хорошо работает... но для жестко закодированных нижних и верхних пределов (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 number" поможет:

  • "Хорошее" число - это число, которое содержит 3 или менее ненулевых цифр (например, 1230000).
  • "Хорошее" число имеет одинаковые или несколько ненулевых цифр, чем нулевые цифры (например, 1230 не очень хорошо, 1200 - приятный).
  • Самые красивые цифры - это те, у которых кратные 3 нули (например, "1,000", "1,000,000" ).
  • Вторые самые приятные числа: onces с множеством из 3 нулей плюс 2 нуля (например, "1500 000", "1200" ).

Не уверен, что указанное выше определение является "правильным" или действительно полезным (но с определением в руке оно становится более простой задачей для разработки алгоритма).

Ответ 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).

Ответ 4

Небольшое уточнение и тестирование... (работает для фракций единиц, а не только целых чисел)

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

Если вы хотите 1400 в верхней части, как насчет настройки последних двух параметров на 1400 вместо 1357:

alt text

Ответ 6

Вы можете использовать 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;