Published
Edited
Oct 28, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
voteDelay = 200
Insert cell
leanScale = state => {
let d = leaningsByState.get(getState(state));
// if (!d) return "#999"//console.log(state, getState(state), d);
if (!d) return width / 2;
if (d.party == "R") {
return d3
.scaleLinear()
.domain(repExtent)
.range([width / 2, width])(d.value);
} else {
return d3
.scaleLinear()
.domain(demExtent)
.range([width / 2, 0])(d.value);
}
}
Insert cell
units2016.filter(d => d.state.indexOf("-") >= 0)
Insert cell
units2016 = {
let units = [];
let ui = 0;
let dems = states
.filter(
d => d[2016].electoral_D > 0 && statesReporting2016.indexOf(d.state) >= 0
)
.sort((a, b) => {
// return b.votes - a.votes;
return leanScale(a.state) - leanScale(b.state);
});
let reps = states
.filter(
d => d[2016].electoral_R > 0 && statesReporting2016.indexOf(d.state) >= 0
)
.sort((a, b) => {
// return a.votes - b.votes;
return leanScale(a.state) - leanScale(b.state);
});

let unknowns = states
.filter(
d =>
// some states didn't pledge all their votes to the 2 main candidates
d[2016].electoral_D + d[2016].electoral_R < d.votes ||
// or they didn't report yet
statesReporting2016.indexOf(d.state) < 0
)
.sort((a, b) => {
return leanScale(a.state) - leanScale(b.state);
});

dems.forEach(d => {
d3.range(d[2016].electoral_D).forEach(i => {
// add a unit for each vote
units.push({
...d,
D: 1,
i,
ui: ui++,
color: blue
});
});
});

ui++;
unknowns.forEach(d => {
let votes = d.votes;
if (statesReporting2016.indexOf(d.state) >= 0) {
votes = d.votes - (d[2016].electoral_D + d[2016].electoral_R);
}
d3.range(votes).forEach(i => {
// add a unit for each vote
units.push({
...d,
i,
ui: ui++,
color: gray
});
});
});

ui++;
reps.forEach(d => {
d3.range(d[2016].electoral_R).forEach(i => {
// add a unit for each vote
units.push({
...d,
R: 1,
i,
ui: ui++,
color: red
});
});
});
return units;
}
Insert cell
units2016leaning = {
let units = [];
let ui = 3;

let leaned = states
.map(d => {
return { ...d };
})
.sort((a, b) => {
return leanScale(a.state) - leanScale(b.state);
})
.forEach(d => {
if (statesReporting2016.indexOf(d.state) >= 0) {
d3.range(d[2016].electoral_D).forEach(i => {
// add a unit for each vote
units.push({
...d,
D: 1,
i,
ui: ui++,
color: blue
});
});
d3.range(d[2016].electoral_R).forEach(i => {
// add a unit for each vote
units.push({
...d,
R: 1,
i,
ui: ui++,
color: red
});
});
}

// if we don't know the result of these votes
let votes = d.votes;
if (statesReporting2016.indexOf(d.state) >= 0) {
votes = d.votes - (d[2016].electoral_D + d[2016].electoral_R);
}
d3.range(votes).forEach(i => {
// add a unit for each vote
units.push({
...d,
i,
ui: ui++,
color: gray
});
});
});

return units;
}
Insert cell
Insert cell
d3.sum(states, d => d.votes)
Insert cell
R2016 = d3.sum(states, d => d[2016].electoral_R)
Insert cell
D2016 = d3.sum(states, d => d[2016].electoral_D)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rows = 5
Insert cell
rw = (width - 5) / (538 / rows)
Insert cell
Insert cell
dems = states.filter(d => d[2016].electoral_D > 0)
Insert cell
unknowns = states.filter(
d => d[2016].electoral_D + d[2016].electoral_R < d.votes
)
Insert cell
reps = states.filter(d => d[2016].electoral_R > 0)
Insert cell
Insert cell
Insert cell
timings2016ByState = new Map(timings2016)
Insert cell
statesByTimings2016 = d3.group(timings2016, d => d[1])
Insert cell
statesReporting2016 = timings2016
.filter(d => +new Date(d[1]) <= +new Date(time2016) + 1)
.map(d => d[0])
Insert cell
states
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
states = Array.from(electoral_votes).map(([state, votes]) => {
let s = getState(state);
let c = cartogramByState.get(s);
if (!c) console.log(state, s, c);
let r2016 = results2016ByState.get(c.state_postal);
if (s == "Nebraska") {
//https://en.wikipedia.org/wiki/2016_United_States_presidential_election_in_Nebraska
r2016.electoral_R = votes;
}
if (state == "Maine") {
r2016.electoral_D = 2;
r2016.electoral_R = 0;
} else if (state == "ME-1") {
r2016.electoral_D = 1;
r2016.electoral_R = 0;
} else if (state == "ME-2") {
r2016.electoral_D = 0;
r2016.electoral_R = 1;
}

return {
state: state,
state_full: c.state_full,
state_postal: c.state_postal,
row: c.row,
column: c.column,
2016: r2016,
votes: votes
};
})
Insert cell
import { electoral_votes } from "@eyeseast/fivethirtyeight-2020-forecast-data"
Insert cell
electoral_votes
Insert cell
d3.sum(Array.from(electoral_votes.values()))
Insert cell
results2016 = FileAttachment("Table 2. Electoral & Pop Vote-Table 1.csv").csv()
Insert cell
results2016ByState = new Map(
results2016.map(d => [
d.STATE,
{
state_postal: d.STATE,
// TODO: deal with third parties
electoral_R: +d["ELECTORAL VOTE Trump (R)"]
.replace("**", "")
.replace("*", ""),
electoral_D: +d["ELECTORAL VOTE Clinton (D)"]
.replace("**", "")
.replace("*", "")
}
])
)
Insert cell
state_size = width / 14
Insert cell
Insert cell
Insert cell
cartogram_data
Insert cell
cartogram_data_mapped = cartogram_data.map(d => [d.state_full, d])
Insert cell
cartogramByState = new Map(cartogram_data_mapped)
Insert cell
margin = ({
top: 40,
left: 40,
right: 40,
bottom: 0
})
Insert cell
repExtent = d3.extent(
Array.from(leaningsByState).filter(d => d[1].party == "R"),
d => d[1].value
)
Insert cell
demExtent = d3.extent(
Array.from(leaningsByState).filter(d => d[1].party == "D"),
d => d[1].value
)
Insert cell
color = state => {
let d = leaningsByState.get(getState(state));
if (!d) return "#999"//console.log(state, getState(state), d);
if (d.party == "R") {
return repScale(d.value);
} else {
return demScale(d.value);
}
}
Insert cell
repScale = d3.scaleSequential(d3.interpolateReds).domain(repExtent)
Insert cell
demScale = d3.scaleSequential(d3.interpolateBlues).domain(demExtent)
Insert cell
leanings = d3.csv(
"https://raw.githubusercontent.com/fivethirtyeight/data/master/partisan-lean/fivethirtyeight_partisan_lean_STATES.csv"
)
Insert cell
leaningsByState = {
let processed = leanings.map(d => {
// let s = d.pvi_538;
let s = d[2020];
return [
d.state,
{
state: d.state,
party: s.slice(0, 1),
value: +s.slice(1)
}
];
});
return new Map(processed);
}
Insert cell
getCenter("Florida")
Insert cell
function getCenter(state) {
return centroidsByName.get(getState(state)).center;
}
Insert cell
function getState(state) {
let s = state;
if (state.indexOf("NE-") == 0) {
s = "Nebraska";
} else if (state.indexOf("ME-") == 0) {
s = "Maine";
}
return s
}
Insert cell
function stateUnits(options) {
let cols = options.columns || 10;
let r = options.r * 2 || 10;
let spacing = options.spacing || 0;

let array = [];
let i = 0;

Array.from(electoral_votes).forEach(d => {
let state = d[0];
let count = d[1];
d3.range(count).forEach(j => {
let tx = margin.left + (i % cols) * (r + spacing);
let ty = margin.top + Math.floor(i / cols) * (r + spacing);
array.push({
i: i,
x: tx,
y: ty,
tx: tx,
ty: ty,
r: r,
state
});
i += 1;
});
});

return array;
}
Insert cell
us = d3.json("https://unpkg.com/us-atlas@3/counties-10m.json")
Insert cell
stateShapes = topojson.feature(us, us.objects.states)//.features
Insert cell
centroids = stateShapes.features.map(d => {
let center = turf.centroid(d).geometry.coordinates;
return {
fips: d.id,
name: d.properties.name,
center: projection(center)
};
})
Insert cell
centroidsByName = new Map(centroids.map(d => [d.name, d]))
Insert cell
projection = d3.geoAlbersUsa().fitSize([width, height], stateShapes)
Insert cell
blue = "#002868"
Insert cell
red = "#bf0a30"
Insert cell
gray = "lightgray"
Insert cell
height = 620
Insert cell
import { slider, select } from "@jashkenas/inputs"
Insert cell
d3 = require("d3")
Insert cell
turf = require("@turf/turf@5")
Insert cell
topojson = require("topojson-client")
Insert cell
fonts = html`<span></span><link href="https://fonts.googleapis.com/css2?family=Seymour+One&family=Share+Tech+Mono&family=Ubuntu+Mono:wght@400;700&display=swap" rel="stylesheet">
`
Insert cell
import { twitch } from "@codingwithfire/twitch"
Insert cell
import { Scrubber } from "@mbostock/scrubber"
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