Taller de visualización de datos

DNlab, Octubre-Noviembre 2014

apptivismo.org/taller-visualizacion-de-datos

El profe

Alberto Labarga @alabarga
alberto.labarga@gmail.com

Temario

  1. Introducción a la visualización de datos
  2. Fusion Tables y Google maps
  3. CartoDB

Temario

  1. Obtención y limpieza de los datos
  2. Publicación de datos
  3. Técnicas avanzadas de visualización
  4. Iniciativas y ejemplos de periodismo de datos y aplicaciones de datos abiertos

No hace falta programar

pero la programacion te da superpoderes

Introducción a la visualización de datos

Tecnologías que transforman datos en información mediante elementos visuales

Datos -> Sabiduría

  • Los datos, organizados y empleados debidamente, pueden convertirse en información.
  • La información, absorbida, comprendida y aplicada por las personas, puede convertirse en conocimientos.
  • Los conocimientos aplicados frecuentemente en un campo pueden convertirse en sabiduría, y la sabiduría es la base de la acción positiva.

La visualización es fundamental para el análisis de datos. Revela la estructura interna de los datos que no podría detectarse de otra manera. Descubrimos fenómenos no esperados, y ponemos a prueba nuestras hipótesis.

¿Cuántos puntos ves?

¿Cuántos puntos ves?

¿Cuántos puntos ves?

El cuarteto de Ascombe en la Wikipedia

El cuarteto de Ascombe en la Wikipedia

There are three kinds of lies: lies, damned lies, and statistics.

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

Se puede mentir con un gráfico?

http://wtfviz.net/

http://wtfviz.net/

http://wtfviz.net/

Correlación

Correlación no es causalidad

http://tylervigen.com/

http://tylervigen.com/

Girls Lead in Science Exam, but Not in the United States

The case for big cities, in 1 map

Las cinco claves de una gran visualización

  1. Veraz (Truthful)
  2. Funcional (Functional)
  3. Atractiva (Beautiful)
  4. Reveladora (Insightful)
  5. Esclarecedora (Enlightening)

Historia de la visualización de datos

Siglo XVII: René Descartes. Geometría Descriptiva

Siglo XVIII: William Playfair. Gráficos Estadísticos

Time series. The Commercial and Political Atlas. 1786

Bar chart. The Commercial and Political Atlas. 1786

Pie chart. The Commercial and Political Atlas. 1786

Siglo XIX: Charles Joseph Minard. Infografía

Multiple series. 1861

Stacked area. 1866

Radar plot. 1859

Mapa. 1858

Mapa. 1859

Mapa. 1862

La mejor visualización de la historia. 1869

Siglo XX: John Tukey. FFT. Box Plot. Exploratory Data Analysis

Siglo XX: Edward Tufte. The Visual Display of Quantitative Information

Siglo XX: The world in terms of General Motors

Historia (moderna) de la visualización de datos

Discursos (ejemplo 1) (ejemplo 2)

Probamos a crear uno a partir de unRSS?

Treamaps creados por Ben Shneiderman, 1990s?

Probamos a crear uno con el Ranking de los equipos de fútbol de la Primera División de España con mayor presupuesto?

Probamos a crear uno con el Ranking de los equipos de fútbol de la Primera División de España con mayor presupuesto?

Ranking de los equipos de fútbol de la Primera División de España con mayor presupuesto?

y entonces llego Hans Rosling

y volvió al año siguiente

Google Public Data Explorer

Show me your data

Tim Berners-Lee :: On the Next Web

Tim Berners-Lee :: The Year Open Data Went Worldwide

5* Opendata

OpenDataNavarra

The Data Hub

The World Bank Data

The World Bank Data

The World Bank Data

The World Bank Data

The World Bank Data

The World Bank Data

Google Motion Chart

Mapas

http://maps.google.es/

http://openstreetmap.org/

MapBox

Cartodb

Crowdmap

Fusion Tables

El futuro

y Ben Fry creo Processing

y Bestiario creo Quadrigram

Tableau

y Spotfire (Silver)

y Mike Bostock creo D3.js

Y entonces llegó Nicholas Felton

Y creó Daytum

Your FlowingData

Microcosm

Wearables

smartcitizen.me

Hay que ver ...

Processing

Bestiario

Visual Complexity

ManyEyes

... y mucho más

Hay que leer ...


Libros

Hay que seguir a ...

@driven_by_data (Gregor Aisch)

@datastore (Simon Rogers. The Guardian)

@moritz_stefaner

@ben_fry

@moebio (Santiago Ortiz)

@saleiva (Sergio Alvarez)

@flowingdata (Nathan Yau)

@albertocairo (Alberto Cairo)

Hay que amar ...

http://flowingdata.com

http://well-formed-data.net

http://www.visualizing.org

http://www.informationisbeautiful.net

http://www.visualisingdata.com/

http://chartporn.org

http://infosthetics.com

http://thedailyviz.com

http://datavisualization.ch

Hay que abrir cuenta en ...

http://infogr.am

http://www.tableausoftware.com/public/

https://import.io/

http://www.informationisbeautiful.net

https://cartodb.com/

Más herramientas

Hasta aquí hemos llegado hoy

Gracias. Preguntas?

D3.js

http://bl.ocks.org/1256572

http://bost.ocks.org/mike/miserables/

http://mbostock.github.com/d3/talk/20111116/iris-splom.html

http://www.jasondavies.com/parallel-sets/

http://mbostock.github.com/d3/talk/20111018/treemap.html

http://mbostock.github.com/d3/talk/20111018/calendar.html

http://mbostock.github.com/d3/talk/20111018/choropleth.html

http://scottcheng.github.com/bj-air-vis/

How D3 Works

What I wish someone had told me
when I was learning D3

Data-DOM Binding

Think about visualization –
it's all about binding data to visual elements.

D3 Programming Pattern

        
var dataset = [20, 5, 10, 0, 50];
d3.select('body')
  .selectAll('p')  // selection
  .data(dataset)  // data binding
  .enter()
    .append('p')  // dom manipulation
    .attr('class', 'paragraph')  // static property
    .text(function(d, i) {  // dynamic property
      return i + ': my value is ' + d;
    })
    .style('font-size', function(d) {
      return (d / 2 + 25) + 'px';  // maps to [25, 50]
    });
        
      

Try it here

Update, Enter & Exit

Visual elements enter or exit the stage

        
// update
var p = d3.select('body')
  .selectAll('p')
  .data(newData)  // elements that already are on the stage
  .text(function(d) { return d + ' has been here all along'; });
  
// enter
p.enter()  // elements that are about to enter the stage
  .append('p')
  .text(function(d) { return d + ' is new here'; });
  
// exit
p.exit()  // elements that are about to exit the stage
  .remove();
        
      

Scale

Scales are functions that map from an input domain to an output range.
        
  var fontSize = d3.scale.linear()
  .domain([d3.min(dataset), d3.max(dataset)])
  .range([25, 50]);
  
d3.selectAll('p')
  .style('font-size', function(d) {
    return fontSize(d) + 'px';
  });
        
      

Transition

Transition provides the visual cue that
resembles how real-world objects change.

        
    d3.selectAll('p')
  .transition()
    .delay(function(d, i) {
      return i * 50;
    })
    .style('padding-left', function() {
      return Math.random() * 200 + 'px';
    });
        
      

Arrays & Data Manipulation

        
d3.max(array[, accessor]);
d3.min(array[, accessor]);
d3.extent(array[, accessor]);
d3.sum(array[, accessor]);
d3.mean(array[, accessor]);
d3.median(array[, accessor]);

d3.range([start, ]stop[, step]);

d3.nest()
  .key(function(d) { return d.school })
  .entries(array);  // or `.map(array)`
        
      

SVG: Scalable Vector Graphic

        
          
        
      

SVG Generators

        
        var width = 840, height = 500, padding = 10;
var x = d3.scale.linear()  // x scale
  .domain([0, dataset.length - 1])
  .range([padding, width - padding]);
var y = d3.scale.linear()  // y scale
  .domain(d3.extent(dataset))
  .range([height - padding, padding]);
var line = d3.svg.line()  // line generator
  .x(function(d, i) { return x(i); })
  .y(function(d) { return y(d); });
d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .append('path')
  .datum(dataset)
  .attr('d', line);
  

Hands-On

Based on http://bl.ocks.org/3048740 by Mike Bostock

Setup

var width = 840;
var height = 500;
var outerRadius = height / 2 - 10;
var innerRadius = 120;

var svg = d3.select('body').append('svg')
  .attr('width', width)
  .attr('height', height)
  .attr('class', 'stacked-radial')
  .append('g')
  .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

Fetch Data With AJAX

d3.csv('data.csv', function(data) {
  // play with `data`
});

Data Preprocessing

// turn strings into numbers
data.forEach(function(d) {
  d.time = +d.time;
  d.value = +d.value;
});

// nest operator
var nest = d3.nest()
  .key(function(d) { return d.key; });
  
// stack operator
var stack = d3.layout.stack()
  .offset('zero')  // stack from a baseline
  .values(function(d) { return d.values; })
  .x(function(d) { return d.time; })
  .y(function(d) { return d.value; });
  
var layers = stack(nest.entries(data));

Stacked Radial Area Chart

// [0, 7] -> [0, 2π]
var angle = d3.time.scale()
  .domain([0, d3.max(data, function(d) {
    return d.time + 1;
  })])
  .range([0, 2 * Math.PI]);
  
// value -> radius
var radius = d3.scale.linear()
  .domain([0, d3.max(data, function(d) {
    return d.y0 + d.y;
  })])
  .range([innerRadius, outerRadius]);
  
// ordinal scale of 20 colors
var color = d3.scale.category20c();

// area generator
var area = d3.svg.area.radial()
  .interpolate('cardinal-closed')
  .angle(function(d) { return angle(d.time); })
  .innerRadius(function(d) { return radius(d.y0); })
  .outerRadius(function(d) { return radius(d.y0 + d.y); });
  
svg.selectAll('.layer')
  .data(layers)
  .enter().append('path')
    .attr('class', 'layer')
    .attr('d', function(d) { return area(d.values); })
    .style('fill', function(d, i) { return color(i); });

Axes

// date -> weekday name
var formatDate = d3.time.format('%a');
// day -> weekday name
var formatDay = function(d) {
  return formatDate(new Date(2007, 0, d));
};

svg.selectAll('.axis')
  .data(d3.range(
    d3.max(data, function(d) { return d.time; }) - 
    d3.min(data, function(d) { return d.time; }) + 1))
  .enter().append('g')
    .attr('class', 'axis')
    .attr('transform', function(d) {
      return 'rotate(' + angle(d) * 180 / Math.PI + ')';
    })
    .call(d3.svg.axis()
      .scale(radius.copy().range([-innerRadius, -outerRadius]))
      .orient('left'))
    .append('text')  // weekdays
      .attr('y', -innerRadius + 6)
      .attr('dy', '.71em')
      .attr('text-anchor', 'middle')
      .text(function(d) { return formatDay(d); });

Interaction

var selected = [];  // ui state
layers.forEach(function() { selected.push(false); });  // initialize state

// add mouse click listener
svg.selectAll('.layer')
  .on('click', function(d, i) {
    selected[i] = !selected[i];
    render();
  });
  
// render current state
var render = function() {
  svg.selectAll('.layer')
    .transition()
      .style('fill', function(d, i) {
        if (d3.max(selected)) {
          // something is selected
          return selected[i] ? color(i) : '#eee';
        } else {
          // nothing is selected
          return color(i);
        }
      });
};

Full, Runnable Code

    
var width = 840;
var height = 500;
var outerRadius = height / 2 - 10;
var innerRadius = 120;

var svg = d3.select('body').append('svg')
  .attr('width', width)
  .attr('height', height)
  .attr('class', 'stacked-radial')
  .append('g')
  .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
  
d3.csv('data.csv', function(data) {
  // data preprocessing
  
  // turn strings into numbers
  data.forEach(function(d) {
    d.time = +d.time;
    d.value = +d.value;
  });
  
  // nest operator
  var nest = d3.nest()
    .key(function(d) { return d.key; });
    
  // stack operator
  var stack = d3.layout.stack()
    .offset('zero')  // stack from a baseline
    .values(function(d) { return d.values; })
    .x(function(d) { return d.time; })
    .y(function(d) { return d.value; });
    
  var layers = stack(nest.entries(data));
  
  
  // draw stacked radial area chart
  
  // [0, 7] -> [0, 2π]
  var angle = d3.time.scale()
    .domain([0, d3.max(data, function(d) {
      return d.time + 1;
    })])
    .range([0, 2 * Math.PI]);
    
  // value -> radius
  var radius = d3.scale.linear()
    .domain([0, d3.max(data, function(d) {
      return d.y0 + d.y;
    })])
    .range([innerRadius, outerRadius]);
    
  // ordinal scale of 20 colors
  var color = d3.scale.category20c();
  
  // area generator
  var area = d3.svg.area.radial()
    .interpolate('cardinal-closed')
    .angle(function(d) { return angle(d.time); })
    .innerRadius(function(d) { return radius(d.y0); })
    .outerRadius(function(d) { return radius(d.y0 + d.y); });
    
  svg.selectAll('.layer')
    .data(layers)
    .enter().append('path')
      .attr('class', 'layer')
      .attr('d', function(d) { return area(d.values); })
      .style('fill', function(d, i) { return color(i); });
      
      
  // draw axes
  
  // date -> weekday name
  var formatDate = d3.time.format('%a');
  // day -> weekday name
  var formatDay = function(d) {
    return formatDate(new Date(2007, 0, d));
  };
  
  svg.selectAll('.axis')
    .data(d3.range(
      d3.max(data, function(d) { return d.time; }) - 
      d3.min(data, function(d) { return d.time; }) + 1))
    .enter().append('g')
      .attr('class', 'axis')
      .attr('transform', function(d) {
        return 'rotate(' + angle(d) * 180 / Math.PI + ')';
      })
      .call(d3.svg.axis()
        .scale(radius.copy().range([-innerRadius, -outerRadius]))
        .orient('left'))
      .append('text')  // weekdays
        .attr('y', -innerRadius + 6)
        .attr('dy', '.71em')
        .attr('text-anchor', 'middle')
        .text(function(d) { return formatDay(d); });
        
        
  // interaction
  
  var selected = [];  // ui state
  layers.forEach(function() { selected.push(false); });  // initialize state
  
  // add mouse click listener
  svg.selectAll('.layer')
    .on('click', function(d, i) {
      selected[i] = !selected[i];
      render();
    });
    
  // render current state
  var render = function() {
    svg.selectAll('.layer')
      .transition()
        .style('fill', function(d, i) {
          if (d3.max(selected)) {
            // something is selected
            return selected[i] ? color(i) : '#eee';
          } else {
            // nothing is selected
            return color(i);
          }
        });
  };
});

Try it here

Resources

D3.js Website

Gallery

Tutorials

API Reference

GitHub Project