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.scaleSequential()
.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.scaleSequential([0, max_value], d3.interpolateYlOrBr), {
title: "Donation (Dollars)"
})
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
data0 = {const filteredData = aiddata.filter(d =>
recipients.includes(d.recipient) &&
donors.includes(d.donor) &&
purposes.includes(d.purpose)
);

const purposeGroups = d3.rollups(
filteredData,
group => group.map(d => ({
source: d.donor,
target: d.recipient,
weight: d.amount
})),
d => d.purpose
);

// Convert the rollup into a more accessible structure
const purposeLinks = purposeGroups.map(([purpose, links]) => ({
purpose,
links
}));
return purposeLinks}
Insert cell
data0[0].links
Insert cell
lineWidth = d3.scaleLinear()
.domain([0, max_value])
.range([0, 20])
Insert cell
{
// Assuming 'width' is defined; replace it with your desired width if not
const width = 500; // example width

// Data: donations and their corresponding colors
const data = [
{ amount: 30, color: 'red' },
{ amount: 20, color: 'green' },
{ amount: 30, color: 'blue' },
{ amount: 20, color: 'yellow' },
{ amount: 50, color: 'purple' }
];

// Create an SVG container
const margin = { top: 50, right: 70, bottom: 70, left: 70 };
const visWidth = width - margin.left - margin.right;
const visHeight = 100; // Set a fixed height or calculate as needed

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

// Set up a scale for the x-axis
const totalAmount = d3.sum(data, d => d.amount);
const xScale = d3.scaleLinear()
.domain([0, totalAmount])
.range([0, visWidth]);

const height_changed = 20;
// Function to create a path for each data point
const createPath = (x, width, height) => {
return `M ${x},0 L ${x + width},0 L ${x + width},${height_changed} L ${x},${height_changed} Z`;
};

// Append paths for each data point
let acc = 0; // Accumulator for the x position
data.forEach(d => {
const pathWidth = xScale(d.amount) - xScale(0);
g.append('path')
.attr('d', createPath(acc, pathWidth, 50))
.attr('fill', d.color);
acc += pathWidth;
});

// Append the x-axis (optional)
// ...

return svg.node();

}
Insert cell
{
// set up
const margin = {top: 50, right: 70, bottom: 70, left: 70};
const visWidth = width - margin.left - margin.right;
const visHeight = width - margin.top - margin.bottom;

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// create scale

const y = d3.scalePoint()
.domain(donors)
.range([0, visHeight]);

const y2 = d3.scalePoint()
.domain(recipients)
.range([0, visHeight]);
// create and add axes
const leftAxis = d3.axisLeft(y).tickSize(0);
const rightAxis = d3.axisRight(y2).tickSize(0);
g.append("g")
.call(leftAxis)
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", -6)
.attr("y", -30)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Source");
g.append("g")
.attr('transform', `translate(${visWidth},0)`)
.call(rightAxis)
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", 6)
.attr("y", -30)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Target");
// draw lines

g.append('g')
.selectAll('line')
.data(data0[0].links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
.attr('stroke', 'steelblue')

g.append('g')
.selectAll('line')
.data(data0[1].links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
.attr('stroke', 'orange')

g.append('g')
.selectAll('line')
.data(data0[2].links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
.attr('stroke', 'red')



g.append('g')
.selectAll('line')
.data(data0[3].links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
.attr('stroke', 'green')


g.append('g')
.selectAll('line')
.data(data0[4].links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
.attr('stroke', 'purple')


return svg.node();
}
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