我正在编写一些代码来在我们的软件中显示条形图(或线条).一切都很顺利.令我难过的是标记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很好地解决了这个问题.
有没有人知道解决这个问题的通用算法(甚至有些蛮力没问题)?我不必快速做,但它应该看起来不错.
很久以前我写了一个图形模块,很好地涵盖了这一点.挖掘灰色质量得到以下内容:
确定数据的下限和上限.(注意下限=上限的特殊情况!
将范围除以所需的刻度数.
将蜱范围缩小到很好的数量.
相应地调整下限和上限.
让我们举个例子:
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轴段比刻度数少一个.
这是我正在使用的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 )
试试这个代码.我已经在一些图表场景中使用它并且效果很好.它也很快.
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;
}
}
听起来好像来电者不会告诉你它想要的范围.
所以你可以自由地改变终点,直到你的标签数量很好地整除它.
让我们定义"好".如果标签关闭,我会称之为好:
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
直到偏移"很好"