2013-02-17 21 views
6

Scrittura di una routine per visualizzare i dati su un asse orizzontale (utilizzando PHP gd2, ma non è questo il punto qui).visualizzazione dell'asse dal valore minimo a massimo - calcolo della scala e delle etichette

L'asse inizia a $min a $max e visualizza un diamante $result, tale immagine sarà di circa 300 x larghezza e 30px alto, in questo modo:

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

Nell'esempio precedente, $min=0, $max=3 , $result=0.6. Ora, ho bisogno di calcolare una scala e le etichette che hanno senso, nell'esempio sopra, ad es. linee tratteggiate allo 0 .25 .50 .75 1 1.25 ... up to 3, con numeri di identificazione allo 0 1 2 3.

Se $min=-200 e $max=600, linee tratteggiate dovrebbe essere -200 -150 -100 -50 0 50 100 ... up to 600, con il numero di etichette a -200 -100 0 100 ... up to 600.

Con $min=.02 e $max=5.80, linee tratteggiate a .02 .5 1 1.5 2 2.5 ... 5.5 5.8 e numeri a .02 1 2 3 4 5 5.8.

Ho provato a dire esplicitamente alla funzione dove inserire linee e numeri punteggiati per array, ma ciao, è il computer che dovrebbe funzionare, non io, giusto ?!

Quindi, come calcolare ???

+0

nessuno? almeno qualche suggerimento? – michi

+0

C'è un certo numero di etichette che vuoi visualizzare per immagine? – Kyle

+0

@Kyle: nessun numero fisso di etichette, deve adattarsi ai valori. Tenendo conto della larghezza di 300 px, esiste una sorta di limite naturale. – michi

risposta

4

Un algoritmo (ad esempio i valori $min=-186 e $max=+153 come limiti):

  1. Prendete questi due limiti $min, $max e segnare loro se lo si desidera

  2. calcolare la differenza tra $max e $min: $diff = $max - $min
    153 - (-186) = 339

  3. Calcolare 10 logaritmo della differenza $base10 = log($diff,10) = 2,5302

  4. rotonda verso il basso: $power = round($base10) = 2.
    Questa è la vostra decima potenza come unità di base

  5. Per calcolare $step calcolare questo:
    $base_unit = 10^$power = 100;
    $step = $base_unit/2; (se vuoi 2 tick per ogni $base_unit).

  6. Calcolare se $min è divisibile per $step, se non prendere il più vicino (eccesso) uno
    (nel caso di $step = 50 è $loop_start = -150)

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

  8. fine

L'ho provato in Excel e dà risultati piuttosto piacevoli, yo u È possibile aumentare la sua funzionalità,

per esempio (punto 5) calcolando $step prima dal $diff,
dire $step = $diff/4 e rotondo $step in modo tale che $base_unit è divisibile per $step;

questo eviterà tali situazioni esistenti tra (101; 201) quattro zecche con $step=25 e avete 39 passi $step=25 tra 0 e 999.

+0

questo è fantastico, non vedo l'ora di provarlo! – michi

+0

michi, questo è stato inventato da me, non testato in troppi casi, volevo darti un indizio. I punti più cruciali sono il punto 4 e 5. L'ho sempre arrotondato, ma forse per esempio quando hai 2.999 dovresti arrotondarlo? Ricorda che questo registro (339,10) è 2,5302, che andrà ad arrotondare a 3 (che equivale a 1000). È una scala logaritmica, pensa solo a come arrotondarla, forse se è maggiore di 2.6989 (log 500), se meno arrotondato? – Voitcus

+0

errr ... ti risponderò non appena ci avrò provato. Devo confessare che le mie abilità matematiche sono meno di quella media, e le tue spiegazioni sul registro levo il cervello in bianco ... quindi proverò a riferire :-) – michi

3

So che questo non è esattamente quello che stai cercando, ma spero che ti porti nella giusta direzione.

$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 ... ha assegnato il premio a questa risposta (anche chi è utile) ... ma ha voluto assegnare l'algoritmo Voitcus ... newbie-errore ... cosa posso fare? – michi

+0

@michi non sono sicuro. Non ho mai fatto un premio prima. – Kyle

4

ACM Algorithm 463 fornisce tre funzioni semplici per produrre buoni bilance assi con uscite xminp, xmaxp e dist per i valori minimo e massimo della scala e la distanza tra le tacche sulla scala, data una richiesta di n intervalli includere i punti dati xmin e xmax:

  1. Scale1() dà una scala lineare con circa n intervalli e dist essendo una potenza intera di 10 volte 1, 2 o 5.
  2. 012.351.641.061.
  3. Scale2() fornisce una scala lineare con esattamente gli intervalli n (il divario tra xminp e xmaxp tende ad essere maggiore dell'intervallo prodotto da Scale1()).
  4. Scale3() fornisce una scala logaritmica.

Il documento originale del 1973 è online here, che fornisce ulteriori spiegazioni rispetto al codice collegato sopra.

Il codice è in Fortran ma è solo un insieme di calcoli aritmetici, quindi è molto semplice interpretarlo e convertirlo in altre lingue. Non ho scritto PHP da solo, ma sembra molto simile a C, quindi potresti iniziare eseguendo il codice tramite f2c che dovrebbe darti qualcosa di simile a eseguibile in PHP.

Ci sono funzioni più complicate che forniscono scale più carine (ad esempio quelle in gnuplot), ma Scale1() probabilmente farebbe il lavoro per voi con codice minimo.

(Questa risposta si basa sulla mia risposta ad una precedente interrogazione Graph axis calibration in C++)

(EDIT - ho trovato un'implementazione di Scale1() che ho fatto in 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

grazie, controllerò questo! – michi

+0

Ama il puntatore agli algoritmi degli anni '70. Non chiamerei questa domanda un duplicato, ma [questo] (http://stackoverflow.com/q/13404336/383793) [è] (http://stackoverflow.com/q/236261/383793) [non] (http://stackoverflow.com/q/326679/383793) [nuovo] (http://stackoverflow.com/q/1524965/383793). –

1

Un esempio veloce sarebbe qualcosa nelle righe di $increment = ($max-$min)/$scale in cui è possibile modificare la scala in modo che sia la variabile in base alla quale l'incremento viene ridimensionato. Dato che lo dividi, dovrebbe cambiare proporzionalmente al variare dei valori massimo e minimo. Dopodiché avrai una funzione del tipo:

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

Almeno questo è il concetto ... Fammi sapere se hai problemi con esso.

Problemi correlati