Public
Edited
Nov 22, 2023
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
geoJSON = FileAttachment("countries-50m.json").json()
Insert cell
Insert cell
Insert cell
Insert cell
import {Legend, swatches} from "@d3/color-legend"
Insert cell
Insert cell
Insert cell
// add cells here
Insert cell
Insert cell
top_10_recipients = d3.rollups(
aiddata,
group => d3.sum(group, d => d.amount),
d => d.recipient
).sort((a, b) => d3.descending(a[1], b[1]))
.slice(0, 10);
Insert cell
recipients = d3.map(top_10_recipients, data=>data[0])
Insert cell
top_20_donors = d3.rollups(
aiddata,
group => d3.sum(group, d => d.amount),
d => d.donor
).sort((a, b) => d3.descending(a[1], b[1]))
.slice(0, 20);
Insert cell
donors = d3.map(top_20_donors, data=>data[0])
Insert cell
data = {var filteredData = aiddata.filter(function(d) {
return recipients.includes(d.recipient) && donors.includes(d.donor);
});

var result = [];

recipients.forEach(function(recipient) {
var recipientEntry = { index: recipient };

// Initialize each donor's amount to 0 for the recipient
donors.forEach(function(donor) {
recipientEntry[donor] = 0;
});

// Filter data for the current recipient
var recipientData = filteredData.filter(function(d) {
return d.recipient === recipient;
});

// Calculate the sum of donations for each donor
recipientData.forEach(function(d) {
recipientEntry[d.donor] += d.amount;
});

result.push(recipientEntry);
});
return result}
Insert cell
height = width
Insert cell
margin = ({top: width/10, right: width/10, bottom: width/5, left: width/5})
Insert cell
x = d3.scaleBand()
.domain(recipients)
.range([margin.left, width - margin.right])
.padding(0.01)
Insert cell
y = d3.scaleBand()
.domain([...donors].reverse())
.range([height - margin.bottom, margin.top])
.padding(0.01)
Insert cell
max_value = d3.max(aiddata, data=>data.amount)
Insert cell
color = d3.scaleSequentialSqrt()
.domain([0,max_value])
.interpolator(d3.interpolateYlOrBr)


Insert cell
xAxis = g => g
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.style("text-anchor", "start")
.attr("dx", "-.8em")
.attr("dy", ".8em")
.attr("transform", "rotate(30)")
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y).ticks(null, "s"))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.text(d => d)
Insert cell
Legend(d3.scaleSequentialSqrt([0, max_value], d3.interpolateYlOrBr), {
title: "Donation (Dollars)",
tickFormat: d3.format("(.2s")

})
Insert cell
programs_matrix = {
const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")
.style("font", "1rem verdana");
const make_class = (item) => item.toLowerCase().split(' ').join('_').split('-').join('')
const make_id = d => `coords_${Math.floor(x(d.xval))}_${Math.floor(y(d.yval))}`


const rects = svg.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("class", (d, i) => `${i} bar`)
.selectAll("g")
.data(d => {
const innerData = [];
for (const donor in d) {
if (donor !== "index") {
innerData.push({
xval: d.index, // Recipient
yval: donor, // Donor (country)
count: d[donor] // Numerical data
});
}
}
console.log(innerData)
return innerData;
})
.enter().append("g");

rects.append("rect")
.attr("x", d => x(d.xval))
.attr("y", d => y(d.yval))
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.style("fill", d => d.count === 0 ? "lightgray" : color(d.count))
.append("title")
.text(d => d.count)

svg.append("text")
.attr("x", width / 2 + 10) //
.attr("y", height - margin.bottom/1.5)
.style("text-anchor", "middle")
.text("Recipients");

svg.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", margin.left-80)
.style("text-anchor", "middle")
.text("Donors");
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);

return svg.node();
}
Insert cell
Insert cell
Insert cell
top_5 = d3.rollups(
aiddata,
group => d3.sum(group, d => d.amount),
d => d.purpose
).sort((a, b) => d3.descending(a[1], b[1]))
.slice(0, 5);
Insert cell
purposes = d3.map(top_5, d=>d[0])
Insert cell
data_filtered = d3.map(aiddata, d=>d).filter(d => purposes.includes(d.purpose))
.filter(d=> recipients.includes(d.recipient) )
.filter(d=> donors.includes(d.donor));
Insert cell
data0 = {
const result = {}
const grouped = d3.group(data_filtered, d => d.donor)
for (const name of grouped){
let data = {}
let group = d3.group(name[1], d => d.recipient)
for (const recipient of group){
let first = d3.rollup(recipient[1], v => d3.sum(v, d => d.amount), d=> d.purpose)
let second = Array.from(first, ([purpose, amount]) => ({purpose, amount}))

let sorted = second.sort((a, b) => d3.ascending(a.purpose, b.purpose))
data[recipient[0]] = sorted
}
result[name[0]] = data
}
return result
}
Insert cell
color_map = d3.scaleOrdinal()
.domain(purposes)
.range(d3.schemeCategory10);
Insert cell
x0 = d3.scaleBand()
.domain(recipients)
.range([0, width])
.padding(0.1);
Insert cell
countrypair = {
let data = []
for (var i =0;i< donors.length;i++) {
var donor = donors[i];
for (var j =0;j< recipients.length;j++) {
var recipient = recipients[j];
data.push({donor : donor, recipient : recipient})
}
}
return data
}
Insert cell
swatches({color: color_map})
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