71 votes

Algorithme pour les "jolis" intervalles de lignes de grille sur un graphique

J'ai besoin d'un raisonnablement algorithme intelligent de venir avec "gentil" lignes de la grille pour un graphique (graphique).

Supposons, par exemple, un histogramme des valeurs de 10, 30, 72 et 60. Vous le savez:

Valeur Min: 10 Valeur Max: 72 Gamme: 62

La première question est: que voulez-vous commencer? Dans ce cas, 0 serait l'intuition de la valeur, mais cela ne tient pas sur d'autres ensembles de données donc je suppose:

Grille min valeur doit être 0 ou un "gentil", valeur inférieure à la valeur min des données dans la gamme. Alternativement, il peut être spécifié.

Grille de max valeur doit être un "gentil" valeur au-dessus de la valeur max de la gamme. Alternativement, il peut être spécifié (par exemple, vous pourriez vouloir de 0 à 100 si vous êtes montrant les pourcentages, indépendamment de la valeur réelle).

Le nombre de lignes de la grille (tiques) dans la plage doit être spécifié ou un nombre dans un intervalle donné (par exemple 3-8) de telle sorte que les valeurs sont "sympa" (c'est à dire des nombres ronds) et permet d'optimiser l'utilisation de la zone de graphique. Dans notre exemple, 80 serait judicieux max serait d'utiliser 90% de la carte de hauteur (72/80) alors que 100 permettrait de créer plus d'espace perdu.

Quelqu'un connais un bon algorithme pour cela? La langue n'est pas pertinente car je vais la mettre en œuvre dans ce que j'en ai besoin.

34voto

Mark Ransom Points 132545

J'ai fait cela avec une sorte de "force brute" de la méthode. Tout d'abord, déterminez le nombre maximum de marques de graduation vous pouvez adapter dans l'espace. Diviser le total plage de valeurs par le nombre de tiques; c'est le minimum d'espacement de la tique. Calculons maintenant le plancher de le logarithme en base 10 pour obtenir de l'ampleur de la tique, et de diviser par cette valeur. Vous devriez vous retrouver avec quelque chose dans la gamme de 1 à 10. Il suffit de choisir le numéro de la ronde supérieure ou égale à la valeur et le multiplier par le logarithme calculé précédemment. C'est votre dernier tick de l'espacement.

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

30voto

Adam Liss Points 27815

Il y a 2 morceaux pour le problème:

  1. Déterminer l'ordre de grandeur impliqués, et
  2. Tour à quelque chose de pratique.

Vous pouvez gérer la première partie en utilisant les logarithmes:

range = max - min;  
exponent = int(log(range));       // See comment below.
magnitude = pow(10, exponent);

Ainsi, par exemple, si votre portée est de 50 - 1200, l'exposant est 3 et la magnitude est de 1000.

Ensuite aborder la deuxième partie en décidant de la façon dont de nombreuses subdivisions que vous voulez dans votre grille:

value_per_division = magnitude / subdivisions;

C'est un calcul approximatif car l'exposant a été tronquée à un entier. Vous pouvez ajuster la puissance de calcul pour traiter des conditions aux limites de mieux, par exemple, en arrondissant au lieu de prendre l' int() si vous vous retrouvez avec trop de subdivisions.

17voto

Drew Noakes Points 69288

J'utilise l'algorithme suivant. C'est semblable aux autres postés ici, mais c'est le premier exemple en C #.

 public static class AxisUtil
{
    public static float CalcStepSize(float range, float targetSteps)
    {
        // calculate an initial guess at step size
        float tempStep = range/targetSteps;

        // get the magnitude of the step size
        float mag = (float)Math.Floor(Math.Log10(tempStep));
        float magPow = (float)Math.Pow(10, mag);

        // calculate most significant digit of the new step size
        float magMsd = (int)(tempStep/magPow + 0.5);

        // promote the MSD to either 1, 2, or 5
        if (magMsd > 5.0)
            magMsd = 10.0f;
        else if (magMsd > 2.0)
            magMsd = 5.0f;
        else if (magMsd > 1.0)
            magMsd = 2.0f;

        return magMsd*magPow;
    }
}
 

11voto

Christoph Rüegg Points 1620

CPAN fournit une implémentation ici (voir lien source)

Voir aussi algorithme Tickmark pour un axe de graphe

FYI, avec vos données d'échantillon:

  • Érable: Min = 8, Max = 74, étiquettes = 10,20, .., 60,70, tiques = 10,12,14, .. 70,72
  • MATLAB: Min = 10, Max = 80, étiquettes = 10,20 ,, .., 60,80

1voto

FryGuy Points 5999

Une autre idée est d'avoir la gamme de l'axe correspondre à l'échelle des valeurs, mais de mettre les marques de graduation à la position appropriée.. c'est à dire de 7 à 22 faire:

[- - - | - - - - | - - - - | - - ]
 10 15 20

Comme pour la sélection de la tique espacement, je dirais tout nombre de la forme 10^x * i / n, où i < n et 0 < n < 10. Générer la liste, et de les trier, et vous pouvez trouver le plus grand nombre plus petit que value_per_division (comme dans adam_liss) à l'aide d'une recherche binaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X