Public
Edited
May 16
Insert cell
Insert cell
Insert cell
Inputs.table(aiddata)
Insert cell
aiddata = {
const data = await d3.csv(googleSheetCsvUrl, row => ({
yearDate: d3.timeParse('%Y')(row.year),
yearInt: +row.year,
donor: row.donor,
recipient: row.recipient,
amount: +row.commitment_amount_usd_constant,
purpose: row.coalesced_purpose_name,
}));
data.columns = Object.keys(data[0]);
return data;
}
Insert cell
Insert cell
width = 700;
Insert cell
height = 400
Insert cell
margin = ({top: 20, right: 20, bottom: 40, left: 50});
Insert cell
x = d3.scaleTime()
.domain(d3.extent(dataByYear, d => d.year))
.range([margin.left, width - margin.right]);
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(dataByYear, d => d.total)]).nice()
.range([height - margin.bottom, margin.top]);
Insert cell
xAxis = d3.axisBottom(x).ticks(10).tickFormat(d3.timeFormat('%Y'));
Insert cell
yAxis = d3.axisLeft(y);
Insert cell
{
aiddata.forEach(d => {
d.year = d.yearDate.getFullYear();
d.amount = +d.amount;
});


const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);


svg.append('g')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(xAxis);

svg.append('g')
.attr('transform', `translate(${margin.left},0)`)
.call(yAxis)
.call(g => g.select('.domain').remove())
.append('text')
.attr('fill', 'black')
.attr('font-weight', 'bold')
.attr('x', 0)
.attr('y', 5)
.text('Monto total donado ');

const line = d3.line()
.x(d => x(d.year))
.y(d => y(d.total));

svg.append('path')
.datum(dataByYear)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2)
.attr('d', line);

return svg.node();
}
Insert cell
donacionesPorPais = d3.rollups(aiddata, v => d3.sum(v, d => d.amount), d => d.donor, d => d.yearInt);
Insert cell
recepcionesPorPais = d3.rollups(aiddata, v => d3.sum(v, d => d.amount), d => d.recipient, d => d.yearInt);
Insert cell
donaciones = Array.from(donacionesPorPais, ([donor, years]) => ({
donor,
values: years.map(([year, amount]) => ({ year, amount }))
}));

Insert cell
recepciones = Array.from(recepcionesPorPais, ([recipient, years]) => ({
recipient,
values: years.map(([year, amount]) => ({ year, amount }))
}));
Insert cell
allCountries = Array.from(new Set([
...donaciones.map(d => d.donor),
...recepciones.map(r => r.recipient)
]));
Insert cell
Insert cell
viewof countrySelect = Inputs.select(
Array.from(new Set(aiddata.map(d => d.donor))).sort(),
{ label: "Selecciona un país" }
)

Insert cell
dataByYear = Array.from(
d3.rollup(
aiddata,
v => ({
donado: d3.sum(v.filter(d => d.donor === countrySelect), d => d.amount),
recibido: d3.sum(v.filter(d => d.recipient === countrySelect), d => d.amount)
}),
d => d.yearInt
),
([year, values]) => ({ year, ...values })
).sort((a, b) => a.year - b.year);
Insert cell
dataPais = aiddata.filter(d => d.donor === countrySelect || d.recipient === countrySelect);

Insert cell
{

const svg = d3.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);

const x = d3.scaleLinear()
.domain(d3.extent(dataByYear, d => d.year))
.range([0, width]);

const y = d3.scaleLinear()
.domain([
0,
d3.max(dataByYear, d => Math.max(d.donado, d.recibido))
])
.nice()
.range([height, 0]);
const lineDonado = d3.line()
.x(d => x(d.year))
.y(d => y(d.donado));

const lineRecibido = d3.line()
.x(d => x(d.year))
.y(d => y(d.recibido));

g.append("path")
.datum(dataByYear)
.attr("fill", "none")
.attr("stroke", "#d62728")
.attr("stroke-width", 2)
.attr("d", lineDonado);

g.append("path")
.datum(dataByYear)
.attr("fill", "none")
.attr("stroke", "#1f77b4")
.attr("stroke-width", 2)
.attr("d", lineRecibido);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).tickFormat(d3.format("d")));

g.append("g")
.call(d3.axisLeft(y));

g.append("text")
.attr("x", width / 2)
.attr("y", -10)
.attr("text-anchor", "middle")
.style("font-weight", "bold")
.text(`Balance de donaciones y recepciones para ${countrySelect}`);

const legend = svg.append("g").attr("transform", `translate(${width - 50},${margin.top})`);
legend.append("rect").attr("x", -20).attr("width", 12).attr("height", 12).attr("fill", "#d62728");
legend.append("text").attr("x", -5).attr("y", 10).text("Donado").attr("font-size", "12px");
legend.append("rect").attr("x", -20).attr("y", 20).attr("width", 12).attr("height", 12).attr("fill", "#1f77b4");
legend.append("text").attr("x", -5).attr("y", 30).text("Recibido").attr("font-size", "12px");

return svg.node()
}
Insert cell
Select a data source…
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
filterede = aiddata.filter(d => d.purpose !== "UNSPECIFIED");
Insert cell
grouped = d3.rollups(
filterede,
v => d3.sum(v, d => d.amount),
d => d.purpose,
d => d.yearInt
);
Insert cell
totalsd = grouped.map(([purpose, yearGroup]) => {
return {
purpose,
total: d3.sum(yearGroup, ([, amount]) => amount),
values: yearGroup
.map(([yearInt, amount]) => ({ year: +yearInt, amount }))
};
});
Insert cell
top1s0 = totalsd
.sort((a, b) => d3.descending(a.total, b.total))
.slice(0, 10);
Insert cell
years = Array.from(new Set(aiddata.map(d => d.yearInt))).sort((a, b) => a - b)
Insert cell
{
top1s0.forEach(d => {
const map = new Map(d.values.map(v => [v.year, v.amount]));
d.values = years.map(year => ({
year,
amount: map.get(year) || 0
}));
});
const svg = d3.create("svg")
.attr("width", 1000)
.attr("height", height)

const x = d3.scaleLinear()
.domain(d3.extent(years))
.range([margin.left, width - margin.right]);

const y = d3.scaleLinear()
.domain([0, d3.max(top1s0.flatMap(d => d.values.map(v => v.amount)))]).nice()
.range([height - margin.bottom, margin.top]);

const color = d3.scaleOrdinal()
.domain(top1s0.map(d => d.purpose))
.range(d3.schemeTableau10);

const line = d3.line()
.curve(d3.curveMonotoneX)
.x(d => x(d.year))
.y(d => y(d.amount));

svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d3.format("d")))
.append("text")
.attr("x", width / 2)
.attr("y", 35)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Año");

svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.append("text")
.attr("x", -margin.left + 10)
.attr("y", margin.top - 20)
.attr("fill", "black")
.attr("text-anchor", "start")
.text("Monto donado");

svg.selectAll(".line")
.data(top1s0)
.join("path")
.attr("fill", "none")
.attr("stroke", d => color(d.purpose))
.attr("stroke-width", 2)
.attr("d", d => line(d.values));

svg.selectAll(".legend")
.data(top1s0)
.join("text")
.attr("x", width - margin.right + 10)
.attr("y", (d, i) => margin.top + i * 20)
.text(d => d.purpose)
.attr("fill", d => color(d.purpose))
.style("font-size", "12px")
.style("alignment-baseline", "middle");

return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require('d3@7')
Insert cell
googleSheetCsvUrl = 'https://docs.google.com/spreadsheets/d/1YiuHdfZv_JZ-igOemKJMRaU8dkucfmHxOP6Od3FraW8/gviz/tq?tqx=out:csv'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more