Codemotion 2016 - d3.js un taller divertido y difícil
-
Upload
javier-abadia -
Category
Internet
-
view
326 -
download
1
Transcript of Codemotion 2016 - d3.js un taller divertido y difícil
Un taller de d3.js difícil y divertido*
¿qué necesitas para hacer visualizaciones de datos impresionantes?
*http://www.macwright.org/presentations/dcjq/
Javier Abadía@javierabadia
¿por qué es difícil?
¿y por qué es divertido?
un gráfico de barras
nop!
de cabeza!
http://bl.ocks.org/mbostock/4062006
logística• ¿os funciona todo?• https://github.com/jabadia/d3-workshop
¿qué queremos conseguir?1. preparar los datos2. elegir el tipo de visualización apropiado3. buscar un ejemplo (p.ej. en bl.ocks.org) parecido4. cambiar los datos5. cambiar la apariencia6. adaptar la interacción7. integrarlo en mi app
contar una historiacon los datos
al lío…
Un poco de teoríad3.js, Javascript Nivel Demonio+
d3.js• no es una herramienta de visualización– Tableau, Excel, R-studio
• no es una librería de gráficas– highcharts.js, google charts, dimple, nvd3
• no es una librería de mapas– leaflet.js
• no es una librería de dibujo– SVG, Canvas
entonces, que es?
DataDrivenDocumentsDDD
3
¿qué tiene de especial?
DATOSJSONCSVTSV …
d3 necesita JS nivel Demonio+
funciones
http://www.smashingmagazine.com/2014/07/dont-be-scared-of-functional-programming/
function aceptaDos(a, b){ a = a || ‘default A’; 🌶 b = b || ‘default B’; return a + b + arguments[2];}
aceptaDos(); // legalaceptaDos(‘pA’); // legalaceptaDos(‘pA’,’pB’); // legalaceptaDos(‘pA’,’pB’,’pC’); // legal
function modifica1(s){ return 'un ' + s + ' precioso';}
function modifica2(s){ return 'el mejor ' + s + ' de todos';}
function generaDia(m){ return m('dia');}
generaDia(modifica1); // ‘un dia precioso’generaDia(modifica2); // ‘el mejor dia de todos’
function modifyString(){ var prefix='|'; var postfix='|';
var output = function(s) { return prefix + s + postfix; }
output.prefix = function(p) { prefix = p; return output; } output.postfix = function(p) { postfix = p; return output; }
return output;}
m = modifyString().prefix(‘un’).postfix(‘precioso’);m(‘dia’) // ‘un dia precioso’
❶
❷
❸
❹
❶ ❷ ❹
e.attr('x', function(d) { return x(d); })
e.attr('x', x)
e.attr('x', function(d) { return this.x(d); })
=>=>
¿lo tenéis?
¿quien sabe jQuery?
selecciones
lis = $(‘li’)lis.length
lis.each( function() { console.log(this); } )lis.each( function() {$(this).fadeOut(); } ) 🌶lis.each( function() {$(this).fadeIn(); } ) 🌶
lis.fadeOut()lis.fadeIn()lis.css({color: ‘red’})
<ul><li>a</li><li>b</li><li>c</li><li>d</li><li>e</li></ul>
más selecciones• jQuery– $(‘li’).css(‘background-color’,’red’);
• D3– d3.selectAll(‘.bar’).attr(‘fill’,’red’);
• pero…– d3.selectAll(‘.bar’).data(meses);
los joins
<ul><li>Java</li><li>C</li><li>C++</li><li>C#</li><li>Python</li><li>JavaScript</li><li>Visual Basic .NET</li><li>Perl</li><li>Objective-C</li><li>Assembly language</li><li>Swift</li><li>Ruby</li><li>Visual Basic</li><li>Delphi/Object Pascal</li><li>Go</li><li>Groovy</li><li>R</li><li>MATLAB</li><li>PL/SQL</li></ul>
var progLang = [ 'Java', 'C', 'C++', 'C#', 'Python', 'JavaScript', 'PHP', 'Visual Basic .NET', 'Perl', 'Objective-C', 'Assembly language', 'Swift', 'Ruby', 'Visual Basic', 'Delphi/Object Pascal', 'Go', 'Groovy', 'R', 'MATLAB', 'PL/SQL',];
var progLang = [ 'Java', …, 'PL/SQL'];
var list = d3.select('ul');var items = list.selectAll('li.lang').data(progLang);
items.enter().append('li') .attr('class', 'lang') .text(String);… .text(function(d) { return String(d); });
❶❸❷
❹
❺❻
selection.attr(‘x’, function(d, i) { … }).text(function(d, i) { … }).property(‘selected’, function(d, i) { … }).style(‘opacity’, function(d, i) { … })
this.__data__
function(d,i,data) { … }
d = datoi = índice del datodata = array de todos los datosthis = nodo DOM
al lío
Más teoríaVisualización de datos, codificación visual
valores
codificaciónvisual
https://www.safaribooksonline.com/library/view/designing-data-visualizations/9781449314774/
var x = d3.scaleLinear() .domain([10, 130]) .range([0, 960]);
x(20); // 80x(50); // 320
var color = d3.scaleLinear() .domain([10, 100]) .range(["brown", "steelblue"]);
color(20); // "#9a3439"color(50); // "#7b5167"
var x = d3.scaleTime() .domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]) .range([0, 960]);
x(new Date(2000, 0, 1, 5)); // 200x(new Date(2000, 0, 1, 16)); // 640x.invert(200); // Sat Jan 01 2000 05:00:00 GMT-0800 (PST)x.invert(640); // Sat Jan 01 2000 16:00:00 GMT-0800 (PST)
var x = d3.scalePoint() .domain(["a", "b", "c"]) .range([0, width]);
var x = d3.scaleBand() .domain(["a", "b", "c"]) .range([0, width]) .padding(0.05);
var c = d3.scaleOrdinal() .domain(["apple", "orange", "banana", "grapefruit"]) .range(["green”, ”orange”, ”yellow”, ”red"]);
ahora si que noos libráis de las barras
Datos dinámicos y movimientoAnimación, actualización de datos, story-telling
['Python', 'JavaScript', 'PHP', 'Visual Basic .NET', 'Perl', 'Objective-C', 'Assembly language', 'Swift', 'Ruby', 'Visual Basic', 'Delphi/Object Pascal','Go', 'Groovy', 'R', 'MATLAB', 'PL/SQL', ]
<ul><li>Java</li><li>C</li><li>C++</li><li>C#</li><li>Python</li><li>JavaScript</li><li>Visual Basic .NET</li><li>Perl</li><li>Objective-C</li><li>Assembly language</li><li>Swift</li><li>Ruby</li><li>Visual Basic</li><li>Delphi/Object Pascal</li><li>Go</li></ul>
EXIT
UPDATEUPDATE
ENTER
var bars = svg.selectAll('rect.bar').data(visibleWeather, day);
console.log(bars.enter().size(), bars.size(), bars.exit().size());
bars.enter() // ENTER .append('rect') .attr('class','bar') .attr('x', function(d,i) { return x(i) + x.bandwidth(); }) .attr('width', x.bandwidth()) .attr('y', function(d) { return y(tempMin(d)); }) .attr('height', function(d) { return y(tempMax(d)-tempMin(d)); }) .style('fill', function(d) { return color(tempMin(d));}).transition() .attr('x', function(d,i) { return x(i); })
bars // UPDATE .transition() .attr('x', function(d,i) { return x(i); }) .attr('width', x.bandwidth()) .attr('y', function(d) { return y(tempMin(d)); }) .attr('height', function(d) { return y(tempMax(d)-tempMin(d)); })
bars.exit() // EXIT .remove()
going out of the boxlayouts, arcs, paths
layouts• Bundle• Chord• Cluster• Force• Histogram• Pack• Partition• Pie• Stack• Tree• Treemap• …
datos
datos más fáciles de pintar
{ region: ‘Norte’, ventas: 11245}
{ startAngle: 0, endAngle: 0.124, data: { region: ‘Norte’, ventas: 11245 }}
data
pie(data)
arc• d3.arc() es una función que devuelve un generador de arcos– un arco es un path de SVG
• se puede/suele configurar con parámetros fijos
var arc = d3.arc();
arc({ innerRadius: 0, outerRadius: 100, startAngle: 0, endAngle: Math.PI / 2}); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
var arc = d3.arc() .innerRadius(0) .outerRadius(100) .startAngle(0) .endAngle(Math.PI / 2);
arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
pie y arc trabajan juntosvar arc = d3.svg.arc() .outerRadius(radius - 10) .innerRadius(0);
var pie = d3.layout.pie() .sort(null) .value(function(d) { return d.population; });
var g = svg.selectAll(".arc").data(pie(countries)) .enter().append("path") .attr("class", "arc") .attr("d", arc) .style("fill", function(d) { return color(d.data.age); });
la mitad de los parámetros
la otra mitadWTF?
http://bl.ocks.org/mbostock/3887235
¡dale manolo!
tu propio layout
var resultados = [{sCandidatura: ’pp', iEscanos: 23 },{sCandidatura: ’psoe', iEscanos: 20 },{sCandidatura: ’podemos', iEscanos: 11 },{sCandidatura: ’ciudadanos', iEscanos: 7 },];
nos preparamos los datosvar resultados = [{sCandidatura: ’pp', iEscanos: 23 },{sCandidatura: ’psoe', iEscanos: 20 },{sCandidatura: ’podemos', iEscanos: 11 },{sCandidatura: ’ciudadanos', iEscanos: 7 },];
var diputados = [ ’pp0’, ‘pp1’, ’pp2’, …, ’pp22’, ‘psoe0’, ’psoe1’, …,’psoe19’, ’podemos0’, …, …, ’ciudadanos6’];
mi layoutvar squaresLayout = function(squaresPerRow, width, gapRatio){ var squareSize = width / (squaresPerRow + (squaresPerRow-1) * gapRatio); var gapSize = squareSize * gapRatio;
var layout = function(data) { return _.map(data, function(d, i) { var row = Math.floor(i / squaresPerRow); var col = i - (row*squaresPerRow); return { row: row, col: col, x: col * squareSize + col * gapSize, y: row * squareSize + row * gapSize, data: d, }; }); } layout.squareSize = squareSize; layout.gapSize = gapSize; layout.squaresPerRow = squaresPerRow;
return layout;};
layout = squaresLayout(escanosPorFila, width, gapRatio);
var squares = svg.select('.squares').datum(diputados) .selectAll('rect.square').data(layout, function(d) { return d.data.idDiputado; });
WTF?
story-tellingelegir el layout, animaciones
http://www.datamake.io/project/film-money
crossfilter - dc.js
http://square.github.io/crossfilter/https://dc-js.github.io/dc.js/
d3.js v3 ▶v4no toques… ¿por que tocas?
librería modularv3<script src="https://d3js.org/d3.v3.js"></script>
v4<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script><script src="https://d3js.org/d3-collection.v1.min.js"></script><script src="https://d3js.org/d3-color.v1.min.js"></script><script src="https://d3js.org/d3-format.v1.min.js"></script><script src="https://d3js.org/d3-interpolate.v1.min.js"></script><script src="https://d3js.org/d3-time.v1.min.js"></script><script src="https://d3js.org/d3-time-format.v2.min.js"></script><script src="https://d3js.org/d3-scale.v1.min.js"></script>
cambios de nombres
v3d3.scale.linear
d3.layout.treemap
d3.scale.ordinal
d3.time.format
v4d3.scaleLinear
d3.treemap
d3.scaleOrdinal d3.scaleBands d3.scalePoints
d3.timeFormat
cambian los joins
v3 v4var circle = svg.selectAll("circle").data(data) // UPDATE .style("fill", "blue");
// ENTER; modifies UPDATE! 🌶circle.enter().append("circle") .style("fill", "green");
circle // ENTER + UPDATE .style("stroke", "black");
circle.exit().remove(); // EXIT
var circle = svg.selectAll("circle").data(data) // UPDATE .style("fill", "blue");
circle.enter().append("circle") // ENTER .style("fill", "green") .merge(circle) // ENTER + UPDATE .style("stroke", "black");
circle.exit().remove(); // EXIT
otros cambios• mejor soporte para Canvas (d3-shape)• transiciones coordinadas• muchos otros cambios menores– https://iros.github.io/d3-v4-whats-new
integraciónen mis apps
la convención de márgenesvar margin = {top: 20, right: 10, bottom: 20, left: 10};
var width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").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 + ")");
http://bl.ocks.org/mbostock/3019563
var x = d3.scale.linear() .range([0, width]); // area útil
var y = d3.scale.linear() .range([height, 0]); // area útil
angular.js - directivas
<div class="participacion" an-chart-participacion data-current='dataCurrent' data-prev='dataPrev'></div>
angular.js - directivasmodule.directive('anChartParticipacion', function(){ var _drawChart = function(elem, dataCurrent, dataPrev, options) { … // codigo d3 aquí … };
return { restrict: 'A', scope: { dataCurrent: '=current', dataPrev: '=prev', }, link: function(scope, elem) { elem.addClass('an-chart-participacion'); scope.$watchGroup(['dataCurrent','dataPrev'], function() { if( !scope.dataCurrent || !scope.dataPrev ) return;
_drawChart(elem, scope.dataCurrent, scope.dataPrev); }); }, };});
angular.js - directivasvar _drawChart = function(elem, dataCurrent, dataPrev, options) { elem = d3.select(elem[0]); options = _.defaults(options, { width: 50, height: 60 });
var margin = {top: 0, right: 0, bottom: 20, left: 0};
var width = options.width - margin.left - margin.right, height = options.height - margin.top - margin.bottom;
elem.select("svg").remove(); var svg = elem.append("svg") .attr("width", options.width) .attr("height", options.height) .attr("viewBox", [0, 0, options.width, options.height].join(' ')) .append('g') .attr('transform', 'translate(0, ' + margin.top + height + ') scale(1,-1)'); …}
Un taller de d3.js difícil y divertido*
¿qué necesitas para hacer visualizaciones de datos impresionantes?
*http://www.macwright.org/presentations/dcjq/
Javier Abadía@javierabadia
FIN
conceptos y ejemplos
• teoría• selecciones• data joins• svg• escalas• animación• layouts• datos anidados• adornos, tooltips, eventos• otras libs: dc.js / crossfilter• dimple
• bullets (html)– selecciones, data joins
• barras horizontales (otro más interesante?)– svg, escalas, animación
• evolución temporal– data joins (enter, exit), animación
• sectores– pie, arc
• escaños• [datos anidados]• cajamar
Nested data – Nested selections• selectAll().data()– selectAll().data(function(l1) { return l1.l2_items; })
• great example: http://code.hazzens.com/d3tut/lesson_3.html