Published
Edited
Jul 1, 2021
Importers
2 stars
Insert cell
Insert cell
EAVSYears = [2012, 2014, 2016, 2018]
Insert cell
EAVSFile = FileAttachment("allYearsFlat@1.csv").csv()
Insert cell
allEAVSData = EAVSFile.map(d => {
let keys = Object.keys(d)
let obj = {}
keys.forEach(key => {
if(key.indexOf("|") >= 0) {
let k = key.split("|")[0]
let k2 = key.split("|")[1]
if(!obj[k]) obj[k] = { label: keyLabelsFile.get(k)}
obj[k][k2] = +d[key]
} else {
obj[key] = d[key]
}
})
return obj
})
Insert cell
Insert cell
keyLabels = new Map(keysByYear[2018].map(d => [d.key, d.label]))
Insert cell
keyLabelsFile = new Map(await FileAttachment("keyLables.json").json())
Insert cell
Insert cell
presYears = [2012, 2016]
Insert cell
rawElectionsData = new Object({
2012: await FileAttachment("election-data--2012.csv").csv(),
2016: await FileAttachment("election-data--2016.csv").csv()
})
Insert cell
getElectionForYear = (year) => {
return _.chain(rawElectionsData[year])
.groupBy('FIPS')
.map((parties, FIPS) => {
let {candidatevotes: demVotes} = _.find(parties, ({party}) => party === 'democrat')
let {candidatevotes: repVotes, totalvotes: totalVotes} = _.find(parties, ({party}) => party === 'republican')
demVotes = +demVotes
repVotes = +repVotes
totalVotes = +totalVotes
return {
FIPSCode: FIPS.toString().padStart(5, 0),
party: demVotes > repVotes ? 'D' : 'R',
demVotes, repVotes, totalVotes,
}
}).value()
}
Insert cell
allElectionsData = {
const allElections = {}
for (const year of presYears) {
const data = rawElectionsData[year]
// remember by county
_.chain(data)
.groupBy('FIPS')
.each((parties, FIPS) => {
const FIPSCode = FIPS.toString().padStart(5, 0)
let {candidatevotes: demVotes} = _.find(parties, ({party}) => party === 'democrat')
let {candidatevotes: repVotes, totalvotes: totalVotes} = _.find(parties, ({party}) => party === 'republican')
if (!allElections[FIPSCode]) {
allElections[FIPSCode] = {
FIPSCode,
winningParty: {label: 'Winning party (D or R)'},
demVotes: {label: 'Number of Democrat votes'},
repVotes: {label: 'Number of Republican votes'},
demMargin: {label: 'Democrat margin'},
repMargin: {label: 'Republican margin'},
winMargin: {label: 'Winning party margin'},
totalVotes: {label: 'Total number of votes'},
}
}
allElections[FIPSCode].winningParty[year] = +demVotes > +repVotes ? 'D' : 'R'
allElections[FIPSCode].repVotes[year] = +repVotes
allElections[FIPSCode].demVotes[year] = +demVotes
allElections[FIPSCode].demMargin[year] = +demVotes - +repVotes
allElections[FIPSCode].repMargin[year] = +repVotes - +demVotes
allElections[FIPSCode].winMargin[year] = Math.abs(+demVotes - +repVotes)
allElections[FIPSCode].totalVotes[year] = +totalVotes
}).value()
}
return _.values(allElections)
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
demographicsData
Insert cell
allDemographicsData = _.map(Array.from(demographicsData[2018]), ([FIPSCode, county]) => {
// for each country store an object with FIPSCode and demographics columns
// for each demo column remember the year & the label
const countyByYear = _.reduce(EAVSYears, (obj, year) => {
if (!demographicsData[year]) return obj
obj[year] = demographicsData[year].get(FIPSCode)
return obj
}, {})
return _.reduce(county, (obj, val, key) => {
// (TEMP) if key does not end in "E", return
if (_.last(key) !== 'E') return obj
if (_.includes(['NAME', 'state', 'county'], key)) return obj
// aggregate every year by same column
obj[key] = _.reduce(EAVSYears, (obj, year) => {
if (!countyByYear[year]) return obj
obj[year] = countyByYear[year][key]
return obj
}, {
label: niceLabel(key),
})
return obj
}, {FIPSCode})
})
Insert cell
Insert cell
masterData = _.map(allEAVSData, eavs => {
const election = _.find(allElectionsData, d => d.FIPSCode === eavs.FIPSCode)
const demographic = _.find(allDemographicsData, d => d.FIPSCode === eavs.FIPSCode)
// check if preclearance state
let isPrecleared = false
const {State_Full: state, Jurisdiction_Name: jurisdiction} = eavs
// check if county is in preclearance state
// check if preclearance applies to all states or only a subset
const preState = _.filter(preclearance, d => d.State.toLowerCase() === state.toLowerCase())
if (preState.length) {
const preJurisdictions = _.map(preState, d => d.Jurisdiction.toLowerCase())
if (preJurisdictions[0] === 'all') {
isPrecleared = true
} else if (_.includes(preJurisdictions, jurisdiction.toLowerCase())) {
isPrecleared = true
}
}
return Object.assign({isPrecleared}, election, demographic, eavs)
})
Insert cell
totalRepDemVotes = [
_.sumBy(masterData, d => d.repVotes && d.repVotes[2016]),
_.sumBy(masterData, d => d.demVotes && d.demVotes[2016]),
]
Insert cell
_.filter(allElectionsData, d => d.FIPSCode === '00000')
Insert cell
Insert cell
Insert cell
unemploymentData = d3.csv(`https://docs.google.com/spreadsheets/d/1ww8jWcpuzJAYwwk34-msjgm8WbdDPJ488tG1ReRX28A/gviz/tq?tqx=out:csv`)
.then(data => {
return _.map(data, d => {
const FIPSCode = d['FIPS Code'].toString().padStart(5, 0)
return {
FIPSCode,
total: +replaceCommas(d['Labor Force']),
employed: +replaceCommas(d['Employed']),
unemployed: +replaceCommas(d['Unemployed']),
rate: +d['Unemployment rate'] / 100,
}
})
})
Insert cell
replaceCommas = (str) => str.replace(/\,/g, '')
Insert cell
md`
## # preclearance states
`
Insert cell
preclearance = d3.csv(`https://docs.google.com/spreadsheets/d/1SsRUx7FYON9iwE2oiBrQDsXNuS1AujCwNa_AYFI0xM8/gviz/tq?tqx=out:csv`)
// .then(data => {
// return _.map(data, ({State, Jurisdiction}) => {
// if (Jurisdiction === 'All') return State.toUpperCase()
// return Jurisdiction.toUpperCase()
// })
// })
Insert cell
md`
## # Early Voting
`
Insert cell
earlyVotingData = new Object({
2016: cleanEarlyVotingData(await d3.csv(`https://docs.google.com/spreadsheets/d/1khkaan-NewSq1J6zQhBqNfna1kxlNUR--CmBp51EVVg/gviz/tq?tqx=out:csv&sheet=Early%20voting%202016`)),
2018: cleanEarlyVotingData(await d3.csv(`https://docs.google.com/spreadsheets/d/1khkaan-NewSq1J6zQhBqNfna1kxlNUR--CmBp51EVVg/gviz/tq?tqx=out:csv&sheet=Early%20voting%202020`)),
2020: cleanEarlyVotingData(await d3.csv(`https://docs.google.com/spreadsheets/d/1khkaan-NewSq1J6zQhBqNfna1kxlNUR--CmBp51EVVg/gviz/tq?tqx=out:csv&sheet=Early%20voting%202018`)),
})
Insert cell
Insert cell
Insert cell
countiesData2018 = calcCountiesData(2018)
Insert cell
statesData2018 = calcStatesData(countiesData2018)
Insert cell
countiesData2016 = calcCountiesData(2016)
Insert cell
statesData2016 = calcStatesData(countiesData2016)
Insert cell
calcCountiesData = (year) => {
return _.chain(masterData)
.map(({
FIPSCode, State_Full, State_Abbr, Jurisdiction_Name,
F1b, F1f, D4b, D4c, D5b, D5c,// GINI inequality index, all income, total population, Black population
B19083_001E, B19301_001E, B01003_001E, B02001_002E,
}) => {
const dayOf = F1b[year]
const early = F1f[year]
const dayOfPoll = D4b[year] + D4c[year]
const earlyPoll = D5b[year] + D5c[year]
let earlyDuration = _.find(earlyVotingData[year], ({state}) => State_Full === state)
earlyDuration = earlyDuration && earlyDuration.duration
if (!earlyPoll || !dayOfPoll || !earlyDuration) return

return {
id: FIPSCode, stateID: State_Abbr, state: State_Full,
label: `${Jurisdiction_Name}, ${State_Abbr}`,
earlyRatio: earlyDuration ? early / earlyPoll / earlyDuration : 0,
dayOfRatio: dayOf / dayOfPoll,
early, dayOf, earlyPoll, earlyDuration, dayOfPoll,
// demographics
inequality: B19083_001E && B19083_001E[year],
income: B19301_001E && B19301_001E[year],
poc: B02001_002E && 0 <= B02001_002E[year] && B01003_001E[year] && 0 <= B01003_001E[year]
&& (1 - B02001_002E[year] / B01003_001E[year]),
}
}).filter()
.sortBy(d => -d.dayOfRatio).value()
}
Insert cell
calcStatesData = (countiesData) => {
return _.chain(countiesData)
.groupBy('state')
.map((counties, state) => {
const dayOf = d3.sum(counties, ({dayOf}) => dayOf || 0)
const dayOfPoll = d3.sum(counties, ({dayOfPoll}) => dayOfPoll || 0)
const early = d3.sum(counties, ({early}) => early || 0)
const earlyPoll = d3.sum(counties, ({earlyPoll}) => earlyPoll || 0)
const earlyDuration = counties[0].earlyDuration
if (!earlyPoll || !dayOfPoll) return

return {
id: counties[0].stateID,
label: state,
earlyRatio: earlyDuration ? early / earlyPoll / earlyDuration : 0,
dayOfRatio: dayOf / dayOfPoll,
early, dayOf, earlyPoll, earlyDuration, dayOfPoll,
income: d3.median(counties, d => d.income),
inequality: d3.median(counties, d => d.inequality),
poc: d3.median(counties, d => d.poc),
}
}).filter()
.sortBy(d => -d.dayOfRatio).value()
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
d3 = require('d3')
Insert cell
_ = require('lodash')
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