added doubling time, graph color tweaks
This commit is contained in:
parent
df22e3c79f
commit
8bff273d8b
100
generate.js
100
generate.js
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user