|
@@ -9,7 +9,7 @@ import { Cases, Data, DataPoint, CountryCase, CountryCases, CountryDataPoint } f
|
|
|
const regressionStart = 50;
|
|
const regressionStart = 50;
|
|
|
|
|
|
|
|
// Number of days to extrapolate the regression fit on the graph
|
|
// Number of days to extrapolate the regression fit on the graph
|
|
|
-const futureDays = 10;
|
|
|
|
|
|
|
+const futureDays = 3;
|
|
|
|
|
|
|
|
const mean = (values: number[]): number =>
|
|
const mean = (values: number[]): number =>
|
|
|
values.reduce((last, value) => last + value, 0) / values.length;
|
|
values.reduce((last, value) => last + value, 0) / values.length;
|
|
@@ -54,17 +54,17 @@ const withCumulative = (cumulative: boolean) => (cases: WithNumericDate): WithCu
|
|
|
|
|
|
|
|
const logArray = (values: number[]): number[] => values.map(value => Math.log(value));
|
|
const logArray = (values: number[]): number[] => values.map(value => Math.log(value));
|
|
|
|
|
|
|
|
-const withExponentialRegression = (cumulative: boolean) => (
|
|
|
|
|
|
|
+const withExponentialRegression = (cumulative: boolean, regressionBuffer: number) => (
|
|
|
cases: WithCumulative,
|
|
cases: WithCumulative,
|
|
|
): CountryDataPoint[] => {
|
|
): CountryDataPoint[] => {
|
|
|
const startIndex = cases.findIndex(
|
|
const startIndex = cases.findIndex(
|
|
|
({ valueCumulative = 0 }) => valueCumulative >= regressionStart,
|
|
({ valueCumulative = 0 }) => valueCumulative >= regressionStart,
|
|
|
);
|
|
);
|
|
|
- if (startIndex === -1) {
|
|
|
|
|
|
|
+ if (startIndex === -1 || startIndex >= cases.length - regressionBuffer) {
|
|
|
return cases;
|
|
return cases;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const casesToRegress = cases.slice(startIndex);
|
|
|
|
|
|
|
+ const casesToRegress = cases.slice(startIndex, cases.length - regressionBuffer);
|
|
|
|
|
|
|
|
// It's assumed that the input here is ordered by date ascending
|
|
// It's assumed that the input here is ordered by date ascending
|
|
|
const minDate: Date = new Date(casesToRegress[0].date);
|
|
const minDate: Date = new Date(casesToRegress[0].date);
|
|
@@ -103,13 +103,17 @@ const withExponentialRegression = (cumulative: boolean) => (
|
|
|
}));
|
|
}));
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-function processCountryCases(cases: Cases, cumulative: boolean): CountryDataPoint[] {
|
|
|
|
|
|
|
+function processCountryCases(
|
|
|
|
|
+ cases: Cases,
|
|
|
|
|
+ cumulative: boolean,
|
|
|
|
|
+ regressionBuffer: number,
|
|
|
|
|
+): CountryDataPoint[] {
|
|
|
if (!cases.length) {
|
|
if (!cases.length) {
|
|
|
return [];
|
|
return [];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return compose<Cases, WithNumericDate, WithCumulative, CountryDataPoint[]>(
|
|
return compose<Cases, WithNumericDate, WithCumulative, CountryDataPoint[]>(
|
|
|
- withExponentialRegression(cumulative),
|
|
|
|
|
|
|
+ withExponentialRegression(cumulative, regressionBuffer),
|
|
|
withCumulative(cumulative),
|
|
withCumulative(cumulative),
|
|
|
withNumericDate,
|
|
withNumericDate,
|
|
|
)(cases);
|
|
)(cases);
|
|
@@ -159,7 +163,7 @@ function fillData(countryCases: CountryCases): CountryCases {
|
|
|
country,
|
|
country,
|
|
|
dataSource: {
|
|
dataSource: {
|
|
|
...dataSource,
|
|
...dataSource,
|
|
|
- cases: times.reduce((last: Cases, time: number, index: number): Cases => {
|
|
|
|
|
|
|
+ cases: times.reduce((last: Cases, time: number): Cases => {
|
|
|
const matchingCase = dataSource.cases.find(({ date }) => date.getTime() === time);
|
|
const matchingCase = dataSource.cases.find(({ date }) => date.getTime() === time);
|
|
|
if (matchingCase) {
|
|
if (matchingCase) {
|
|
|
return [...last, matchingCase];
|
|
return [...last, matchingCase];
|
|
@@ -178,20 +182,26 @@ function fillData(countryCases: CountryCases): CountryCases {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export function processCases(countryCases: CountryCases, cumulative = true): Data {
|
|
|
|
|
|
|
+export function processCases(
|
|
|
|
|
+ countryCases: CountryCases,
|
|
|
|
|
+ cumulative = true,
|
|
|
|
|
+ regressionBuffer = 0, // Don't count the last X days into the regression fit
|
|
|
|
|
+): Data {
|
|
|
const filledData = fillData(countryCases);
|
|
const filledData = fillData(countryCases);
|
|
|
|
|
|
|
|
const data = filledData.map(({ country, dataSource: { cases } }) =>
|
|
const data = filledData.map(({ country, dataSource: { cases } }) =>
|
|
|
- processCountryCases(cases, cumulative).map(({ date, xValue, value, regression }) => ({
|
|
|
|
|
- date,
|
|
|
|
|
- xValue,
|
|
|
|
|
- value: {
|
|
|
|
|
- [country]: value,
|
|
|
|
|
- },
|
|
|
|
|
- regression: {
|
|
|
|
|
- [country]: regression,
|
|
|
|
|
- },
|
|
|
|
|
- })),
|
|
|
|
|
|
|
+ processCountryCases(cases, cumulative, regressionBuffer).map(
|
|
|
|
|
+ ({ date, xValue, value, regression }) => ({
|
|
|
|
|
+ date,
|
|
|
|
|
+ xValue,
|
|
|
|
|
+ value: {
|
|
|
|
|
+ [country]: value,
|
|
|
|
|
+ },
|
|
|
|
|
+ regression: {
|
|
|
|
|
+ [country]: regression,
|
|
|
|
|
+ },
|
|
|
|
|
+ }),
|
|
|
|
|
+ ),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
return combineData(data);
|
|
return combineData(data);
|