2012-11-22 11 views
10

Sto usando d3js per visualizzare una rappresentazione in tempo reale delle viste di un sito web. Per questo io uso un layout dello stack e aggiorno il mio set di dati di JSON al momento.Visualizza solo valori interi lungo l'asse y in d3.js quando l'intervallo è 0-1 o 0-2

Quando sull'asse delle y è visualizzata solo 1 o 2, che è dinamica in relazione alla quantità di viste nel grafico, le etichette degli assi sono: 1 =>0, 0.2, 0.4, 0.6, 0.8, 1, le etichette degli assi sono: 2 =>0, 0.5, 1, 1.5, 2 Questo non ha senso per il mio set di dati poiché mostra le visualizzazioni di una pagina e non è possibile avere una mezza vista.

Ho una scala lineare in d3js baso mio asse y su

var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height]); 

Secondo the documentation of rangeRound() devo ottenere solo valori interi su questa scala. Per disegnare il mio asse che uso:

var y_axis = svg.append("g") 
    .attr("class", "y axis") 
    .attr("transform", "translate(0,0)") 
    .call(y_inverted.axis = d3.svg.axis() 
     .scale(y_inverted) 
     .orient("left") 
     .ticks(5)); 

Poiché si tratta di un'applicazione in tempo reale aggiorno questo ogni secondo chiamando:

function update(){ 
    y_inverted.domain([yStackMax, 0]); 
    y_axis.transition() 
     .duration(interval) 
     .ease("linear") 
     .call(y_inverted.axis); 
} 

yStackMax viene calcolato da uno stackLayout, per quanto ne so i dati utilizzati per i valori y contengono solo numeri interi.

var yStackMax = d3.max(layers, function(layer) { 
    return d3.max(layer, function(d) { 
     return d.y0 + d.y; 
    }); 
}); 

Ho provato diverse cose per ottenere un valore corretto per il mio asse y.

d3.svg.axis() 
    .scale(y_inverted) 
    .orient("left") 
    .ticks(5).tickFormat(d3.format(",.0f")) 

mi ha fatto la più vicina sofar, ma mostra ancora 0, 0, 0, 1, 1, 1

Fondamentalmente quello che voglio è quello di avere solo 1 tick quando yStackMax è 1, 2 zecche quando è 2, ma dovrebbe funzionare anche se yStackMax è 12 o 1.000.000

+0

btw, se si è soddisfatti con una delle risposte, si prega di accettarlo. – Juve

risposta

10

Risposta breve: È possibile impostare in modo dinamico il numero di segni di graduazione. Impostare a 1 per visualizzare solo due etichette delle tacche:

var maxTicks = 5, minTicks = 1; 
if (yStackMax < maxTicks) { 
    y_axis.ticks(minTicks) 
} 
else { 
    y_axis.ticks(maxTicks) 
} 

risposta Lunghi (andando un po 'fuori tema): Giocando con il tuo esempio mi si avvicinò con una "soluzione completa", piuttosto per tutta la formattazione i problemi. Sentitevi liberi di usarlo :)

var svg = d3.select("#svg") 
var width = svg.attr("width") 
var height = svg.attr("height") 
var yStackMax = 100000 
var interval = 500 
var maxTicks = 5 
var minTicks = 1 

var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height]) 
var defaultFormat = d3.format(",.0f") 

var format = defaultFormat 

var y_axis = d3.svg.axis() 
    .scale(y_inverted) 
    .orient("left") 
    .ticks(minTicks) 
    .tickFormat(doFormat) 

var y_axis_root; 
var decimals = 0; 

function countDecimals(v){ 
    var test = v, count = 0; 
    while(test > 10) { 
     test /= 10 
     count++; 
    } 
    return count; 
} 

function doFormat(d,i){ 
    return format(d,i) 
} 

function init(){ 
    y_axis_root = svg.append("g") 
     .attr("class", "y axis") 
     // I modified your example to move the axis to a visible part of the screen 
     .attr("transform", "translate(150,0)") 
     .call(y_axis) 
} 

// custom formatting functions: 
function toTerra(d) { return (Math.round(d/10000000000)/100) + "T" } 
function toGiga(d) { return (Math.round(d/10000000)/100) + "G" } 
function toMega(d) { return (Math.round(d/10000)/100) + "M" } 
function toKilo(d) { return (Math.round(d/10)/100) + "k" } 

// the factor is just for testing and not needed if based on real world data 
function update(factor){ 
    factor = (factor) || 0.1; 
    yStackMax*=factor 
    decimals = countDecimals(yStackMax) 
    console.log("yStackMax decimals:",decimals, factor) 
    if  (yStackMax < maxTicks) { 
     format = defaultFormat 
     y_axis.ticks(minTicks) 
    } 
    else { 
     y_axis.ticks(maxTicks) 
     if  (decimals < 3) format = defaultFormat 
     else if(decimals < 6) format = toKilo 
     else if(decimals < 9) format = toMega 
     else if(decimals < 12) format = toGiga 
     else     format = toTerra 
    } 

    y_inverted.domain([yStackMax, 0]); 
    y_axis_root.transition() 
     .duration(interval) 
     .ease("linear") 
     .call(y_axis); 
} 

init() 
setTimeout(update, 200) 
setTimeout(update, 400) 
setTimeout(update, 600) 

Si può provare insieme a questo snippet HTML:

<!DOCTYPE html> 
<html> 
    <head> 
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
     <script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script> 
    </head> 

    <body> 
     <div><svg id="svg" width="200" height="300"></svg></div> 
     <script src="axis.js"></script> 
     <button id="button1" onclick="update(10)">+</button> 
     <button id="button2" onclick="update(0.1)">-</button> 
    </body> 
</html> 

So che è un po 'fuori tema, ma io di solito piace a fornire esempi in esecuzione/soluzioni. Considerare la roba di formattazione aggiuntiva come un bonus al problema reale.

5

Se si richiede un certo numero di zecche (tramite axis.ticks()), d3 proverà a darti quel numero di tick, ma proverà ad usare valori piuttosto carini. Non ha niente a che fare con i tuoi dati.

Le tue soluzioni utilizzano tickFormat, come hai fatto tu, per arrotondare tutti i valori ai valori interi, solo chiedere un segno di spunta alla risposta della Juve, o impostare i valori di tick in modo esplicito usando axis.tickValues([...]) che sarebbe piuttosto facile usato in congiunzione con d3.range

rangeRound non è di aiuto in questo caso perché si riferisce al campo di uscita della scala, che in questo caso è l'offset del pixel per tracciare a: tra 0 e altezza.

4

Uscire dalla risposta di Superboggly, questo è ciò che ha funzionato per me. In primo luogo ho ottenuto il (grande) numero massimo dal dominio y utilizzando y.domain().slice(-1)[0] e poi ho costruito un array di valori tick da quello usando d3.range() ...

var y_max = y.domain().slice(-1)[0] 
    var yAxis = d3.svg.axis() 
     .scale(y) 
     .tickValues(d3.range(y_max+1)) 
     .tickFormat(d3.format(",.0f")) 
3

o semplicemente lasciare le zecche come sono e "nascondere" numeri decimali

d3.svg.axis() 
    .scale(y_inverted) 
    .orient("left") 
    .ticks(5).tickFormat(function(d) { 
       if (d % 1 == 0) { 
       return d3.format('.f')(d) 
       } else { 
       return "" 
       } 
      }); 
1

Ecco il codice:.

var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left") 
    .tickFormat(d3.format(".2s")); 
Problemi correlati