annotateStateData = (singleStateData) => {
const blurredCases = blur(singleStateData.map(d => d['actuals.newCases']))
const blurredDeaths = blur(singleStateData.map(d => d['actuals.newDeaths']))
const memo = {
vaccinesInitiatedRatioCursor: 0,
vaccinesCompletedRatioCursor: 0,
caseWaveCursor: 0,
deathWaveCursor: 0,
}
return singleStateData.map((d, i, arr) => ({
date: d.date,
newCases: d['actuals.newCases'],
newDeaths: d['actuals.newDeaths'],
newCasesSmooth: blurredCases[i],
newDeathsSmooth: blurredDeaths[i],
caseMinimum: blurredCases[i - 1] > blurredCases[i] && blurredCases[i + 1] > blurredCases[i],
deathMinimum: blurredDeaths[i - 1] > blurredDeaths[i] && blurredDeaths[i + 1] > blurredDeaths[i],
vaccinesInitiatedRatio: d['metrics.vaccinationsInitiatedRatio'],
vaccinesCompletedRatio: d['metrics.vaccinationsCompletedRatio'],
state: d.state,
}))
.map((d, i, all) => {
memo.vaccinesInitiatedRatioCursor = d.vaccinesInitiatedRatio ? d.vaccinesInitiatedRatio : memo.vaccinesInitiatedRatioCursor
memo.vaccinesCompletedRatioCursor = d.vaccinesCompletedRatio ? d.vaccinesCompletedRatio : memo.vaccinesCompletedRatioCursor
return {
...d,
vaccinesInitiatedRatio: memo.vaccinesInitatedRatioCursor,
vaccinesCompletedRatio: memo.vaccinesCompletedRatioCursor,
}
})
.map((d, i, arr) => ({
...d,
// Find the next death peak data point and record it's index in the dataset
nextDeathMinimumIdx: (
arr.slice(i).findIndex(d => d.deathMinimum) !== -1 ?
i + arr.slice(i).findIndex(d => d.deathMinimum) :
null
),
}))
.map((d, i, arr) => ({
...d,
// Fetch the next death minimum date
nextDeathMinimum: (
d.nextDeathMinimumIdx ?
arr[d.nextDeathMinimumIdx].date :
null
)
}))
.map((d, i, arr) => ({
...d,
// Calculate the time in days until the next death minimum
daysTillNextDeathMinimum: (
d.nextDeathMinimum ?
d3.timeDay.range(d.date, d.nextDeathMinimum).length :
null
)
}))
.map((d, i, arr) => ({
...d,
// Mark all case minima that have a corresponding death minimum
isValidCaseMinimum: validMinimaPair(d),
}))
.map((d, i, arr) => ({
...d,
// Mark all death minima that have a corresponding case minimum
isValidDeathMinimum: !!arr.find(p => p.isValidCaseMinimum && p.nextDeathMinimumIdx === i)
}))
.map((d, i, arr) => {
// This function annotates datapoints with what wave they're part of
// When it encounters a new minimum, it increments the wave number
if (d.isValidCaseMinimum) memo.caseWaveCursor += 1
if (d.isValidDeathMinimum) memo.deathWaveCursor += 1
return {
...d,
// Break datapoints into discrete wave categories according to how many case or death minima precede that datapoint
caseWave: memo.caseWaveCursor,
deathWave: memo.deathWaveCursor,
}
})
}