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