D3.js

Data-Driven Documents

Created by Łukasz Zdanikowski

Czym jest D3.js

D3.js jest biblioteką ułatwiającą tworzenie wykresów skupiającą się na przetwarzaniu danych a nie na ułożeniu elementów na wykresie.

Pozwala powiązać dowolne dane z drzewem DOM i przekstałcać całość w dowolny sposób. D3 potrafi wygenerować z tablicy liczb HTML-ową tabelkę, jak również interaktywny SVG z wykresem słupkowym z płynnymi przejściami i interakcjami.

dataset


[{
    "miejsce": "1",
    "xx": "16036468",
    "tytul": "Tajemnica Filomeny",
    "cnt": "63"
}, {
    "miejsce": "2",
    "xx": "12147754",
    "tytul": "Sponsoring",
    "cnt": "61"
}, {
    "miejsce": "3",
    "xx": "15937681",
    "tytul": "Pod Mocnym Aniolem",
    "cnt": "53"
}]
                    

Data binding


var result = d3.select('[data-state="visualizeParagraphs"] .result')
    .selectAll("p")
    .data(dataset).enter()
        .append("p")
            .text("Nowy paragraf!");

                    

Data binding

Z każdym elementen <p> związany zostaje obiekt __data__ z tablicy z danymi.

Transformajce danych


d3.select('[data-state="visualizeParagraphs2"] .result')
    .selectAll("p")
    .data(dataset).enter()
        .append("p")
            .text(function(data) { return 'tytuł: ' + data.tytul; });

                    

Transformajce i skale


var colorScale = d3.scale.category20c();
d3.select('[data-state="visualizeParagraphs2b"] .result')
    .selectAll("p")
    .data(dataset).enter()
        .append("p")
            .text(function(data) { return 'tytuł: ' + data.tytul;})
            .style("color", function(data, idx) { return colorScale(idx); });

                    

Transformajce danych c. d.



d3.select('[data-state="visualizeParagraphs3"] .result')
    .selectAll("div")
    .data(dataset).enter()
        .append("div").attr("class", "bar")
            .text(function(data) { return data.tytul;})
            .style({
                "background-color": function(data) { return colorScale(data.miejsce); },
                "width": function(data) { return (data.cnt * 10 + "px"); },
                "height": (100 / dataset.length) + "px",
                "font-size": "18px"
            });

                    

Tabelki



var table = d3.select('[data-state="visualizeTable"] .result')
        .append("table"),
    thead = table.append("thead"),
    tbody = table.append("tbody");

thead
    .append("tr")
        .selectAll("th")
            .data(Object.keys(dataset[0])).enter()
                .append("th")
                    .style({"border": "1px solid #000"})
                    .text(function(d) {return d;});
var rows = tbody
    .selectAll("tr")
        .data(dataset).enter()
            .append("tr");
rows
    .selectAll("td")
        .data(function(data) {
            var d = []; 
            for(var i in data) d.push(data[i]); 
            return d;
        }).enter()
            .append("td")
                .style({"border": "1px solid #000"})
                .text(function(data) { return data;});


                    

SVG



var svg = d3.select('[data-state="visualizeSVG"] .result')
    .append("svg").attr({"width": barWidth, "height": barHeight});
    
svg
    .selectAll("rect")
    .data(dataset)
    .enter().append("rect")
        .attr({
            "x": function(d, i) { return i*(barWidth / dataset.length); },
            "y": function(d) { return barHeight - d.cnt; },
            "width": (barWidth / dataset.length) - barMargin,
            "height": function(d) { return d.cnt; },
            "fill": function(d, i) { return colorScale(i); }
        });
svg
    .selectAll("text")
    .data(dataset)
    .enter().append("text")
        .attr({ 
            "x": function(d, i) { return i*(barWidth / dataset.length); },
            "y": function(d) { return barHeight - d.cnt; } 
        })
        .text(function(d) { return d.tytul; })
        .style({"font-size": "20px"});

                    

SVG - Bar Chart


var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset.map(function(d) { return d.cnt; } ))])
    .range([svgHeight, 0]);

var xScale = d3.scale.ordinal()
    .rangeRoundBands([0, svgWidth], .1)
    .domain(dataset.map(function(d) { return d.tytul; }));

var svg = d3.select('[data-state="visualizeSVG2"] .result')
        .append("svg").attr({"width": svgWidth, "height": svgHeight});

var barWidth = svgWidth / dataset.length;

var bar = svg.selectAll("g")
        .data(dataset)
        .enter().append("g")
            .attr("transform", function(d, i) { return "translate(" + xScale(d.tytul) + ", 0)";});
            
bar
    .append("rect").attr({
        "y": function(d, i) { return yScale(d.cnt); },
        "width": xScale.rangeBand(),
        "height": function(d, i) { return svgHeight - yScale(d.cnt); },
        "fill": function(d, i) { return colorScale(i); }
    });

bar
    .append("text")
        .attr({ 
            "x": 2,
            "y": function(d, i) { return yScale(d.cnt) + 3; },
            "dy": ".75em"
        })
        .text(function(d) { return d.tytul; })
        .style({"font-size": "20px"});
                    

SVG - Bar Chart


var margin = {top: 20, right: 30, bottom: 30, left: 40},
    width = svgWidth - margin.left - margin.right,
    height = svgHeight - margin.top - margin.bottom;

var svg = d3.select('[data-state="visualizeSVG2b"] .result')
    .append("svg")
        .attr({
            "width": width + margin.left + margin.right,
            "height": height + margin.top + margin.bottom
        })
        .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var barWidth = width / dataset.length;

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset.map(function(d) { return d.cnt; } ))])
    .range([height, 0]);

var xScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1)
    .domain(dataset.map(function(d) { return d.tytul; }));

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left");

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);

var bar = svg.selectAll(".bar")
    .data(dataset)
    .enter().append("g")
        .attr({
            "transform": function(d, i) { return "translate(" + xScale(d.tytul) + ", 0)";},
            "class": "bar"
        });
            
bar
    .append("rect").attr({
        "y": function(d, i) { return yScale(d.cnt); },
        "width": xScale.rangeBand(),
        "height": function(d, i) { return height - yScale(d.cnt); },
        "fill": function(d, i) { return colorScale(i); }
    });

                    

SVG - Bar Chart


var margin = {top: 20, right: 30, bottom: 30, left: 40},
    width = svgWidth - margin.left - margin.right,
    height = svgHeight - margin.top - margin.bottom;

var svg = d3.select('[data-state="visualizeSVG2c"] .result')
    .append("svg")
        .attr({
            "width": width + margin.left + margin.right,
            "height": height + margin.top + margin.bottom
        })
        .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var barWidth = width / dataset.length;

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset.map(function(d) { return d.cnt; } ))])
    .range([height, 0]);

var xScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1)
    .domain(dataset.map(function(d) { return d.tytul; }));

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(10, "");

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Odtworzeń");

var bar = svg.selectAll(".bar")
    .data(dataset);
    
bar.enter().append("g")
        .attr({
            "transform": function(d, i) { return "translate(" + xScale(d.tytul) + ", 0)";},
            "class": "bar",
            "y" : -20
        })
        .style("fill-opacity", 1e-6)
        .transition()
        .duration(750)
        .attr("y", 0)
        .style("fill-opacity", 1);
            
bar
    .append("rect").attr({
        "y": function(d, i) { return yScale(d.cnt); },
        "width": xScale.rangeBand(),
        "height": function(d, i) { return height - yScale(d.cnt); },
        "fill": function(d, i) { return colorScale(i); }
    });

                    

Co dalej?

THE END

BY Łukasz Zdanikowski