Pregunta

Necesito hacer un gráfico con un valor máximo optimizado del eje y .

El método actual que tengo para hacer gráficos simplemente usa el valor máximo de todos los gráficos, luego lo divide por diez y lo usa como líneas de cuadrícula. No lo escribí.

Nota de actualización: estos gráficos se han cambiado. Tan pronto como arreglé el código, mis gráficos dinámicos comenzaron a funcionar, haciendo que esta pregunta no tuviera sentido (porque los ejemplos ya no tenían errores). He actualizado estos con imágenes estáticas, pero algunas de las respuestas hacen referencia a diferentes valores. Mantenlo en mente. Gráfico antiguo En febrero hubo entre 12003 y 14003 llamadas entrantes. Informativo, pero feo.

Me gustaría evitar gráficos que parecen un mono con los números de eje y .

El uso de la API de gráficos de Google ayuda un poco, pero todavía no es exactamente lo que quiero. Gráfico de API de Google Los números están limpios, pero la parte superior del valor y es siempre el mismo que el valor máximo en el gráfico. Este gráfico escala de 0 a 1357. Necesito haber calculado el valor adecuado de 1400, problemáticamente .


Estoy lanzando la rbobby 's defanition of a' nice 'número aquí porque lo explica muy bien.

  • A "agradable" número es uno que tiene 3 o menos dígitos distintos de cero (por ejemplo, 1230000)
  • A "agradable" el número tiene los mismos o pocos dígitos distintos de cero que cero dígitos (por ejemplo, 1230 no es bueno, 1200 es bueno)
  • Los números más bonitos son unos con múltiplos de 3 ceros (p. ej. " 1,000 " ;, " 1,000,000 ")
  • Los segundos números más bonitos son onces con múltiplos de 3 ceros más 2 ceros (por ejemplo, "1,500,000", "1,200")

Solución

 Nuevo gráfico

Encontré la manera de obtener los resultados que quiero usando una versión modificada de la idea de Mark Ransom.

Puño, el código de Mark Ransom determina el espacio óptimo entre ticks, cuando se le da el número de ticks. A veces, este número termina siendo más del doble del valor más alto en el gráfico, dependiendo de cuántas líneas de cuadrícula desee.

Lo que estoy haciendo es ejecutar el código de Mark con 5, 6, 7, 8, 9 y 10 líneas de cuadrícula (ticks) para encontrar cuál de ellas es la más baja. Con un valor de 23, la altura del gráfico va a 25, con una línea de cuadrícula en 5, 10, 15, 20 y 25. Con un valor de 26, la altura del gráfico es 30, con líneas de cuadrícula en 5, 10 , 15, 20, 25 y 30. Tiene el mismo espacio entre las líneas de la cuadrícula, pero hay más de ellas.

Entonces, estos son los pasos para copiar lo que Excel hace para hacer que los gráficos sean elegantes.

  1. Aumente temporalmente el valor más alto del gráfico en aproximadamente un 5% (para que siempre haya algo de espacio entre el punto más alto del gráfico y la parte superior del área del gráfico. Queremos que 99.9 se redondee a 120)
  2. Encuentre la ubicación óptima de la línea de la cuadrícula para cuadrículas de 5, 6, 7, 8, 9 y 10 líneas.
  3. Elija el más bajo de esos números. Recuerde la cantidad de líneas de cuadrícula que se necesitaron para obtener ese valor.
  4. Ahora tiene la altura óptima del gráfico. Las líneas / barra nunca chocarán contra la parte superior del gráfico y tendrá la cantidad óptima de ticks.

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)
¿Fue útil?

Solución

Esto es de una pregunta similar anterior:

Algoritmo para " nice " intervalos de línea de cuadrícula en un gráfico

  

He hecho esto con una especie de bruto   método de fuerza. Primero, descubra el   número máximo de marcas de verificación que pueda   encajar en el espacio. Divide el total   rango de valores por el número de   garrapatas este es el mínimo   espaciado de la garrapata. Ahora calcular   el piso del logaritmo base 10 a   obtener la magnitud de la marca y   dividir por este valor. Deberías terminar   con algo en el rango de 1 a   10. Simplemente elija el número redondo mayor o igual al valor y   multiplícalo por el logaritmo   calculado anteriormente Esta es tu   espaciado final de ticks.

     

Ejemplo en 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

Otros consejos

En el pasado, he hecho esto de una manera brutal. Aquí hay un fragmento de código C ++ que funciona bien ... pero para un límite inferior y superior codificados (0 y 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];
}

Sin embargo, algo en lo que has dicho me modificó. Para elegir buenos números de eje, una definición de "buen número" ayudaría:

  • A "agradable" número es uno que tiene 3 o menos dígitos distintos de cero (por ejemplo, 1230000)
  • A "agradable" el número tiene los mismos o pocos dígitos distintos de cero que cero dígitos (por ejemplo, 1230 no es bueno, 1200 es bueno)
  • Los números más bonitos son unos con múltiplos de 3 ceros (p. ej. " 1,000 " ;, " 1,000,000 ")
  • Los segundos números más bonitos son onces con múltiplos de 3 ceros más 2 ceros (por ejemplo, "1,500,000", "1,200")

No estoy seguro si la definición anterior es "correcta" o realmente útil (pero con la definición en la mano, entonces se convierte en una tarea más simple diseñar un algoritmo).

Podría redondear hasta dos cifras significativas. El siguiente pseudocódigo debería funcionar:

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

Por ejemplo, si maxValue es 1357, entonces la magnitud es 3 y la base es 100. Dividir por 100, redondear y multiplicar por 100 tiene el resultado de redondear al siguiente múltiplo de 100 , es decir, redondeando hasta dos cifras significativas. En este caso, el resultado es 1400 (1357 ? 13.57 ? 14 ? 1400).

Un ligero refinamiento y probado ... (funciona para fracciones de unidades y no solo enteros)

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


    }

Si desea 1400 en la parte superior, ¿qué le parece ajustar los dos últimos parámetros a 1400 en lugar de 1357:

 texto alternativo

Puedes usar div y mod. Por ejemplo.

Supongamos que desea que su gráfico se redondee en incrementos de 20 (solo para que sea un número más arbitrario que su valor típico de "10").

Entonces supongo que 1, 11, 18 se redondearían a 20. Pero 21, 33, 38 se redondearían a 40.

Para obtener el valor correcto, haga lo siguiente:

Where divisor = your rounding increment.

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

graphMax = multiple * maxValue;

Así que ahora agreguemos números reales:

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top