pero la programacion te da superpoderes
¿Cuántos puntos ves?
¿Cuántos puntos ves?
¿Cuántos puntos ves?
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?
Correlación
Correlación no es causalidad
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
Discursos (ejemplo 1) (ejemplo 2)
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
Tim Berners-Lee :: On the Next Web
Tim Berners-Lee :: The Year Open Data Went Worldwide
5* Opendata
y Ben Fry creo Processing
y Bestiario creo Quadrigram
y Mike Bostock creo D3.js
Y entonces llegó Nicholas Felton
Y creó Daytum
Wearables
@driven_by_data (Gregor Aisch)
http://www.informationisbeautiful.net
http://www.tableausoftware.com/public/
What I wish someone had told me
when I was learning D3
Think about visualization –
it's all about binding data to visual elements.
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]
});
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();
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 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';
});
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)`
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);
Based on http://bl.ocks.org/3048740 by Mike Bostock
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) {
// play with `data`
});
// 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));
// [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); });
// 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); });
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);
}
});
};
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);
}
});
};
});