当前位置:  开发笔记 > 编程语言 > 正文

为图形的Y轴选择有吸引力的线性刻度

如何解决《为图形的Y轴选择有吸引力的线性刻度》经验,为你挑选了4个好方法。

我正在编写一些代码来在我们的软件中显示条形图(或线条).一切都很顺利.令我难过的是标记Y轴.

调用者可以告诉我他们想要Y标记的标记有多精细,但我似乎仍然坚持要以"有吸引力"的方式标记它们.我无法描述"有吸引力",也许你也不能,但是当我们看到它时我们就知道了,对吗?

所以如果数据点是:

   15, 234, 140, 65, 90

并且用户在Y轴上要求10个标签,用纸和铅笔进行一点点处理:

  0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250

那里有10个(不包括0),最后一个延伸超过最高值(234 <250),并且它是一个"好"的增量,每个增加25.如果他们要求8个标签,那么30的增量看起来不错:

  0, 30, 60, 90, 120, 150, 180, 210, 240

九会很棘手.也许只是使用了8或10,并将其称为足够接近就可以了.当一些观点是否定的时候该怎么办?

我可以看到Excel很好地解决了这个问题.

有没有人知道解决这个问题的通用算法(甚至有些蛮力没问题)?我不必快速做,但它应该看起来不错.



1> Toon Krijthe..:

很久以前我写了一个图形模块,很好地涵盖了这一点.挖掘灰色质量得到以下内容:

确定数据的下限和上限.(注意下限=上限的特殊情况!

将范围除以所需的刻度数.

将蜱范围缩小到很好的数量.

相应地调整下限和上限.

让我们举个例子:

15, 234, 140, 65, 90 with 10 ticks

    下限= 15

    上限= 234

    范围= 234-15 = 219

    滴答范围= 21.9.这应该是25.0

    新的下限= 25*圆(15/25)= 0

    新上限= 25*圆(1 + 235/25)= 250

所以范围= 0,25,50,...,225,250

您可以通过以下步骤获得良好的滴答范围:

    除以10 ^ x,结果在0.1和1.0之间(包括0.1除1).

    相应翻译:

    0.1 - > 0.1

    <= 0.2 - > 0.2

    <= 0.25 - > 0.25

    <= 0.3 - > 0.3

    <= 0.4 - > 0.4

    <= 0.5 - > 0.5

    <= 0.6 - > 0.6

    <= 0.7 - > 0.7

    <= 0.75 - > 0.75

    <= 0.8 - > 0.8

    <= 0.9 - > 0.9

    <= 1.0 - > 1.0

    乘以10 ^ x.

在这种情况下,21.9除以10 ^ 2得到0.219.这是<= 0.25所以我们现在有0.25.乘以10 ^ 2得到25.

让我们看一下8个刻度的相同例子:

15, 234, 140, 65, 90 with 8 ticks

    下限= 15

    上限= 234

    范围= 234-15 = 219

    滴答范围= 27.375

      除以10 ^ 2得到0.27375,转换为0.3,得到(乘以10 ^ 2)30.

    新下界= 30*圆(15/30)= 0

    新上限= 30*圆(1 + 235/30)= 240

给出你要求的结果;-).

------由KD添加------

这是在不使用查找表等的情况下实现此算法的代码...:

double range = ...;
int tickCount = ...;
double unroundedTickSize = range/(tickCount-1);
double x = Math.ceil(Math.log10(unroundedTickSize)-1);
double pow10x = Math.pow(10, x);
double roundedTickRange = Math.ceil(unroundedTickSize / pow10x) * pow10x;
return roundedTickRange;

一般来说,刻度数包括底部刻度,因此实际的y轴段比刻度数少一个.


这是一个很好的答案.非常感谢.
@JoelAnair谢谢你让悲伤的一天变得更加光明.
你引用除以10 ^ x并乘以10 ^ x.应该注意的是x可以这样找到:'double x = Math.Ceiling(Math.Log10(tickRange));'

2> 小智..:

这是我正在使用的PHP示例.此函数返回一个漂亮的Y轴值数组,其中包含传入的最小和最大Y值.当然,此例程也可用于X轴值.

它允许您"建议"您可能需要多少个刻度,但例程将返回看起来不错的刻度.我添加了一些示例数据并显示了这些结果.

#!/usr/bin/php -q
 2)
    $ticks -= 2;
  // Get raw step value
  $tempStep = $range/$ticks;
  // Calculate pretty step value
  $mag = floor(log10($tempStep));
  $magPow = pow(10,$mag);
  $magMsd = (int)($tempStep/$magPow + 0.5);
  $stepSize = $magMsd*$magPow;

  // build Y label array.
  // Lower and upper bounds calculations
  $lb = $stepSize * floor($yMin/$stepSize);
  $ub = $stepSize * ceil(($yMax/$stepSize));
  // Build array
  $val = $lb;
  while(1)
  {
    $result[] = $val;
    $val += $stepSize;
    if($val > $ub)
      break;
  }
  return $result;
}

// Create some sample data for demonstration purposes
$yMin = 60;
$yMax = 330;
$scale =  makeYaxis($yMin, $yMax);
print_r($scale);

$scale = makeYaxis($yMin, $yMax,5);
print_r($scale);

$yMin = 60847326;
$yMax = 73425330;
$scale =  makeYaxis($yMin, $yMax);
print_r($scale);
?>

样本数据的结果输出

# ./test1.php
Array
(
    [0] => 60
    [1] => 90
    [2] => 120
    [3] => 150
    [4] => 180
    [5] => 210
    [6] => 240
    [7] => 270
    [8] => 300
    [9] => 330
)

Array
(
    [0] => 0
    [1] => 90
    [2] => 180
    [3] => 270
    [4] => 360
)

Array
(
    [0] => 60000000
    [1] => 62000000
    [2] => 64000000
    [3] => 66000000
    [4] => 68000000
    [5] => 70000000
    [6] => 72000000
    [7] => 74000000
)



3> Drew Noakes..:

试试这个代码.我已经在一些图表场景中使用它并且效果很好.它也很快.

public static class AxisUtil
{
    public static float CalculateStepSize(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;
    }
}



4> Pyrolistical..:

听起来好像来电者不会告诉你它想要的范围.

所以你可以自由地改变终点,直到你的标签数量很好地整除它.

让我们定义"好".如果标签关闭,我会称之为好:

1. 2^n, for some integer n. eg. ..., .25, .5, 1, 2, 4, 8, 16, ...
2. 10^n, for some integer n. eg. ..., .01, .1, 1, 10, 100
3. n/5 == 0, for some positive integer n, eg, 5, 10, 15, 20, 25, ...
4. n/2 == 0, for some positive integer n, eg, 2, 4, 6, 8, 10, 12, 14, ...

查找数据系列的最大值和最小值.我们称之为以下几点:

min_point and max_point.

现在你需要做的就是找到3个值:

- start_label, where start_label < min_point and start_label is an integer
- end_label, where end_label > max_point and end_label is an integer
- label_offset, where label_offset is "nice"

符合等式:

(end_label - start_label)/label_offset == label_count

可能有很多解决方案,所以选择一个.大多数时候我打赌你可以设置

start_label to 0

所以试试不同的整数

end_label

直到偏移"很好"

推荐阅读
TXCWB_523
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有