Published
Edited
Nov 11, 2021
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
data = aiddata
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
Insert cell
{
const margin = {top: 50, right: 20, bottom: 70, left: 90};
const visWidth = width - margin.left - margin.right;
const visHeight = 500 - margin.top - margin.bottom;
const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

svg.append('text')
.attr("x", width / 2)
.attr("y", 0 + (margin.top))
.attr("text-anchor", "middle")
.text('Net Donations Given')
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

// Get Data
const recieved = d3.rollup(data, g => d3.sum(g.map(x => x.amount)), d => d.recipient)
const given = d3.rollup(data, g => d3.sum(g.map(x => x.amount)), d => d.donor)

// Create set of keys
const countries = new Set()
for (let [key, value] of recieved) {
countries.add(key)
}
for (let [key, value] of given) {
countries.add(key)
}
console.log(countries)

// Calculate attribute
const net_ammounts = new Map()
for(let country of countries){
net_ammounts.set(country, (given.get(country) || 0 ) - (recieved.get(country) || 0))
}

let Q = Array.from(countries).sort((a, b) => net_ammounts.get(a) < net_ammounts.get(b))
console.log(Q)

// Create Scales
const x = d3.scaleBand()
.domain(Q)
.range([0, visWidth]);

console.log('here')
console.log(d3.extent(net_ammounts.values()))
const y = d3.scaleLinear()
.domain(d3.extent(net_ammounts.values()))
.range([visHeight, 0]);
const y_abs_scale = d3.scaleLinear()
.domain([0, d3.max(net_ammounts.values()) - d3.min(net_ammounts.values())])
.range([0, visHeight]);
console.log(y_abs_scale(0))
console.log(y_abs_scale(10))
console.log(y_abs_scale(100000000000))
// Create Axes
const xAxis = d3.axisBottom(x);
const yAxis = d3.axisLeft(y);

const points = g.selectAll('g')
.data(countries)
.join('g')
.attr('transform', d => `translate(${x(d)}, ${y(0)})`);
points.append('rect')
.attr('width', 10)
.attr('height', d => y_abs_scale(Math.abs(net_ammounts.get(d))))
.attr('fill', d => net_ammounts.get(d) > 0 ? 'steelblue' : 'red')
.attr('transform', d => `scale(${1}, ${net_ammounts.get(d) > 0 ? -1 : 1})`)

g.append('g')
.attr('transform', `translate(0,${visHeight + 10})`)
.call(xAxis).selectAll('text')
.attr("dx", -24)
.attr("dy", 12)
.attr("transform", "rotate(-45)")
g.append('g')
.call(yAxis)
.call(g => g.selectAll('.domain').remove())
return svg.node();
}
Insert cell
Insert cell
{
// Get Data
const recieved = d3.rollup(data, g => d3.sum(g.map(x => x.amount)), d => d.recipient)
const given = d3.rollup(data, g => d3.sum(g.map(x => x.amount)), d => d.donor)

// Create set of keys
const countries = new Set()
for (let [key, value] of recieved) {
countries.add(key)
}
for (let [key, value] of given) {
countries.add(key)
}
console.log(countries)

// Calculate attribute
const net_ammounts = new Map()
for(let country of countries){
net_ammounts.set(country, (given.get(country) || 0 ) - (recieved.get(country) || 0))
}

// Color Scale
const color_positive = d3.scaleSequential(d3.interpolateBlues)
const color_negative = d3.scaleSequential(d3.interpolateReds)
const max_ammount = d3.max(net_ammounts.values())
const min_ammount = d3.min(net_ammounts.values())
const color = (x) => x ?
( x > 0 ? color_positive(x / max_ammount) : color_negative(-x / max_ammount) ) :
'lightgray'
// Geo Projection
// const projection = d3.geoAlbers().fitWidth(600, {type: 'Sphere'}).rotate([5,0])
const projection = d3.geoNaturalEarth1().fitWidth(600, {type: 'Sphere'})
const pathGenerator = d3.geoPath(projection)
// SVG start
const svg = d3.create('svg')
.attr('width', 600)
.attr('height', 350)

const name_corrections = {
'United States of America': 'United States'
}

const correct = (x, corrections) => corrections[x] ? corrections[x] : x

const statesDrawn = svg.selectAll(".state")
.data(geoJSON.features)
.join("path")
.attr("class", "state")
.attr("d", pathGenerator)
.attr("none", d => console.log(d))
.attr("none", d => console.log(d.properties.NAME))
.attr("fill", d => color(net_ammounts.get(correct(d.properties.NAME, name_corrections))))
// .attr("fill", d => net_ammounts.get(d.properties.NAME) ? 'red': "lightgray")

// initial value
// svg.property('value', value);
return svg.node();
}
Insert cell
Insert cell
pie = d3.pie()
.value(d => d[1])
.sort((a, b) => a.value > b.value)
Insert cell
radius = d3.scaleSqrt()
.domain([0, 10])
.range([0, 30])
Insert cell
arc = d3.arc()
.innerRadius(0)
.outerRadius(d => 50)
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