2012-04-12 42 views
24

Necesito crear un gráfico de barras d3 que pueda tener valores negativos. Idealmente, la posición cero del eje debería calcularse en función de la extensión de los datos, pero me conformaría con una solución que asume una extensión positiva y negativa simétrica, es decir, que estaría siempre en el medio del gráfico.Gráfico de barras con valores negativos

Aquí hay un ejemplo de lo que me gustaría lograr.

Bar chart with negative values

Respuesta

52

OK, digamos que tiene una matriz de números como el conjunto de datos, y esto incluye algunos valores positivos y negativos:

var data = [-15, -20, -22, -18, 2, 6, -26, -18]; 

Usted querrá dos escalas para construir un gráfico de barras . Necesita una escala cuantitativa (normalmente linear scale) para calcular las posiciones de la barra a lo largo del eje x, y una segunda ordinal scale para calcular las posiciones de la barra a lo largo del eje y.

Para la escala cuantitativa, normalmente necesita calcular el dominio de sus datos, que se basa en el valor mínimo y máximo. Una forma sencilla de hacerlo es a través de d3.extent:

var x = d3.scale.linear() 
    .domain(d3.extent(data)) 
    .range([0, width]); 

También puede ser que desee nice la escala para redondear la medida ligeramente. Como otro ejemplo, a veces desea que el valor cero a estar centrado en el centro del lienzo, en cuyo caso se tendrá que tomar el mayor de los valores mínimo y máximo valor:

var x0 = Math.max(-d3.min(data), d3.max(data)); 

var x = d3.scale.linear() 
    .domain([-x0, x0]) 
    .range([0, width]) 
    .nice(); 

Como alternativa, puede codificar el dominio que desee.

var x = d3.scale.linear() 
    .domain([-30, 30]) 
    .range([0, width]); 

Para los Y eje x, tendrá que utilizar rangeRoundBands para dividir el espacio vertical en bandas para cada barra. Esto también le permite especificar la cantidad de relleno entre barras. A menudo, se utiliza una escala ordinal con algunos datos de identificación, como un nombre o una identificación única. Sin embargo, también puede utilizar las escalas ordinales en conjunción con el índice de los datos:

var y = d3.scale.ordinal() 
    .domain(d3.range(data.length)) 
    .rangeRoundBands([0, height], .2); 

Ahora que usted tiene sus dos escalas, se pueden crear los elementos rect para mostrar las barras. La única parte difícil es que en SVG, las rectas se ubican (los atributos x y y) según su esquina superior izquierda. Necesitamos usar el x - y y -escalas para calcular la posición de la esquina superior izquierda, y eso depende de si el valor asociado es positivo o negativo: si el valor es positivo, entonces el valor de los datos determina el borde derecho de la barra, mientras que si es negativo, determina el borde izquierdo de la barra. De ahí los condicionales aquí:

svg.selectAll(".bar") 
    .data(data) 
    .enter().append("rect") 
    .attr("class", "bar") 
    .attr("x", function(d, i) { return x(Math.min(0, d)); }) 
    .attr("y", function(d, i) { return y(i); }) 
    .attr("width", function(d, i) { return Math.abs(x(d) - x(0)); }) 
    .attr("height", y.rangeBand()); 

Por último, puede agregar un eje para mostrar las marcas en la parte superior. También puede calcular un estilo de relleno (o incluso un degradado) para alterar la diferenciación de la apariencia de los valores positivos y negativos. Poniendo todo junto:

Cuestiones relacionadas