Cli*_*rce 75 algorithm math graph
我正在编写一些代码来在我们的软件中显示条形图(或线条).一切都很顺利.令我难过的是标记Y轴.
调用者可以告诉我他们想要Y标记的标记有多精细,但我似乎仍然坚持要以"有吸引力"的方式标记它们.我无法描述"有吸引力",也许你也不能,但是当我们看到它时我们就知道了,对吗?
所以如果数据点是:
15, 234, 140, 65, 90
Run Code Online (Sandbox Code Playgroud)
并且用户在Y轴上要求10个标签,用纸和铅笔进行一点点处理:
0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250
Run Code Online (Sandbox Code Playgroud)
那里有10个(不包括0),最后一个延伸超过最高值(234 <250),并且它是一个"好"的增量,每个增加25.如果他们要求8个标签,那么30的增量看起来不错:
0, 30, 60, 90, 120, 150, 180, 210, 240
Run Code Online (Sandbox Code Playgroud)
九会很棘手.也许只是使用了8或10,并将其称为足够接近就可以了.当一些观点是否定的时候该怎么办?
我可以看到Excel很好地解决了这个问题.
有没有人知道解决这个问题的通用算法(甚至有些蛮力没问题)?我不必快速做,但它应该看起来不错.
Too*_*the 93
很久以前我写了一个图形模块,很好地涵盖了这一点.挖掘灰色质量得到以下内容:
让我们举个例子:
15, 234, 140, 65, 90 with 10 ticks
Run Code Online (Sandbox Code Playgroud)
所以范围= 0,25,50,...,225,250
您可以通过以下步骤获得良好的滴答范围:
在这种情况下,21.9除以10 ^ 2得到0.219.这是<= 0.25所以我们现在有0.25.乘以10 ^ 2得到25.
让我们看一下8个刻度的相同例子:
15, 234, 140, 65, 90 with 8 ticks
Run Code Online (Sandbox Code Playgroud)
给出你要求的结果;-).
------由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;
Run Code Online (Sandbox Code Playgroud)
一般来说,刻度数包括底部刻度,因此实际的y轴段比刻度数少一个.
小智 20
这是我正在使用的PHP示例.此函数返回一个漂亮的Y轴值数组,其中包含传入的最小和最大Y值.当然,此例程也可用于X轴值.
它允许您"建议"您可能需要多少个刻度,但例程将返回看起来不错的刻度.我添加了一些示例数据并显示了这些结果.
#!/usr/bin/php -q
<?php
function makeYaxis($yMin, $yMax, $ticks = 10)
{
// This routine creates the Y axis values for a graph.
//
// Calculate Min amd Max graphical labels and graph
// increments. The number of ticks defaults to
// 10 which is the SUGGESTED value. Any tick value
// entered is used as a suggested value which is
// adjusted to be a 'pretty' value.
//
// Output will be an array of the Y axis values that
// encompass the Y values.
$result = array();
// If yMin and yMax are identical, then
// adjust the yMin and yMax values to actually
// make a graph. Also avoids division by zero errors.
if($yMin == $yMax)
{
$yMin = $yMin - 10; // some small value
$yMax = $yMax + 10; // some small value
}
// Determine Range
$range = $yMax - $yMin;
// Adjust ticks if needed
if($ticks < 2)
$ticks = 2;
else if($ticks > 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);
?>
Run Code Online (Sandbox Code Playgroud)
样本数据的结果输出
# ./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
)
Run Code Online (Sandbox Code Playgroud)
试试这个代码.我已经在一些图表场景中使用它并且效果很好.它也很快.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
听起来好像来电者不会告诉你它想要的范围.
所以你可以自由地改变终点,直到你的标签数量很好地整除它.
让我们定义"好".如果标签关闭,我会称之为好:
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, ...
Run Code Online (Sandbox Code Playgroud)
查找数据系列的最大值和最小值.我们称之为以下几点:
min_point and max_point.
Run Code Online (Sandbox Code Playgroud)
现在你需要做的就是找到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"
Run Code Online (Sandbox Code Playgroud)
符合等式:
(end_label - start_label)/label_offset == label_count
Run Code Online (Sandbox Code Playgroud)
可能有很多解决方案,所以选择一个.大多数时候我打赌你可以设置
start_label to 0
Run Code Online (Sandbox Code Playgroud)
所以试试不同的整数
end_label
Run Code Online (Sandbox Code Playgroud)
直到偏移"很好"
| 归档时间: |
|
| 查看次数: |
39964 次 |
| 最近记录: |