From 395ea4a0cce08bb9bec35fe44c9cc06f4d50992d Mon Sep 17 00:00:00 2001
From: Steve Reis <stevereis93@gmail.com>
Date: Mon, 8 Aug 2022 16:01:30 +0200
Subject: [PATCH] feat(exareme2): T-test one sample integration

---
 .../ttest-onesample.handler.spec.ts           | 56 +++++++++++++++
 .../algorithms/ttest-onesample.handler.ts     | 72 +++++++++++++++++++
 2 files changed, 128 insertions(+)
 create mode 100644 api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.spec.ts
 create mode 100644 api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.ts

diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.spec.ts b/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.spec.ts
new file mode 100644
index 0000000..258f810
--- /dev/null
+++ b/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.spec.ts
@@ -0,0 +1,56 @@
+import { TableResult } from '../../../../models/result/table-result.model';
+import { Experiment } from '../../../../models/experiment/experiment.model';
+import TtestOnesampleHandler from './ttest-onesample.handler';
+
+const data = {
+  n_obs: 1991,
+  t_value: 304.98272738655413,
+  p_value: 0.0,
+  df: 1990.0,
+  mean_diff: 220.17867654445,
+  se_diff: 0.7464781919192859,
+  ci_upper: 221.64263732187715,
+  ci_lower: 218.71471576702288,
+  cohens_d: 6.835017232945105,
+};
+
+const createExperiment = (): Experiment => ({
+  id: 'dummy-id',
+  name: 'Testing purpose',
+  algorithm: {
+    name: TtestOnesampleHandler.ALGO_NAME.toUpperCase(),
+  },
+  datasets: ['desd-synthdata'],
+  domain: 'dementia',
+  variables: ['lefthippocampus'],
+  coVariables: ['righthippocampus', 'leftamygdala'],
+  results: [],
+});
+
+describe('T-Test Paired handler', () => {
+  let tTestOnesampleHandler: TtestOnesampleHandler;
+  let experiment: Experiment;
+
+  beforeEach(() => {
+    tTestOnesampleHandler = new TtestOnesampleHandler();
+    experiment = createExperiment();
+  });
+
+  describe('Handle', () => {
+    it('with standard t-test algo data', () => {
+      tTestOnesampleHandler.handle(experiment, data);
+
+      const table = experiment.results[0] as TableResult;
+
+      expect(experiment.results.length).toBe(1);
+      expect(table.data.length).toBe(9);
+    });
+
+    it('Should be empty with another algo', () => {
+      experiment.algorithm.name = 'dummy_algo';
+      tTestOnesampleHandler.handle(experiment, data);
+
+      expect(experiment.results.length).toBe(0);
+    });
+  });
+});
diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.ts b/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.ts
new file mode 100644
index 0000000..0cb7bb1
--- /dev/null
+++ b/api/src/engine/connectors/exareme/handlers/algorithms/ttest-onesample.handler.ts
@@ -0,0 +1,72 @@
+import { isNumber } from '../../../../../common/utils/shared.utils';
+import { Domain } from '../../../../models/domain.model';
+import { Experiment } from '../../../../models/experiment/experiment.model';
+import {
+  TableResult,
+  TableStyle,
+} from '../../../../models/result/table-result.model';
+import BaseHandler from '../base.handler';
+
+const lookupDict = {
+  t_value: 'T-value',
+  n_obs: 'Number of observations',
+  p: 'P-value',
+  df: 'Degrees of freedom',
+  mean_diff: 'Mean difference',
+  se_diff: 'Standard error of difference',
+  ci_lower: 'Lower 95% confidence interval',
+  ci_upper: 'Upper 95% confidence interval',
+  cohens_d: "Cohen's d",
+};
+const NUMBER_PRECISION = 4;
+const EXCLUDE_PRECISION = ['n_obs', 'ci_lower', 'ci_upper'];
+
+const isNumberPrecision = (value: any, name: string) => {
+  if (!EXCLUDE_PRECISION.includes(name) && isNumber(value))
+    return value.toPrecision(NUMBER_PRECISION);
+
+  return value;
+};
+
+export default class TtestOnesampleHandler extends BaseHandler {
+  public static readonly ALGO_NAME = 'ttest_onesample';
+
+  private canHandle(algoId: string) {
+    return algoId.toLocaleLowerCase() === TtestOnesampleHandler.ALGO_NAME;
+  }
+
+  private getTable(data: any): TableResult {
+    const tableModel: TableResult = {
+      name: 'T-test',
+      tableStyle: TableStyle.NORMAL,
+      headers: ['name', 'value'].map((name) => ({ name, type: 'string' })),
+      data: [
+        'n_obs',
+        't_value',
+        'p',
+        'df',
+        'mean_diff',
+        'se_diff',
+        'ci_lower',
+        'ci_upper',
+        'cohens_d',
+      ].map((name) => [
+        lookupDict[name],
+        isNumberPrecision(data[name], name)
+          ? data[name].toPrecision(NUMBER_PRECISION)
+          : data[name],
+      ]),
+    };
+
+    return tableModel;
+  }
+
+  handle(experiment: Experiment, data: any, domain?: Domain): void {
+    if (!this.canHandle(experiment.algorithm.name))
+      return super.handle(experiment, data, domain);
+
+    const tableModel = this.getTable(data);
+
+    if (tableModel) experiment.results.push(tableModel);
+  }
+}
-- 
GitLab