Published
Edited
Sep 3, 2020
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function update() {
// The local data is done on top of a nested set so that the assigned x and y values
// persist by state.
let local = grouped2.map(
d => {
const datum = d.data.filter(e => e.year == year)
if (datum.length){
Object.assign(d, datum[0])
}
return d
})
.filter(d => d.year == year) // Might be left over from a previous run.

let circles = d3
.select(svg)
.selectAll("g")
.data(local, d => d.State)

const size = d3.scaleSqrt().domain(d3.extent(local.map(d => +d[size_variable]))).range([0, width/20])

circles = circles
.join(
enter => {
const g = enter.append("g").attr("transform", `translate(${width/2}, ${height/2})`)
g.append("circle").attr("cx", 0).attr("cy", 0).attr("stroke", "black")

g
.append("text")
.text(d => d.Abbreviation)
.attr("dominant-baseline", "middle")
.attr("font-style", "sans-serif")
.attr("text-anchor", "middle")
return g
},
update => update,
exit => exit.call(
exit => {
exit.selectAll("circle").transition().duration(2000).attr("r", 0)
exit.selectAll("text").transition().duration(2000).attr("font-size", 0)
}
)
.transition().delay(2000).remove()
)

const colormap = d3.scaleOrdinal().domain(["Republican", "Democratic", "Whig/Other"]).range(["#BC4830", "#85C1E9", "orange", "orange", "orange", "orange"])
circles
.selectAll("circle")
.attr("r", d => size(+d[size_variable]))
.style("fill", d => colormap(d[side]))

circles
.selectAll("text").text(d => d.Abbreviation)

// Categorical mapping
const xmap = d3.scalePoint().range([0, width]).padding(.5)
const x_values = Array.from(new Set(local.map(d => d[side])))
x_values.sort()
xmap.domain(x_values)

// Labels
const candidates = d3.group(local, d => d[side])
d3.select(svg).selectAll("g.label").remove() // God knows
let labels = d3.select(svg)
.selectAll("g.label")
.data(x_values.map(d => [d, candidates.get(d)[0].winner_name]),
d => d[0])
labels = labels.join(
enter => {
const e = enter.append("g").attr("class", "label")
e.append("text");
e.append("text").attr("transform", "translate(0, 25)")
return e
},
update => update.remove(),
exit => exit.remove()
)
.attr("transform", d => `translate(${xmap(d[0])}, 22)`)
.selectAll("text")
.style("font-size", 24)
.attr("text-anchor", "middle")
.attr("fill", d => colormap(d[0]))
.text((d, i) => {
console.log(d, i)
return d[i]
})
// Done with labels.
const simulation = d3.forceSimulation(local)
.force(
"breakout",
forceBreakout(
datum => xmap(datum[side]), // x accessor
datum => height/2), // y accessor
.0000003 // Strength
)
.force("collide", d3.forceCollide(d => d3.max([size(d[size_variable]), 15])), .001);

simulation.on("tick", () => {
circles
.attr("transform", d => `translate(${d.x}, ${d.y})`)
});

invalidation.then(() => simulation.stop());
}
Insert cell
{
svg
grouped2
update()
}
Insert cell
color = "party"
Insert cell
width = 840
Insert cell
height = width * 1/2
Insert cell
grouped2 =

{
const cleaned = data
data.forEach( d=> {
d.total_votes = +d.total_votes
d.margin = +d.winner_votes - (d.total_votes - d.winner_votes);
d
})
return Array.from(d3.rollup(cleaned, d => {return {"State": d[0].State, data: d}}, d => d.State).values())
}
Insert cell
data = d3.csv("//benschmidt.org/notebooks/prez_results.csv")
Insert cell
data[0]
Insert cell
import { radio, select, slider } from '@jashkenas/inputs';
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