Data Visualization D3.js - 2016
D3.js is a JavaScript library for manipulating documents based on data. D3 helps we bring data to life using HTML, SVG, and CSS. D3's emphasis on web standards gives us the full capabilities of modern browsers without tying ourselves to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
Here is the link to the latest version:
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
From //d3js.org/:
D3 allows us to bind arbitrary data to a Document Object Model (DOM), and then apply data-driven transformations to the document. For example, we can use D3 to generate an HTML table from an array of numbers. Or, use the same data to create an interactive SVG bar chart with smooth transitions and interaction.
D3 is not a monolithic framework that seeks to provide every conceivable feature. Instead, D3 solves the crux of the problem: efficient manipulation of documents based on data. This avoids proprietary representation and affords extraordinary flexibility, exposing the full capabilities of web standards such as HTML, SVG, and CSS. With minimal overhead, D3 is extremely fast, supporting large datasets and dynamic behaviors for interaction and animation. D3's functional style allows code reuse through a diverse collection of components and plugins.
The actual script looks like this:
<div class="legend">SVG circle with hovering:</div> <div id="bogo1"></div> <script type="text/javascript"> var bogoSVG = d3.select("#bogo1") .append("svg") .attr("width", 300) .attr("height", 300); bogoSVG.append("circle") .style("stroke", "gray") .style("fill", "cyan") .attr("r", 130) .attr("cx", 150) .attr("cy", 150) .on("mouseover", function(){d3.select(this).style("fill", "red");}) .on("mouseout", function(){d3.select(this).style("fill", "cyan");}); </script>
This is an html file with a simple html. The D3 script will be loaded directly from the D3 repository. We have a <div> tag to be used as a container for our visualization. The D3 script lay down just before the closing </body> tag to be sure the page is fully loaded and ready to work with, and according to best practices. This example shows the use of D3 for DOM traversal, for adding SVG and HTML elements, for adding styles and attributes and for mouse events binding.
D3 simplify the selection of an element in the DOM but also in the SVG DOM. The static method d3.select() can take any CSS selector as an argument. The d3.append() method is similar, but is used to add a new element as a child of the selected element. To append an SVG element, we can prefix it by the SVG namespace:
d3.append("circle")
D3 uses a declarative style of programming. We declare what we want to select, then our modifications on the DOM, CSS styles, HTML and SVG attributes, texts, animations, and events. D3 will loop through our selection set and apply the modifications for us, hiding just enough of the DOM manipulation ugliness to leave us with the fun part and with full control on the process.
Binding events is just as simple. Select a DOM element and use the .on() method with the event we want as a string for the first attribute and a callback function for the second attribute, and it looks like this:
.on("mouseover", function(){d3.select(this).style("fill", "white");}).
We will often see this type of anonymous function as an argument in D3.
Here is the code:
<div class="legend">SVG circle with animation - to see it again, refresh the browser:</div> <div id="bogo_animation"></div> <script type="text/javascript"> var bogoSVG = d3.select("#bogo_animation") .append("svg") .attr("width", 300) .attr("height", 300); bogoSVG.append("circle") .style("stroke", "gray") .style("fill", "cyan") .attr("r", 130) .attr("cx", 150) .attr("cy", 150) .transition() .delay(100) .duration(10000) .attr("r", 10) .attr("cx", 150) .style("fill", "blue"); </script>
In this section, we'll chaining automation. It listens to the end event with the .each(); method to detect the end of each animation.
Here is the code:
<div class="legend">SVG circle with animation chain, to see the animation, pressed the mouse inside the circle</div> <div id="bogo_chain_animation"></div> <script type="text/javascript"> var bogoSVG = d3.select("#bogo_chain_animation") .append("svg") .attr("width", 300) .attr("height", 300); bogoSVG.append("circle") .style("stroke", "gray") .style("fill", "cyan") .attr("r", 130) .attr("cx", 150) .attr("cy", 150) .on("mouseover", function(){d3.select(this).style("fill", "red");}) .on("mouseout", function(){d3.select(this).style("fill", "cyan");}) .on("mousedown", animate_1); function animate_1(){ d3.select(this) .transition() .delay(0) .duration(10000) .attr("r", 10) .each("end", animate_2); }; function animate_2(){ d3.select(this) .transition() .duration(1000) .attr("r", 130); }; </script>
D3 refers to Data-Driven Document because of the way it is binding data to graphics. Here is an example adding elements to a dataset. Anonymous functions are used to dynamically calculate values for styles and attributes. Those functions provide two arguments:
- the data bound to the element.
- the index of the element in the selection set.
Here is the code:
<div class="legend">One SVG circle generated for each data member:</div> <div id="bogo_data_binding"></div> <script type="text/javascript"> var dataset = [], i = 0; for(i=0; i<4; i++){ dataset.push(Math.round(Math.random()*200)); } var bogoSVG = d3.select("#bogo_data_binding") .append("svg") .attr("width", 600) .attr("height", 200); bogoSVG.selectAll("circle") .data(dataset) .enter().append("circle") .style("stroke", "gray") .style("fill", "cyan") .attr("r", 75) .attr("cx", function(d, i){return i*150+75}) .attr("cy", 100) .on("mouseover", function(){d3.select(this).style("fill", "red");}) .on("mouseout", function(){d3.select(this).style("fill", "cyan");}) .on("mousedown", animate_1); function animate_1(){ d3.select(this) .transition() .delay(0) .duration(1000) .attr("r", 30) .each("end", animate_2); }; function animate_2(){ d3.select(this) .transition() .duration(1000) .attr("r", 75); }; </script>
To bind data made from an array of arrays, we must use a little trick. We will build a simple html table.
First we add a <table> tag, then <tr> tags to add rows. For this, we bind the dataset to an empty <tr> selection and we then append as many <tr> tags as there is members in the first dimension of the dataset by chaining the two methods .enter().append(). But when it's time to bind data to the <td> tags to add columns, you have to bind the sub-array with this trick:
.data(function(d){return d;})
Here is the code:
<div class="legend">HTML Table:</div> <div id="bogo_table"></div> <script type="text/javascript"> var dataset = [], tmpDataset = [], i, j; for (i = 0; i < 7; i++) { for (j = 0, tmpDataset = []; j < 5; j++) { tmpDataset.push("R"+i+",C"+j); } dataset.push(tmpDataset); } d3.select("#bogo_table") .append("table") .style("border-collapse", "collapse") .style("border", "2px black solid") .selectAll("tr") .data(dataset) .enter().append("tr") .selectAll("td") .data(function(d){return d;}) .enter().append("td") .style("border", "1px black solid") .style("padding", "10px") .on("mouseover", function(){d3.select(this).style("background-color", "cyan")}) .on("mouseout", function(){d3.select(this).style("background-color", "white")}) .text(function(d){return d;}) .style("font-size", "12px"); </script>
D3 can handle a data file such as a Comma Separated Value (CSV) file using Ajax. The following example will work as is on a web server, local or remote. To load a CSV file with D3, we just need to use the d3.csv() method. But in this example, we will use an equivalent method, d3.text(), to load it as plain text and use a d3 helper to parse the CSV.
Here is the code:
<div class="legend">HTML Table created from a data file:</div> <br /> <div id="bogo_data_from_a_file"></div> <script type="text/javascript"> d3.text("sales.csv", function(datasetText) { var parsedCSV = d3.csv.parseRows(datasetText); var sampleHTML = d3.select("#bogo_data_from_a_file") .append("table") .style("border-collapse", "collapse") .style("border", "2px black solid") .selectAll("tr") .data(parsedCSV) .enter().append("tr") .selectAll("td") .data(function(d){return d;}) .enter().append("td") .style("border", "1px black solid") .style("padding", "5px") .on("mouseover", function(){d3.select(this).style("background-color", "cyan")}) .on("mouseout", function(){d3.select(this).style("background-color", "white")}) .text(function(d){return d;}) .style("font-size", "12px"); }); </script>
Data file : sales.csv
The code:
<div class="legend">HTML chart created from a data file:</div> <br /> <style> .bar { fill: cyan; } .bar:hover { fill: steelblue; } .axis { font: 10px sans-serif; } .axis path,ata_Visualization_D3js_Select_Animat .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .x.axis path { display: none; } </style> <br /> <div id="bogo_data_from_a_file_tsv"></div> <script> var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(10, "%"); var svg = d3.select("#bogo_data_from_a_file_tsv").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.tsv("chart.tsv", type, function(error, data) { x.domain(data.map(function(d) { return d.letter; })); y.domain([0, d3.max(data, function(d) { return d.frequency; })]); 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("Frequency"); svg.selectAll(".bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.letter); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.frequency); }) .attr("height", function(d) { return height - y(d.frequency); }); }); function type(d) { d.frequency = +d.frequency; return d; } </script>
Data file : chart.tsv
This section will simply draw a inscribed triangle. In the next section, we'll implement mouse actions.
Here is the code:
<div id="inscribedTriangle"> <script type="text/javascript"> var svgContainer = d3.select("#inscribedTriangle").append("svg") .attr("width", 600) .attr("height", 300) .append("g") .attr("transform", "translate(250,250) scale(1,-1)") .style("stroke-width", 3) .style("stroke", "#0077bb") .style("fill", "#ffffdd"); svgContainer.append("path") .attr("d","M -200,0 A200,200 0 0,0 200,0 L -200,0") .attr("class", "outline") .style("fill", "#ffffdd") .style("stroke", "#0077bb") .style("stroke-width", 3); var cGroup = svgContainer.append("g"); var circleData = [ { "id": "circlepoint", "class": "point", "cx": 0, "cy": 200, "radius": 5 }, { "id": "", "class": "point", "cx": 0, "cy": 0, "radius": 5 }, { "id": "", "class": "point", "cx": -200, "cy": 0, "radius": 5 }, { "id": "", "class": "point", "cx": 200, "cy": 0, "radius": 5 } ]; var circles = cGroup.selectAll("circle") .data(circleData) .enter() .append("circle"); var circleAttributes = circles .attr("id", function (d) { return d.id; }) .attr("class", function (d) { return d.class; }) .attr("cx", function (d) { return d.cx; }) .attr("cy", function (d) { return d.cy; }) .attr("r", function (d) { return d.radius; }); var lineData = [ { "id": "rightline", "class": "thickline", "x1": 200, "y1": 0, "x2": 0, "y2": 200 }, { "id": "leftline", "class": "thickline", "x1": -200, "y1": 0, "x2": 0, "y2": 200 }, { "id": "radline", "class": "thinline", "x1": 0, "y1": 0, "x2": 0, "y2": 200 } ]; var lines = cGroup.selectAll("line") .data(lineData) .enter() .append("line"); var lineAttributes = lines .attr("id", function (d) { return d.id; }) .attr("class", function (d) { return d.class; }) .attr("x1", function (d) { return d.x1; }) .attr("y1", function (d) { return d.y1; }) .attr("x2", function (d) { return d.x2; }) .attr("y2", function (d) { return d.y2; }); var axis = svgContainer.append("line") .attr("class", "axis") .attr("x1",-240) .attr("y1",0) .attr("x2",240) .attr("y2",0) .style("fill", "none") .style("stroke", "#333333") .style("stroke-width", 1.6); var square = svgContainer.append("rect") .attr("x",0) .attr("y",200) .attr("width", 20) .attr("height", 20) .attr("transform","rotate(225,0,200)") .style("fill", "#ffffff") .style("stroke", "#000000") .style("stroke-width", 1.6); var textData = [ { "id": "circpointlabel", "class": "pointlabels", "x": 0, "y": -214, "transform": "scale(1,-1)", "name": "B" }, { "class": "pointlabels", "x": -210, "y": 20, "transform": "scale(1,-1)", "name": "A" }, { "class": "pointlabels", "x": 210, "y": 20, "transform": "scale(1,-1)", "name": "C" }, { "class": "pointlabels", "x": 0, "y": 20, "transform": "scale(1,-1)", "name": "D" }, ]; var texts = cGroup.selectAll("text") .data(textData) .enter() .append("svg:text"); var textAttributes = texts .attr("id", function (d) { return d.id; }) .attr("class", function (d) { return d.class; }) .attr("x", function (d) { return d.x; }) .attr("y", function (d) { return d.y; }) .attr("transform", function (d) { return d.transform; }) .text(function(d) { return d.name; }) .style("font-size", "16px") .style("font-family", "Arial") .style("font-style", "italic") .style("fill", "black") .style("stroke", "none"); d3.selectAll(".thinline") .attr("style", "stroke: #770000; stroke-dasharray: 12,4; stroke-width:1.6"); d3.selectAll(".thickline") .attr("style", "stroke: #ff2222; fill: none; stroke-width:3.5"); d3.selectAll(".point") .attr("style", "stroke: white; fill: #000000; stroke-width:1"); </script> </div>
Visit Inscribed Triangle with Animation.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization