Published
Edited
Jan 1, 2021
1 fork
Insert cell
md`# Pie Chart`
Insert cell
chart = {
const svg = d3.create("svg")
.attr("font-size", "10pt")
.attr("cursor", "default")
.attr("viewBox", [0, 0, width, height]);
svg.append("g").call(g => drawGuidelines(g, chartData.map(d => d.year),
d => d3.line()([[x(d) + hx, margin.top],[x(d) + hx, height - margin.bottom]]))
);
svg.append("g").call(g => drawGuidelines(g, y.ticks().reverse().slice(1),
d => d3.line()([[margin.left, y(d)],[width - margin.left - margin.right, y(d)]]))
);
const g = svg.selectAll(".pie")
.data(chartData)
.join("g")
.attr("class", "pie")
.attr("transform", d => `translate(${x(d.year) + hx},${y(d.total)})`)
.call(g => g.append("text")
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("transform", d => `translate(0,${r(d.total)})`)
.text(d => d.total));
const pg = g.selectAll("g")
.data(d => d3.pie()(d.values).map(p => ({pie: p, total: d.total})))
.join("g")
.call(g => g.append("title")
.text((d, i) => `${fumer[i]}\n${d.pie.value} (${(d.pie.value / d.total * 100).toFixed(1)}%)`));
const slice = pg.append("path")
.attr("d", d => pie(d)())
.attr("opacity", 1)
.attr("fill", (d, i) => color(fumer[i]));
const pct = pg.append("text")
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("transform", (d, i) => {
const c = pie(d).centroid(d.pie.value);
return `translate(${c[0]},${c[1]})`;
})
.attr("opacity", "0")
.text(d => (d.pie.value / d.total * 100).toFixed(1) + "%");
svg.append("g").call(g => drawAxis(g, margin.left, 0, d3.axisLeft(y).ticks(height / 100, "s")));
svg.append("g").call(g => drawAxis(g, 0, height - margin.bottom, d3.axisBottom(x)));
svg.append("g").call(drawLegend);
return svg.node();
var legend;
function drawLegend(g) {
legend = g.attr("transform", `translate(${width - margin.right},0)`)
.selectAll("g")
.data(fumer)
.join("g")
.attr("transform", (d, i) => `translate(0,${i * 20})`)
.call(g => g.append("rect")
.attr("rx", 3).attr("ry", 3)
.attr("width", 20).attr("height", 15)
.attr("fill", d => color(d)))
.call(g => g.append("text").attr("dx", 25).attr("alignment-baseline", "hanging").text(d => d))
.on("mouseover", e => highlight(e))
.on("mouseout", () => highlight());
}
function highlight(e) {
const i = e ? legend.nodes().indexOf(e.currentTarget) : -1;
slice.transition().duration(500).attr("opacity", (d, j) => i === -1 || j === i ? 1 : 0.3);
pct.transition().duration(500)
.attr("opacity", function(d, j) {
if (j === i) {
this.parentNode.parentNode.appendChild(this.parentNode);
return 1;
}
else return 0;
});
}
}
Insert cell
drawAxis = (g, x, y, axis) =>
g.attr("transform", `translate(${x},${y})`)
.call(axis)
.selectAll(".tick text")
.attr("font-size", "9pt");
Insert cell
drawGuidelines = (g, data, line) => {
g.selectAll("path")
.data(data)
.join("path")
.attr("stroke", "#ddd")
.attr("stroke-dasharray", "5,5")
.attr("d", line);
}
Insert cell
x = d3.scaleBand()
.domain(chartData.map(d => d.year))
.range([margin.left, width - margin.left - margin.right])
Insert cell
hx = x.bandwidth() / 2
Insert cell
y = {
const ext = d3.extent(chartData.map(d => d.total));
// value(total) to length ratio of radius
const f = (ext[1] - ext[0]) / (height - margin.bottom - margin.top * 2);
// margin = 1.5 radius
ext[0] -= hx * 1.5 * f;
ext[1] += hx * 1.5 * f;
return d3.scaleLinear().domain(ext).range([height - margin.top - margin.bottom, margin.top])
}
Insert cell
r = d3.scaleLinear()
.domain(d3.extent(chartData.map(d => d.total)))
.range([hx / 2, hx])
Insert cell
pie = d => d3.arc()
.innerRadius(0)
.outerRadius(r(d.total))
.startAngle(d.pie.startAngle)
.endAngle(d.pie.endAngle)
Insert cell
color = d3.scaleOrdinal(d3.schemeTableau10).domain(fumer);
Insert cell
data = d3.csvParse(await FileAttachment("data_pies_correct.csv").text(), d3.autoType)
Insert cell
chartData = data.columns.slice(1).map(y => {
const values = data.map(d => d[y]);
return {
year: y,
values: values,
total: values.reduce((a, b) => a + b)
}
});
Insert cell
fumer = data.map(d => d.usage)
Insert cell
width = 1024
Insert cell
height = 768
Insert cell
margin = ({left: 40, bottom: 50, top: 10, right: 150})
Insert cell
d3 = require("d3@6")
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