2013-02-17 56 views
6

編寫一個例程以在橫軸上顯示數據(使用PHP gd2,但這不是重點)。顯示軸從最小值到最大值 - 計算比例和標籤

軸以$min開始$max,並顯示在$result金剛石,這樣的圖像將圍繞300像素寬和30像素高,這樣的:

example http://www.testwolke.de/profile.png

在上面的例子,$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的數字。

我試着明確告訴函數在哪裏放置虛線和數組,但是嘿,這是應該工作的計算機,而不是我,對嗎?!

那麼,如何計算???

+0

沒有人之間?至少有一些提示? – michi 2013-02-17 20:47:12

+0

是否有一定數量的標籤要顯示在每張圖片上? – Kyle 2013-02-19 17:37:59

+0

@凱爾:沒有固定數量的標籤,它必須符合數值。考慮到300px的寬度,有一種自然限制。 – michi 2013-02-19 19:03:39

回答

4

的算法(例如值$min=-186$max=+153爲界限):

  1. 取這兩個極限$min$max和標記他們,如果你想

  2. 計算$max$min之間的區別:$diff = $max - $min
    153 - (-186) = 339

  3. 計算差值的10日對數$base10 = log($diff,10) = 2,5302

  4. 回合下來:$power = round($base10) = 2
    這是你的10次方爲基本單元

  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<=$max; $i++=$step){ // $i's are your ticks

我在Excel中測試它,它給出了相當不錯的結果,喲U可要通過從$diff$step計算第一至增加其功能,

例如(在第5點),
$step = $diff/4並且在這種方式輪$step$base_unit是整除$step;

這將避免必須之間這樣的情況下(101; 201)4個蜱與$step=25,你有39個步驟$step=25 0和999

+0

這真棒,迫不及待地嘗試它! – michi 2013-02-26 16:17:25

+0

michi,這是我發明的,未經過太多測試,我想給你一個線索。最關鍵的一點是第4點和第5點。我總是將它捨去,但是也許例如當你有2.999的時候你應該把它四捨五入?請記住,這個日誌(339,10)是2,5302,它會舍入到3(即等於1000)。這是一個對數尺度,只是想一想如何將其舍入,也許如果大於2.6989(log 500)則舍入,如果更小的話? – Voitcus 2013-02-26 21:13:08

+0

errr ...我會盡快回復您。我必須承認,我的數學技能不及那個平均水平,而你對日誌的解釋使我的大腦空白......所以我會試着報告:-) – michi 2013-02-26 21:52:03

3

我知道這不是你正在尋找的東西,但希望它會讓你開始朝正確的方向發展。

$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); 
} 
+0

hummm ...給這個答案賞金(whioch也很有幫助)...但是想要授予Voitcus算法... newbie-mistake ...我該怎麼辦? – michi 2013-02-26 21:54:45

+0

@michi我不確定。我以前從未做過賞金。 – Kyle 2013-02-27 15:49:47

4

ACM Algorithm 463提供了三個簡單的功能,以產生具有輸出xminp,xmaxp和DIST用於在規模和在標尺刻度標記之間的距離的最大值和最小值良好軸秤,給出n間隔的請求包括數據點xminxmax

  1. Scale1()給出的線性標尺與大約n間隔和dist爲10倍1,2或5
  2. 的整數冪
  3. Scale2()給出了一個精確到n間隔的線性標度(xminp和xmaxp之間的間隙往往比Scale1()產生的間隙大)。
  4. Scale3()給出了一個對數尺度。

原始的1973年論文在線here,它提供了比上面鏈接的代碼更多的解釋。

代碼位於Fortran中,但它只是一組算術計算,因此解釋並轉換爲其他語言非常簡單。我自己並沒有編寫任何PHP,但它看起來很像C,所以你可能需要通過f2c運行代碼開始,它應該給你一些接近於在PHP中運行的東西。

還有更復雜的功能可以提供更漂亮的比例(例如gnuplot中的那些),但Scale1()可能會以最少的代碼爲您完成工作。

(這個答案建立在我的回答上一個問題Graph axis calibration in C++

(編輯 - 我發現的Scale1()的實現,我在Perl一樣):

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"; 
    } 
    } 
+0

謝謝,我會檢查一下! – michi 2013-02-25 23:25:58

+0

愛上70年代算法的指針。我不會稱這個問題爲重複,但[this](http://stackoverflow.com/q/13404336/383793)[is](http://stackoverflow.com/q/236261/383793)[not] (http://stackoverflow.com/q/326679/383793)[新](http://stackoverflow.com/q/1524965/383793)。 – 2013-02-26 11:41:45

1

一個簡單的例子將在$increment = ($max-$min)/$scale的行中,您可以調整比例作爲增量的比例變量。既然你依靠它,它應該隨着你的最大值和最小值的變化按比例變化。之後,你將有一個功能,如:

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

至少是這個概念......讓我知道你是否有麻煩。