added doubling time, graph color tweaks

This commit is contained in:
tmont 2020-04-29 20:59:42 -07:00
parent df22e3c79f
commit 8bff273d8b
2 changed files with 80 additions and 48 deletions

View File

@ -154,23 +154,66 @@ const processGlobalDeaths = async () => {
// return record['Country/Region'] === 'US'; // return record['Country/Region'] === 'US';
// }); // });
const getGrowthRate = (record) => { const getRollingAverage = (item) => {
const ts = record.timeSeriesDaily; return item.timeSeriesDaily.map((item, i, arr) => {
const len = ts.length; const prevValues = arr.slice(Math.max(0, i - 6), i);
const valueAverage = (item.value + prevValues.reduce((sum, next) => sum + next.value, 0)) / (1 + prevValues.length);
const deltaAverage = (item.delta + prevValues.reduce((sum, next) => sum + next.delta, 0)) / (1 + prevValues.length);
return {
key: item.key,
value: Math.round(valueAverage),
delta: Math.round(deltaAverage),
};
});
};
const getDoublingTime = (item) => {
return item.timeSeriesDaily.map((item, i, arr) => {
const prevItems = arr.slice(Math.max(i - 7, 0), i);
const sum = prevItems.reduce((sum, next) => sum + next.value, 0);
let value;
if (sum < 10) {
value = 0;
} else {
const growthRate = calcGrowthRate(arr, i, 7);
if (growthRate <= 0 || !growthRate) {
value = 0;
} else {
value = (Math.log(2) / Math.log(1 + growthRate));
}
}
return {
key: item.key,
value: Math.round(value * 10) / 10,
};
});
};
const calcGrowthRate = (ts, i, range) => {
const items = ts.slice(Math.max(0, i - range), i + 1);
const len = items.length;
if (len < 2) { if (len < 2) {
return 0; return 0;
} }
const latest = len - 1; const latest = len - 1;
const earliest = Math.max(len - 14, 0); const earliest = 0;
const pow = 1/(latest - earliest + 1); const pow = 1 / (latest - earliest + 1);
const hi = ts[latest].value; const hi = items[latest].value;
const lo = Math.max(ts[earliest].value, 0.5); const lo = Math.max(items[earliest].value, 0.5);
if (hi === 0 && lo < 1) { if (hi === 0 && lo < 1) {
return 0; return 0;
} }
return Math.pow((hi / lo), pow) - 1; return Math.pow((hi / lo), pow) - 1;
}; };
const getGrowthRate = (record) => {
return calcGrowthRate(record.timeSeriesDaily, record.timeSeriesDaily.length - 1, 7);
};
// state/county data is separated for the US and doesn't need to be rolled up // state/county data is separated for the US and doesn't need to be rolled up
tsUSRecords.forEach((usRecord) => { tsUSRecords.forEach((usRecord) => {
const newRecord = { const newRecord = {
@ -442,19 +485,9 @@ const processGlobalDeaths = async () => {
}), }),
}; };
stateItem.rollingAverageDaily = stateItem.timeSeriesDaily.map((item, i, arr) => {
const prevValues = arr.slice(Math.max(0, i - 6), i);
const valueAverage = (item.value + prevValues.reduce((sum, next) => sum + next.value, 0)) / (1 + prevValues.length);
const deltaAverage = (item.delta + prevValues.reduce((sum, next) => sum + next.delta, 0)) / (1 + prevValues.length);
return {
key: item.key,
value: Math.round(valueAverage),
delta: Math.round(deltaAverage),
};
});
stateItem.deathGrowthRate = getGrowthRate(stateItem); stateItem.deathGrowthRate = getGrowthRate(stateItem);
stateItem.rollingAverageDaily = getRollingAverage(stateItem);
stateItem.doublingDaily = getDoublingTime(stateItem);
// insert into states array for the country // insert into states array for the country
perCountryTotals[item.country].states.push(stateItem); perCountryTotals[item.country].states.push(stateItem);
@ -492,19 +525,9 @@ const processGlobalDeaths = async () => {
}), }),
}; };
countryItem.rollingAverageDaily = countryItem.timeSeriesDaily.map((item, i, arr) => {
const prevValues = arr.slice(Math.max(0, i - 6), i);
const valueAverage = (item.value + prevValues.reduce((sum, next) => sum + next.value, 0)) / (1 + prevValues.length);
const deltaAverage = (item.delta + prevValues.reduce((sum, next) => sum + next.delta, 0)) / (1 + prevValues.length);
return {
key: item.key,
value: Math.round(valueAverage),
delta: Math.round(deltaAverage),
};
});
countryItem.deathGrowthRate = getGrowthRate(countryItem); countryItem.deathGrowthRate = getGrowthRate(countryItem);
countryItem.rollingAverageDaily = getRollingAverage(countryItem);
countryItem.doublingDaily = getDoublingTime(countryItem);
return countryItem; return countryItem;
}); });
@ -554,19 +577,10 @@ const processGlobalDeaths = async () => {
}; };
}); });
worldData.rollingAverageDaily = worldData.timeSeriesDaily.map((item, i) => {
const prevValues = worldData.timeSeriesDaily.slice(Math.max(0, i - 6), i);
const valueAverage = (item.value + prevValues.reduce((sum, next) => sum + next.value, 0)) / (1 + prevValues.length);
const deltaAverage = (item.delta + prevValues.reduce((sum, next) => sum + next.delta, 0)) / (1 + prevValues.length);
return {
key: item.key,
value: Math.round(valueAverage),
delta: Math.round(deltaAverage),
};
});
worldData.deathGrowthRate = getGrowthRate(worldData); worldData.deathGrowthRate = getGrowthRate(worldData);
worldData.rollingAverageDaily = getRollingAverage(worldData);
worldData.doublingDaily = getDoublingTime(worldData);
worldData.population = 7781841000; worldData.population = 7781841000;
worldData.deathsPerMillion = worldData.total / worldData.population * 1000000; worldData.deathsPerMillion = worldData.total / worldData.population * 1000000;

View File

@ -22,6 +22,16 @@ html
} }
script. script.
Chart.pluginService.register({
beforeDraw: (chart) => {
const ctx = chart.chart.ctx;
ctx.save();
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
});
const charts = { const charts = {
logarithmic: false, logarithmic: false,
heroChart: null, heroChart: null,
@ -90,7 +100,7 @@ html
axis.type = 'logarithmic'; axis.type = 'logarithmic';
const maxLogPower = Math.ceil(Math.log10(charts.heroMaxValue)); const maxLogPower = Math.ceil(Math.log10(charts.heroMaxValue));
axis.ticks.max = Math.pow(10, maxLogPower); axis.ticks.max = Math.pow(10, maxLogPower);
axis.ticks.callback = value => Number(value.toString()); axis.ticks.callback = value => Number(value.toString()).toLocaleString();
} else { } else {
axis.type = 'linear'; axis.type = 'linear';
delete axis.ticks.max; delete axis.ticks.max;
@ -114,7 +124,7 @@ html
axis.type = 'logarithmic'; axis.type = 'logarithmic';
const maxLogPower = Math.ceil(Math.log10(data.maxValue)); const maxLogPower = Math.ceil(Math.log10(data.maxValue));
axis.ticks.max = Math.pow(10, maxLogPower); axis.ticks.max = Math.pow(10, maxLogPower);
axis.ticks.callback = value => Number(value.toString()); axis.ticks.callback = value => Number(value.toString()).toLocaleString();
} else { } else {
axis.type = 'linear'; axis.type = 'linear';
delete axis.ticks.max; delete axis.ticks.max;
@ -125,9 +135,8 @@ html
}); });
} }
function makeHeroChart(id, title, labels, totalDeaths, newDeaths, rollingAverage) { function makeHeroChart(id, title, labels, totalDeaths, newDeaths, rollingAverage, doubling) {
const canvas = document.getElementById(id); const canvas = document.getElementById(id);
charts.heroMaxValue = totalDeaths[totalDeaths.length - 1]; charts.heroMaxValue = totalDeaths[totalDeaths.length - 1];
charts.heroChart = new Chart(canvas.getContext('2d'), { charts.heroChart = new Chart(canvas.getContext('2d'), {
@ -157,6 +166,14 @@ html
fill: false, fill: false,
borderColor: 'rgb(96, 96, 96, 0.25)', borderColor: 'rgb(96, 96, 96, 0.25)',
borderWidth: 1, borderWidth: 1,
},
{
label: 'Days to 2x',
data: doubling,
fill: false,
borderColor: 'rgb(187,40,193, 0.5)',
borderWidth: 2,
pointRadius: 0,
} }
], ],
}, },
@ -183,7 +200,7 @@ html
beginAtZero: true, beginAtZero: true,
min: 0, min: 0,
max: Math.pow(10, Math.ceil(Math.log10(charts.heroMaxValue))), max: Math.pow(10, Math.ceil(Math.log10(charts.heroMaxValue))),
callback: value => Number(value.toString()) callback: value => Number(value.toString()).toLocaleString(),
}, },
afterBuildTicks: (axis, ticks) => { afterBuildTicks: (axis, ticks) => {
if (axis.type === 'logarithmic') { if (axis.type === 'logarithmic') {
@ -254,6 +271,7 @@ html
!{JSON.stringify(data.timeSeriesDaily.map(x => x.value))}, !{JSON.stringify(data.timeSeriesDaily.map(x => x.value))},
!{JSON.stringify(data.timeSeriesDaily.map(x => x.delta))}, !{JSON.stringify(data.timeSeriesDaily.map(x => x.delta))},
!{JSON.stringify(data.rollingAverageDaily.map(x => x.delta))}, !{JSON.stringify(data.rollingAverageDaily.map(x => x.delta))},
!{JSON.stringify(data.doublingDaily.map(x => x.value))},
); );
mixin dataTable(items, label, type) mixin dataTable(items, label, type)