【问题标题】:displaying axis from min to max value - calculating scale and labels显示从最小值到最大值的轴 - 计算比例和标签
【发布时间】:2013-02-02 03:30:49
【问题描述】:

编写例程以在水平轴上显示数据(使用 PHP gd2,但这不是重点)。

轴从$min$max 开始并在$result 处显示一个菱形,这样的图像大约为300px 宽和30px 高,如下所示:


(来源:testwolke.de

在上面的示例中,$min=0$max=3$result=0.6。 现在,我需要计算一个有意义的比例和标签,在上面的例子中,例如虚线在0 .25 .50 .75 1 1.25 ... up to 3,数字标签在0 1 2 3

如果$min=-200$max=600,虚线应位于-200 -150 -100 -50 0 50 100 ... up to 600,数字标签应位于-200 -100 0 100 ... up to 600

$min=.02$max=5.80,虚线在.02 .5 1 1.5 2 2.5 ... 5.5 5.8,数字在.02 1 2 3 4 5 5.8

我尝试通过数组明确告诉函数将虚线和数字放在哪里,但是嘿,应该是计算机而不是我,对吧?!

那么,怎么计算???

【问题讨论】:

  • 没人?至少有一些提示?
  • 每张图片是否需要显示一定数量的标签?
  • @Kyle:没有固定数量的标签,它必须适合值。考虑到300px的宽度,有一种天然的限制。
  • 感谢您的回复。我确实发布了一个解决方案,可以为您提供指定数量的标签。我会把它留在那里。也许您会发现其中至少有一些有用。
  • 链接的图像不起作用

标签: php algorithm php-gd axis-labels


【解决方案1】:

我知道这不是您正在寻找的,但希望它能让您朝着正确的方向开始。

$min = -200;
$max = 600;
$difference = $max - $min;
$labels = 10;
$picture_width = 300;
/* Get units per label */
$difference_between = $difference / ($labels - 1);
$width_between = $picture_width / $labels;
/* Make the label array */
$label_arr = array();
$label_arr[] = array('label' => $min, 'x_pos' => 0);
/* Loop through the number of labels */
for($i = 1, $l = $labels; $i < $l; $i++) {
    $label = $min + ($difference_between * $i);
    $label_arr[] = array('label' => $label, 'x_pos' => $width_between * $i);
}

【讨论】:

  • hummm... 奖励这个答案(也很有帮助)...但想奖励 Voitcus 算法...新手错误...我该怎么办?跨度>
【解决方案2】:

ACM Algorithm 463 提供了三个简单的函数来产生良好的轴刻度,输出 xminp、xmaxp 和 dist 用于刻度上的最小值和最大值以及刻度上刻度线之间的距离,给定n 间隔的请求包括数据点xminxmax

  1. Scale1() 给出了一个线性比例,大约有 n 间隔,dist 是 10 乘以 1、2 或 5 的整数幂。
  2. Scale2() 给出了一个线性比例尺,恰好有 n 间隔(xminp 和 xmaxp 之间的差距往往大于Scale1() 产生的差距)。
  3. Scale3() 给出对数刻度。

1973年的原论文在线here,比上面链接的代码提供了更多的解释。

代码在 Fortran 中,但它只是一组算术计算,因此解释和转换为其他语言非常简单。我自己没有编写任何 PHP,但它看起来很像 C,因此您可能希望首先通过 f2c 运行代码,这应该会给您一些接近可在 PHP 中运行的东西。

还有更复杂的函数可以提供更漂亮的比例(例如 gnuplot 中的函数),但 Scale1() 可能会用最少的代码为您完成这项工作。

(此答案基于我对上一个问题Graph axis calibration in C++ 的回答)

(编辑——我找到了我在 Perl 中所做的Scale1() 的实现):

use strict;

sub scale1 ($$$) {
  # from TOMS 463
  # returns a suitable scale ($xMinp, $xMaxp, $dist), when called with 
  # the minimum and maximum x values, and an approximate number of intervals
  # to divide into. $dist is the size of each interval that results.
  # @vInt is an array of acceptable values for $dist.
  # @sqr is an array of geometric means of adjacent values of @vInt, which
  # is used as break points to determine which @vInt value to use.
  #
  my ($xMin, $xMax, $n) = @_;
  @vInt = {1, 2, 5, 10};
  @sqr = {1.414214, 3.162278, 7.071068 }
  if ($xMin > $xMax) {
    my ($tmp) = $xMin;
    $xMin = $xMax;
    $xMax = $tmp;
    }
  my ($del) = 0.0002;   # accounts for computer round-off
  my ($fn) = $n;
  # find approximate interval size $a
  my ($a) = ($xMax - $xMin) / $fn;
  my ($al) = log10($a);
  my ($nal) = int($al);
  if ($a < 1) {
    $nal = $nal - 1;
    }
  # $a is scaled into a variable named $b, between 1 and 10
  my ($b) = $a / 10^$nal;
  # the closest permissable value for $b is found)
  my ($i);
  for ($i = 0; $i < $_sqr; $i++) {
    if ($b < $sqr[$i]) last;
  }
  # the interval size is computed
  $dist = $vInt[$i] * 10^$nal;
  $fm1 = $xMin / $dist;
  $m1 = int($fm1);
  if ($fm1 < 0) $m1--;
  if (abs(($m1 + 1.0) - $fm1) < $del) $m1++;
  # the new minimum and maximum limits are found
  $xMinp = $dist * $m1;
  $fm2 = $xMax / $dist;
  $m2 = $fm2 + 1;
  if ($fm2 < -1) $m2--;
  if (abs ($fm2 + 1 - $m2) < $del) $m2--;
  $xMaxp = $dist * $m2;
  # adjust limits to account for round-off if necessary
  if ($xMinp > $xMin) $xMinp = $xMin;
  if ($xMaxp < $xMax) $xMaxp = $xMax;
  return ($xMinp, $xMaxp, $dist);
  }

sub scale1_Test {
  $par = (-3.1, 11.1, 5,
      5.2, 10.1, 5,
      -12000, -100, 9);
  print "xMin\txMax\tn\txMinp\txMaxp,dist\n";
  for ($i = 0; $i < $_par/3; $i++) {
    ($xMinp, $xMaxp, $dist) = scale1($par[3*$i+0], 
    $par[3*$i+1], $par[3*$i+2]);
    print "$par[3*$i+0]\t$par[3*$i+1]\t$par[3*$i+2]\t$xMinp\t$xMaxp,$dist\n";
    }
  }

【讨论】:

  • 喜欢 70 年代算法的指针。我不会把这个问题称为重复问题,但thisisnotnew
【解决方案3】:

一个简单的例子是在$increment = ($max-$min)/$scale 的行中,您可以将比例调整为增量缩放的变量。既然你按它划分,它应该随着你的最大值和最小值的变化而成比例地变化。之后,您将拥有如下功能:

$end = false;
while($end==false){
    $breakpoint = $last_value + $increment; // that's your current breakpoint
    if($breakpoint > $max){
        $end = true;
    } 
}

至少是这个概念......如果你有问题,请告诉我。

【讨论】:

    【解决方案4】:

    一种算法(示例值$min=-186$max=+153 作为限制):

    1. 取这两个限制$min$max,如果你愿意,可以标记它们

    2. 计算$max$min之间的差异:$diff = $max - $min
      153 - (-186) = 339

    3. 计算差值 $base10 = log($diff,10) = 2,5302 的 10 次对数

    4. 向下取整:$power = round($base10) = 2。
      这是你作为基本单位的第十次幂

    5. 要计算$step,计算这个:
      $base_unit = 10^$power = 100;
      $step = $base_unit / 2; (如果你想要每个$base_unit 2 个滴答声)。

    6. 计算$min 是否可以被$step 整除,如果不是,则取最接近的(向上取整)一个
      (在$step = 50的情况下是$loop_start = -150

    7. for ($i=$loop_start; $i&lt;=$max; $i++=$step){ // $i's are your ticks

    8. 结束

    我在 Excel 中对其进行了测试,结果非常好,您可能想增加它的功能,

    例如(在第 5 点),首先从 $diff 计算 $step
    $step = $diff / 4 并以$base_unit 可被$step 整除的方式对$step 进行四舍五入;

    这将避免在 (101;201) 四个刻度之间使用 $step=25 并且您在 0 和 999 之间有 39 个步骤 $step=25

    【讨论】:

    • 这太棒了,迫不及待想试试!
    • michi,这是我自己发明的,没有经过太多案例测试,我想给你一个线索。最关键的点是第 4 点和第 5 点。我总是将其四舍五入,但也许例如当您有 2.999 时您应该将其四舍五入?请记住,此 log(339,10) 为 2,5302,将四舍五入为 3(等于 1000)。这是一个对数刻度,想想怎么四舍五入吧
    • errr... 我会尽快回复您。我不得不承认我的数学技能不如平均水平,而且你对日志的解释让我的大脑一片空白……所以我会尝试报告:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-03
    • 2012-02-03
    • 2015-10-29
    相关资源
    最近更新 更多