Question

Je dois créer un graphique avec une valeur maximale optimisée pour l'axe y .

Ma méthode actuelle de création de graphiques utilise simplement la valeur maximale de tous les graphiques, puis la divise par dix et l’utilise comme lignes de la grille. Je ne l'ai pas écrit.

Mise à jour Remarque: Ces graphiques ont été modifiés. Dès que j'ai corrigé le code, mes graphiques dynamiques ont commencé à fonctionner, rendant cette question absurde (car les exemples ne contenaient plus d'erreur). J'ai mis à jour ces images avec des images statiques, mais certaines réponses répercutent des valeurs différentes. Garde cela à l'esprit. Ancien graphique Il y a eu entre 12003 et 14003 appels entrants jusqu'à présent en février. Informatif, mais moche.

J'aimerais éviter les graphiques qui ressemblent à un singe avec les y nombres-axes.

Utiliser l'API Google Maps aide un peu, mais ce n'est toujours pas ce que je veux. Graphique API Google Les nombres sont clairs, mais le haut de la valeur y est toujours identique à la valeur maximale du graphique. Ce graphique varie de 0 à 1357. Je dois avoir calculé la valeur appropriée de 1400, de manière problématique .

Je lance la défanition d'un 'nice par rbobby "Numéro ici parce qu'il explique si bien.

  • Un "sympa" numéro est celui qui a 3 ou moins des chiffres non nuls (par exemple, 1230000)
  • Un "sympa" le nombre a le même nombre ou peu de chiffres non nuls que le chiffre zéro (par exemple, 1230 n'est pas beau, 1200 est beau)
  • Les plus beaux nombres sont ceux avec des multiples de 3 zéros (par exemple. "1 000", "1 000 000")
  • Les deuxièmes plus beaux nombres sont des onces avec des multiples de 3 zéros plus 2 zéros (par exemple, "1 500 000", "1 200")

Solution

 Nouveau graphique

J'ai trouvé le moyen d'obtenir les résultats souhaités en utilisant une version modifiée de l'idée de Mark Ransom.

Fist, le code de Mark Ransom détermine l’espacement optimal entre les ticks, en fonction du nombre de ticks. Parfois, ce nombre finit par être plus de deux fois supérieur à la valeur la plus élevée du graphique, en fonction du nombre de lignes de la grille que vous souhaitez.

Ce que je fais, c’est que je lance le code de Mark avec 5, 6, 7, 8, 9 et 10 lignes de quadrillage (ticks) pour trouver laquelle est la plus basse. Avec une valeur de 23, la hauteur du graphique passe à 25, avec une ligne de quadrillage à 5, 10, 15, 20 et 25. Avec une valeur de 26, la hauteur du graphique est de 30, avec des lignes de quadrillage à 5, 10. , 15, 20, 25 et 30. L’espacement entre les lignes de la grille est identique, mais elles sont plus nombreuses.

Alors, voici les étapes à suivre pour copier ce que Excel fait pour créer des graphiques fantaisistes.

  1. Augmentez provisoirement la valeur la plus élevée du graphique d'environ 5% (afin qu'il y ait toujours un espace entre le point le plus haut du graphique et le haut de la zone de graphique. Nous voulons que 99,9 soit arrondi à 120)
  2. Trouvez le placement optimal de la ligne de grille pour 5, 6, 7, 8, 9 et 10 grilles lignes.
  3. Choisissez le plus bas de ces chiffres. Rappelez-vous le nombre de lignes de grille qu'il a fallu pour obtenir cette valeur.
  4. Vous avez maintenant la hauteur optimale de la carte. Les lignes / barres ne seront jamais alignées avec le haut du graphique et vous obtiendrez un nombre optimal 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)
Était-ce utile?

La solution

Ceci provient d'une question similaire précédente:

Algorithme pour "bien" et "bien" intervalles de ligne de grille sur un graphique

  

Je l'ai fait avec une sorte de brute   méthode de la force. Tout d'abord, comprendre le   nombre maximum de graduations que vous pouvez   s'insérer dans l'espace. Diviser le total   plage de valeurs par le nombre de   les tiques; c'est le minimum   espacement de la tique. Maintenant calculer   le sol de la base du logarithme 10 à   obtenir l'ampleur de la tique, et   diviser par cette valeur. Tu devrais finir   avec quelque chose dans la gamme de 1 à   10. Choisissez simplement le nombre de tours supérieur ou égal à la valeur et   multipliez-le par le logarithme   calculé plus tôt. C'est ton   espacement final des ticks.

     

Exemple 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

Autres conseils

Dans le passé, j’ai fait cela d’une manière brutale. Voici un morceau de code C ++ qui fonctionne bien ... mais pour les limites inférieures et supérieures codées en dur (0 et 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];
}

Cependant, quelque chose dans ce que vous avez dit m'a modifié. Pour choisir les numéros d’axe de Nice, définissez le "nombre de Nice". aiderait:

  • Un "sympa" numéro est celui qui a 3 ou moins des chiffres non nuls (par exemple, 1230000)
  • Un "sympa" le nombre a le même nombre ou peu de chiffres non nuls que le chiffre zéro (par exemple, 1230 n'est pas beau, 1200 est beau)
  • Les plus beaux nombres sont ceux avec des multiples de 3 zéros (par exemple. "1 000", "1 000 000")
  • Les deuxièmes plus beaux nombres sont des onces avec des multiples de 3 zéros plus 2 zéros (par exemple, "1 500 000", "1 200")

Je ne suis pas sûr que la définition ci-dessus soit "correcte". ou réellement utile (mais avec la définition en main, il devient alors plus simple de concevoir un algorithme).

Vous pouvez arrondir à deux chiffres significatifs. Le pseudocode suivant devrait fonctionner:

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

Par exemple, si maxValue est égal à 1357, magnitude égale à 3 et base à 100. La division par 100, l’arrondi vers le haut et la multiplication par 100 ont pour résultat d’arrondir au prochain multiple de 100. , c'est-à-dire en arrondissant à deux chiffres significatifs. Dans ce cas, le résultat est 1400 (1357 ? 13.57 ? 14 ? 1400).

Un léger raffinement et testé ... (fonctionne pour des fractions d'unités et pas seulement des entiers)

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 vous voulez 1400 en haut, pourquoi ne pas régler les deux derniers paramètres sur 1400 au lieu de 1357:

 alt text

Vous pouvez utiliser div et mod. Par exemple.

Supposons que vous souhaitiez que votre graphique soit arrondi par incréments de 20 (juste pour en faire un nombre plus arbitraire que votre valeur typique "10").

Je suppose donc que 1, 11, 18 arrondiraient tous à 20. Mais 21, 33, 38 arrondiraient à 40.

Pour obtenir la bonne valeur, procédez comme suit:

Where divisor = your rounding increment.

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

graphMax = multiple * maxValue;

Alors maintenant, greffons les vrais nombres:

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top