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';
// });
const getGrowthRate = (record) => {
const ts = record.timeSeriesDaily;
const len = ts.length;
const getRollingAverage = (item) => {
return item.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),
};
});
};
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) {
return 0;
}
const latest = len - 1;
const earliest = Math.max(len - 14, 0);
const earliest = 0;
const pow = 1 / (latest - earliest + 1);
const hi = ts[latest].value;
const lo = Math.max(ts[earliest].value, 0.5);
const hi = items[latest].value;
const lo = Math.max(items[earliest].value, 0.5);
if (hi === 0 && lo < 1) {
return 0;
}
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
tsUSRecords.forEach((usRecord) => {
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.rollingAverageDaily = getRollingAverage(stateItem);
stateItem.doublingDaily = getDoublingTime(stateItem);
// insert into states array for the country
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.rollingAverageDaily = getRollingAverage(countryItem);
countryItem.doublingDaily = getDoublingTime(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.rollingAverageDaily = getRollingAverage(worldData);
worldData.doublingDaily = getDoublingTime(worldData);
worldData.population = 7781841000;
worldData.deathsPerMillion = worldData.total / worldData.population * 1000000;

View File

@ -22,6 +22,16 @@ html
}
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 = {
logarithmic: false,
heroChart: null,
@ -90,7 +100,7 @@ html
axis.type = 'logarithmic';
const maxLogPower = Math.ceil(Math.log10(charts.heroMaxValue));
axis.ticks.max = Math.pow(10, maxLogPower);
axis.ticks.callback = value => Number(value.toString());
axis.ticks.callback = value => Number(value.toString()).toLocaleString();
} else {
axis.type = 'linear';
delete axis.ticks.max;
@ -114,7 +124,7 @@ html
axis.type = 'logarithmic';
const maxLogPower = Math.ceil(Math.log10(data.maxValue));
axis.ticks.max = Math.pow(10, maxLogPower);
axis.ticks.callback = value => Number(value.toString());
axis.ticks.callback = value => Number(value.toString()).toLocaleString();
} else {
axis.type = 'linear';
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);
charts.heroMaxValue = totalDeaths[totalDeaths.length - 1];
charts.heroChart = new Chart(canvas.getContext('2d'), {
@ -157,6 +166,14 @@ html
fill: false,
borderColor: 'rgb(96, 96, 96, 0.25)',
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,
min: 0,
max: Math.pow(10, Math.ceil(Math.log10(charts.heroMaxValue))),
callback: value => Number(value.toString())
callback: value => Number(value.toString()).toLocaleString(),
},
afterBuildTicks: (axis, ticks) => {
if (axis.type === 'logarithmic') {
@ -254,6 +271,7 @@ html
!{JSON.stringify(data.timeSeriesDaily.map(x => x.value))},
!{JSON.stringify(data.timeSeriesDaily.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)