Selaa lähdekoodia

Exponential regression line

Fela Maslen 5 vuotta sitten
vanhempi
commit
9830a89090
6 muutettua tiedostoa jossa 58 lisäystä ja 8 poistoa
  1. 1 0
      package.json
  2. 5 2
      src/components/graph-cases.tsx
  3. 6 2
      src/types.ts
  4. 6 4
      src/utils/get-cases.ts
  5. 35 0
      src/utils/regression.ts
  6. 5 0
      yarn.lock

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "@types/node": "^12.0.0",
     "@types/react": "^16.9.0",
     "@types/react-dom": "^16.9.0",
+    "date-fns": "^2.11.0",
     "react": "^16.13.0",
     "react-dom": "^16.13.0",
     "react-scripts": "3.4.0",

+ 5 - 2
src/components/graph-cases.tsx

@@ -1,8 +1,9 @@
 import React from 'react';
 import { LineChart, XAxis, YAxis, Line, CartesianGrid } from 'recharts';
 
-import { Cases, Country } from '../types';
+import { Cases, Country, Data } from '../types';
 import { getCases } from '../utils/get-cases';
+import { getExponentialRegression } from '../utils/regression';
 
 type Props = {
   country: Country;
@@ -17,15 +18,17 @@ const margin = {
 
 const GraphCases: React.FC<Props> = ({ country }) => {
   const cases = React.useMemo<Cases>(() => getCases(country), [country]);
+  const data = React.useMemo<Data>(() => getExponentialRegression(cases), [cases]);
 
   return (
     <div>
       <h3>Country: {country.toUpperCase()}</h3>
-      <LineChart width={640} height={480} data={cases} margin={margin}>
+      <LineChart width={640} height={480} data={data} margin={margin}>
         <XAxis dataKey="date" label="Date" />
         <YAxis tick />
         <CartesianGrid stroke="#f5f5f5" />
         <Line type="monotone" dataKey="value" stroke="#000" yAxisId={0} />
+        <Line type="monotone" dataKey="regression" stroke="#06c" yAxisId={0} dot={false} />
       </LineChart>
     </div>
   );

+ 6 - 2
src/types.ts

@@ -1,6 +1,10 @@
-export type Cases = {
+export type Case = {
   date: Date;
   value: number;
-}[];
+};
+
+export type Cases = Case[];
+
+export type Data = (Case & { regression: number })[];
 
 export type Country = 'uk';

+ 6 - 4
src/utils/get-cases.ts

@@ -7,10 +7,12 @@ type RawCases = {
 };
 
 const processCases = (rawCases: RawCases): Cases =>
-  rawCases.dailyCases.map(([date, value]) => ({
-    date: new Date(date),
-    value: Number(value),
-  }));
+  rawCases.dailyCases
+    .map(([date, value]) => ({
+      date: new Date(date),
+      value: Number(value),
+    }))
+    .sort(({ date: dateA }, { date: dateB }) => dateA.getTime() - dateB.getTime());
 
 export function getCases(country: Country): Cases {
   switch (country) {

+ 35 - 0
src/utils/regression.ts

@@ -0,0 +1,35 @@
+import differenceInDays from 'date-fns/differenceInDays';
+import { Cases, Data } from '../types';
+
+const mean = (values: number[]): number =>
+  values.reduce((last, value) => last + value, 0) / values.length;
+
+export function getExponentialRegression(cases: Cases): Data {
+  if (!cases.length) {
+    return [];
+  }
+
+  // It's assumed that the input here is ordered by date ascending
+  const minDate = cases[0].date;
+
+  const xSeries = cases.map(({ date }) => 1 + differenceInDays(date, minDate));
+  const ySeries = cases.map(({ value }) => Math.log(value));
+
+  const xBar = mean(xSeries);
+  const yBar = mean(ySeries);
+
+  const covariance =
+    xSeries.reduce((last, value, index) => last + (value - xBar) * (ySeries[index] - yBar), 0) /
+    xSeries.length;
+
+  const xVariance = mean(xSeries.map(value => value ** 2)) - xBar ** 2;
+
+  const slope = covariance / xVariance;
+  const intercept = yBar - slope * xBar;
+
+  return cases.map(({ date, value }, index) => ({
+    date,
+    value,
+    regression: Math.exp(slope * xSeries[index] + intercept),
+  }));
+}

+ 5 - 0
yarn.lock

@@ -3437,6 +3437,11 @@ data-urls@^1.0.0, data-urls@^1.1.0:
     whatwg-mimetype "^2.2.0"
     whatwg-url "^7.0.0"
 
+date-fns@^2.11.0:
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.11.0.tgz#ec2b44977465b9dcb370021d5e6c019b19f36d06"
+  integrity sha512-8P1cDi8ebZyDxUyUprBXwidoEtiQAawYPGvpfb+Dg0G6JrQ+VozwOmm91xYC0vAv1+0VmLehEPb+isg4BGUFfA==
+
 debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"