codemotion 2016 - d3.js un taller divertido y difícil

Post on 16-Apr-2017

326 Views

Category:

Internet

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

top related