Visualizando datos I

Estoy haciendo un estudio sobre alternativas de visualización de datos. El objetivo que persigo es dar con una librería que me permita pintar datos de forma rápida, sencilla y flexible.

Para ello parto de la pre-selección que han hecho en http://selection.datavisualization.ch.
A esta selección le aplico mis filtros: Que sea Open Source y que sea JavaScript / Html / Java.
Por lo que me quedo con los siguientes candidatos:
  • d3js : A priori el mejor posicionado. Con mucha documentación y referencias. El primero de la lista
  • data-js :  Se me ha hecho poco claro y no he encontrado suficiente documentación.
  • Google Chart Tools : Esta muy bien, es muy fácil de usar y parece un sólido candidato... pero ... los términos del servicio de Google no me acaban de convencer.
  • InfoVis: Un framework del que no había oído hablar pero que promete mucho. Además si miras en la wikipedia dan muy buenas referencias.

¿Me dejo alguna digna de mención? Si alguien considera que si que por favor lo diga.
Así pues, La primera opción es d3js. Es una librería estupenda que permite  pintar cualquier cosa. Y quizás si principal problema sea ese... que tienes que pintarlo todo. Quiero decir, puedes hacer este gráfico :
var data = [ {"label":"1990", "value":16}, {"label":"1991", "value":56}, {"label":"1992", "value":7}, {"label":"1993", "value":77}, {"label":"1994", "value":22}, {"label":"1995", "value":100}, {"label":"1996", "value":16} ]; // establezco el valor maximo a mostrar var valor_maximo = d3.max(data, function(d){ return d.value; }); valor_maximo = valor_maximo + 10; // le doy un margen var numero_marcas = 5; //tamaño var ancho = 300; //width var alto = 300; //height //margenes var margen = {arriba: 30, abajo: 20, izquierda: 100, derecha: 10}; var interior = {arriba: (alto-margen.arriba ), abajo: margen.arriba, izquierda: margen.izquierda, derecha: (ancho - margen.derecha)}; // funciones x, y que definen anchos de las barras var x = d3.scale.linear() .domain([0, valor_maximo ]) .range([0, (ancho - (margen.derecha + margen.izquierda ))]); var y = d3.scale.ordinal() .domain(d3.range(data.length)) .rangeBands([margen.abajo, alto - margen.arriba], .1); /* * Empezamos a pintar */ // 1. defino el grafico var mi_graf = d3.select("#div_grafico") .append("svg:svg") .attr("width", ancho) .attr("height", alto); mi_graf.append("svg:g") .attr("id", "mis_barras") .attr("class", "barchart"); // 2. meto las barras var mis_barras = mi_graf.selectAll("g.mis_barras") .data(data) .enter() .append("svg:g") .attr("class", "bar") .attr("transform", function(d, i) { return "translate(0, " + y(i) + ")"; }); // 3. formateo las barras mis_barras.append("svg:rect") .attr("x", interior.izquierda) .attr("width", function(d) { return (x(d.value)); }) .attr("height", y.rangeBand()) .attr("fill", function(d) { var azul = Math.round( 255- ( (d.value / valor_maximo) * 100 ) ); return "rgb(0, 0, " + azul + " )"; }) .attr("stroke", function(d) { var azul = Math.round( 255- ( (d.value / valor_maximo) * 100 )); return "rgb(0, 0, " + azul + " )"; }) .on("click", function(d){ alert(d.value);} ); //4. Etiquetas var mis_etiquetas = mi_graf.selectAll("g.bar") .append("svg:text") .attr("class", "label") .attr("x", 0) .attr("text-anchor", "right") .text(function(d) { return d.label; }); // las ubico mejor var bbox = mis_etiquetas.node().getBBox(); mi_graf.selectAll(".label") .attr("transform", function(d) { return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")"; }); // valores sobreimpresionados en las barras var etiquetas = mi_graf.selectAll("g.bar") .append("svg:text") .attr("class", "value") .attr("x", function(d) { /* * ubico el valor al final de la barra * Para meterlo dentro lo pongo al final de la barra menos * lo que ocupa la cadena de texto del valor */ posicionado = "" + d.value return x(d.value) + margen.izquierda - ( posicionado.length * 10 ); }) .attr("text-anchor", "left") .text(function(d) { return d.value; }) // esto se puede definir en el estilo .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white"); box = etiquetas.node().getBBox(); mi_graf.selectAll(".value") .attr("transform", function(d) { return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")"; }); //5.marcas var marcas = mi_graf.selectAll("g.rule") .data(x.ticks(numero_marcas)) .enter() .append("svg:g") .attr("transform", function(d) { return "translate(" + ( margen.izquierda + x(d) ) + ")"; }); // las lineas de las marcas marcas.append("svg:line") .attr("class", "marca") .attr("y1", interior.arriba) .attr("y2", interior.arriba + 10) .attr("stroke", "black"); // los valores de las marcas marcas.append("svg:text") .attr("class", "etiqueta_de_la_marca") .attr("text-anchor", "middle") .attr("y", interior.arriba + 20) .text(function(d) { return d; });




Pero para hacer ese gráfico hay que escribi todo este código:

<script src="http://d3js.org/d3.v2.js"></script>

<script type="text/javascript">
     var data = [
{"label":"1990", "value":16},
{"label":"1991", "value":56},
{"label":"1992", "value":7},
{"label":"1993", "value":77},
{"label":"1994", "value":22},
{"label":"1994", "value":100},
{"label":"1995", "value":16}
            ];

// establezco el valor maximo a mostrar
var valor_maximo =  d3.max(data, function(d){    return d.value; });
   valor_maximo = valor_maximo + 10; // le doy un margen
var numero_marcas = 5;

//tamaño
var ancho = 300; //width
        var alto = 300; //height

//margenes
var margen = {arriba: 30, abajo: 20, izquierda: 100, derecha: 10};
var interior = {arriba: (alto-margen.arriba ),
abajo: margen.arriba,
izquierda: margen.izquierda,
derecha: (ancho - margen.derecha)};
 
// funciones x, y que definen anchos de las barras
  var x = d3.scale.linear()
        .domain([0, valor_maximo ])
        .range([0, (ancho - (margen.derecha + margen.izquierda ))]);
var y = d3.scale.ordinal()
        .domain(d3.range(data.length))
        .rangeBands([margen.abajo, alto - margen.arriba], .1);

/*
*  Empezamos a pintar
*/
// 1. defino el grafico

var mi_graf = d3.select("#div_grafico")
.append("svg:svg")
.attr("width", ancho)
.attr("height", alto);
mi_graf.append("svg:g")
.attr("id", "mis_barras")
.attr("class", "barchart");


// 2. meto las barras
var mis_barras = mi_graf.selectAll("g.mis_barras")
.data(data)
.enter()
.append("svg:g")
.attr("class", "bar")
.attr("transform", function(d, i) {
                    return "translate(0, " + y(i) + ")"; });


// 3. formateo las barras
mis_barras.append("svg:rect")
.attr("x", interior.izquierda)
        .attr("width", function(d) {
                return (x(d.value));
                })
        .attr("height", y.rangeBand())
.attr("fill",  function(d) {
var azul =  Math.round( 255- ( (d.value  / valor_maximo) * 100   ) );
    return "rgb(0, 0, " + azul  + " )";
})
.attr("stroke",  function(d) {
var azul =  Math.round( 255- (  (d.value  / valor_maximo) * 100   ));
return "rgb(0, 0, " + azul + " )";
})
.on("click", function(d){  alert(d.value);} );
 

//4. Etiquetas
var mis_etiquetas = mi_graf.selectAll("g.bar")
        .append("svg:text")
            .attr("class", "label")
            .attr("x", 0)
            .attr("text-anchor", "right")
            .text(function(d) {
                    return d.label;
                    });
// las ubico mejor
  var bbox = mis_etiquetas.node().getBBox();
     mi_graf.selectAll(".label")
        .attr("transform", function(d) {
                return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")";
                });

// valores sobreimpresionados en las barras
var etiquetas =  mi_graf.selectAll("g.bar")
        .append("svg:text")
        .attr("class", "value")
        .attr("x", function(d)
                {
/*
* ubico el valor al final de la barra
* Para meterlo dentro lo pongo al final de la barra menos
* lo que ocupa la cadena de texto del valor
*/
posicionado = "" + d.value
                return x(d.value) + margen.izquierda - (  posicionado.length * 10 );
                })
        .attr("text-anchor", "left")
        .text(function(d)
        {
        return  d.value;
        })
// esto se puede definir en el estilo
  .attr("font-family", "sans-serif")
  .attr("font-size", "11px")
  .attr("fill", "white");
 
    box = etiquetas.node().getBBox();
    mi_graf.selectAll(".value")
        .attr("transform", function(d)
        {
            return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")";
      });


   //5.marcas
var marcas = mi_graf.selectAll("g.rule")
        .data(x.ticks(numero_marcas))
    .enter()
        .append("svg:g")
        .attr("transform", function(d)
                {
return "translate(" + (  margen.izquierda +  x(d) ) + ")"; });
// las lineas de las marcas
    marcas.append("svg:line")
        .attr("class", "marca")
        .attr("y1", interior.arriba)
        .attr("y2", interior.arriba + 10)
        .attr("stroke", "black");
// los valores de las marcas
marcas.append("svg:text")
.attr("class", "etiqueta_de_la_marca")
.attr("text-anchor", "middle")
.attr("y", interior.arriba + 20)
.text(function(d)
        {
        return d;
        });

</script>
(por cierto, te recomiendo este video  en el que me he basado)

Podéis apreciar que es una libreria muy potente porque te da toda la libertad para pintar lo que quieras. Lo cual está muy bien .. pero quizás no necesito tanta libertad y flexibilidad.... o si.

La conclusión  a la que he llegado es que si quiero utilizar esta librería debo crearme primero mi propia librería de gráficos  predefinidos a los que llenar de datos. Es decir.... reutilizar este gráfico de barras, definir uno de lineas, etc y a continuación usar los gráficos que he definido.

Esta librería es magnifica pero no acaba de satisfacer mis objetivos. Además de que tampoco nos vamos a quedar con la primera sin probar mas... ¿verdad?


Seguiremos buscando.