Public
Edited
Sep 14, 2023
Insert cell
Insert cell
chart = {
const width = 928;
const height = width;
const innerRadius = 180;
const outerRadius = Math.min(width, height) / 2;

// Stack the data into series by age
const series = d3.stack()
.keys(d3.union(data.map(d => d.age))) // distinct series keys, in input order
.value(([, D], key) => D.get(key).population) // get value for each series key and stack
(d3.index(data, d => d.state, d => d.age)); // group by stack then series key

const arc = d3.arc()
.innerRadius(d => y(d[0]))
.outerRadius(d => y(d[1]))
.startAngle(d => x(d.data[0]))
.endAngle(d => x(d.data[0]) + x.bandwidth())
.padAngle(1.5 / innerRadius)
.padRadius(innerRadius);

// An angular x-scale
const x = d3.scaleBand()
.domain(data.map(d => d.state))
.range([0, 2 * Math.PI])
.align(0);

// A radial y-scale maintains area proportionality of radial bars
const y = d3.scaleRadial()
.domain([0, d3.max(series, d => d3.max(d, d => d[1]))])
.range([innerRadius, outerRadius]);

const color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(d3.schemeSpectral[series.length])
.unknown("#ccc");

// A function to format the value in the tooltip
const formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");

// A group for each series, and a rect for each element in the series
svg.append("g")
.selectAll()
.data(series)
.join("g")
.attr("fill", d => color(d.key))
.selectAll("path")
.data(D => D.map(d => (d.key = D.key, d)))
.join("path")
.attr("d", arc)
.append("title")
.text(d => `${d.data[0]} ${d.key}\n${formatValue(d.data[1].get(d.key).population)}`);

// x axis
svg.append("g")
.attr("text-anchor", "middle")
.selectAll()
.data(x.domain())
.join("g")
.attr("transform", d => `
rotate(${((x(d) + x.bandwidth() / 2) * 180 / Math.PI - 90)})
translate(${innerRadius},0)
`)
.call(g => g.append("line")
.attr("x2", -5)
.attr("stroke", "#000"))
.call(g => g.append("text")
.attr("transform", d => (x(d) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI
? "rotate(90)translate(0,16)"
: "rotate(-90)translate(0,-9)")
.text(d => d));

// y axis
svg.append("g")
.attr("text-anchor", "middle")
.call(g => g.append("text")
.attr("y", d => -y(y.ticks(5).pop()))
.attr("dy", "-1em")
.text("This is title"))
.call(g => g.selectAll("g")
.data(y.ticks(5).slice(1))
.join("g")
.attr("fill", "none")
.call(g => g.append("circle")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.5)
.attr("r", y))
.call(g => g.append("text")
.attr("y", d => -y(d))
.attr("dy", "0.35em")
.attr("stroke", "#fff")
.attr("stroke-width", 5)
.text(y.tickFormat(5, "s"))
.clone(true)
.attr("fill", "#000")
.attr("stroke", "none")));

// color legend
svg.append("g")
.selectAll()
.data(color.domain())
.join("g")
.attr("transform", (d, i, nodes) => `translate(-40,${(nodes.length / 2 - i - 1) * 20})`)
.call(g => g.append("rect")
.attr("width", 18)
.attr("height", 18)
.attr("fill", color))
.call(g => g.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", "0.35em")
.text(d => d));

return svg.node();
}
Insert cell
chart1 = {
const width = 928;
const height = width;
const innerRadius = 180;
const outerRadius = Math.min(width, height) / 2;

// Stack the data into series by age
const series = d3.stack()
.keys(d3.union(data.map(d => d.age))) // distinct series keys, in input order
.value(([, D], key) => D.get(key).population) // get value for each series key and stack
(d3.index(data, d => d.state, d => d.age)); // group by stack then series key
const arc = d3.arc()
.innerRadius(d => y(d[0]))
.outerRadius(d => y(d[1]))
.startAngle(d => x(d.data[0]))
.endAngle(d => x(d.data[0]) + x.bandwidth())
.padAngle(1.5 / innerRadius)
.padRadius(innerRadius);

// An angular x-scale
const x = d3.scaleBand()
.domain(data.map(d => d.state))
.range([0, 2 * Math.PI])
.align(0);

// A radial y-scale maintains area proportionality of radial bars
const y = d3.scaleRadial()
.domain([0, d3.max(series, d => d3.max(d, d => d[1]))])
.range([innerRadius, outerRadius]);

const color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(d3.schemeSpectral[series.length])
.unknown("#ccc");

// A function to format the value in the tooltip
const formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "width: 100%; height: auto; font: 8px sans-serif;");

// A group for each series, and a rect for each element in the series
svg.append("g")
.selectAll()
.data(series)
.join("g")
.attr("fill", d => color(d.key))
.selectAll("path")
.data(D => D.map(d => (d.key = D.key, d)))
.join("path")
.attr("d", arc)
.append("title")
.text(d => `${d.data[0]} ${d.key}\n${formatValue(d.data[1].get(d.key).population)}`);

// x axis
svg.append("g")
.attr("text-anchor", "middle")
.selectAll()
.data(x.domain())
.join("g")
.attr("transform", d => `
rotate(${((x(d) + x.bandwidth() / 2) * 180 / Math.PI - 90)})
translate(${innerRadius},0)
`)
.call(g => g.append("line")
.attr("x2", -5)
.attr("stroke", "#000"))
.call(g => g.append("text")
.attr("transform", d => (x(d) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI
? "rotate(90)translate(0,16)"
: "rotate(-90)translate(0,-9)")
.text(d => d));



// color legend
svg.append("g")
.selectAll()
.data(color.domain())
.join("g")
.attr("transform", (d, i, nodes) => `translate(-40,${(nodes.length / 2 - i - 1) * 20})`)
.call(g => g.append("rect")
.attr("width", 18)
.attr("height", 18)
.attr("fill", color))
.call(g => g.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", "0.35em")
.text(d => d));

return svg.node();
}
Insert cell
data = {
const string = `State, GN, GR, SN, RES, CAN, RIV
NWN,0,0.0144,0.639,0.0419,0.0437,1.76
NEN,0,0.0125,0.00146,0,0.00457,0.0449
WNA,4.08,13.8,20.5,4.28,1.49,32.7
CNA,30.1,40.9,3.96,0.427,1.06,11.3
ENA,0.195,7.71,1.96,0.226,0.0846,3.72
NCA,6.33,8.9,23.6,2.08,1.01,21.7
SCA,0.105,1.47,2.5,0.435,0.111,3.15
CAR,0,0.381,1.15,0.426,0,1.07
NWS,0.797,1.54,9.41,0.455,0.613,9.36
NSA,0.00194,0.215,0.911,0.236,0.0183,2.97
NES,0.01,1.26,1.7,0.199,0.138,5.48
SAM,0,0.0741,0.234,0.00583,0.0901,0.788
SWS,0.0882,0.298,7.14,0.109,0.428,3.88
SES,0.167,3.09,11.7,0.709,0.415,28.5
SSA,0,0.000254,0.00471,0,0,0.305
NEU,0.000656,2.09,0.354,0.0356,0.00375,2.21
WCE,0.028,6.13,10.4,0.059,0.778,30.2
EEU,0.00308,1.86,3.44,0.114,0.12,10.8
MED,4.24,24.2,42.2,5.2,1.2,58.6
SAH,1.38,1.2,11.7,0.0227,0.567,30.1
WAF,0,0.305,0.958,0.116,0.0973,5.4
CAF,0,0.0752,0.173,0.0612,0.044,0.541
NEAF,0,0.358,8.15,0.0306,0.423,8.8
SEAF,0,0.0467,0.624,0.00942,0.0567,2.71
WSAF,0.000474,0.318,1.74,0.52,0.305,3.24
ESAF,2.42E-05,0.505,2.97,1.76,0.508,6.24
MDG,0,0,5.1,0.164,0.111,9.21
WSB,0.0649,0.956,5.93,0.0137,0.866,7.7
ESB,0.393,3.92,1.69,0,1.65,5.82
RFE,0.0172,1.24,0.316,0,0.379,1.12
WCA,35.1,36,135,1.87,14.8,157
ECA,5.19,4.64,39.6,0.0146,5.78,28.8
TIB,19.1,14.9,18.3,0.836,5.83,13.3
EAS,20,43.8,231,53.9,18.9,337
ARP,12.4,2.02,1.45,0,0.0474,0.742
SAS,122,147,257,19.9,30.5,331
SEA,0.124,6.1,91.7,9.79,3.38,140
NAU,0,0.028,0.048,0.00961,0.00867,0.229
CAU,0,0.000597,1.95E-06,0,1.03E-07,0.00104
EAU,0.0154,1.19,0.746,0.0725,0.22,4.69
SAU,0.0764,1.44,3.65,0.0818,0.0244,8.56
NZ,0,0.546,0.612,0.0599,0,1.06
BOB,0,0.0515,5.39,2.38,0,3.73
EIO,0,0.00476,0.922,0.494,0,1.24`;
const data = d3.csvParse(string);
return data.columns.slice(1).flatMap((age) => data.map((d) => ({state: d.State, age, population: d[age]})));
}
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