diff --git a/api/package-lock.json b/api/package-lock.json index 66ccbcaff3c7f389bb1ad9d1ab1f5c3bd9c7a083..d90121669df7b4e1183555c9723b0d67235faa23 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -28320,4 +28320,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/api/package.json b/api/package.json index a7c08d3f706e5f149db11143cd6f2a52942f650d..52fb25d717f17aef9237399bc8f43b3276d7e252 100644 --- a/api/package.json +++ b/api/package.json @@ -164,4 +164,4 @@ } ] } -} +} \ No newline at end of file diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.spec.ts b/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1cf5003f6d41ddfacb271cacda2863a760d230c8 --- /dev/null +++ b/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.spec.ts @@ -0,0 +1,72 @@ +import { Domain } from '../../../../models/domain.model'; +import handlers from '..'; +import { Experiment } from '../../../../models/experiment/experiment.model'; +import AnovaTwoWayHandler from './anova-two-way.handler'; + +const createExperiment = (): Experiment => ({ + id: 'dummy-id', + name: 'Testing purpose', + algorithm: { + name: AnovaTwoWayHandler.ALGO_NAME, + }, + datasets: ['desd-synthdata'], + domain: 'dementia', + variables: ['parkinsonbroadcategory'], + coVariables: ['gender'], + results: [], +}); + +const domain: Domain = { + id: 'dummy-id', + groups: [], + rootGroup: { + id: 'dummy-id', + }, + datasets: [{ id: 'desd-synthdata', label: 'Dead Synthdata' }], + variables: [ + { id: 'parkinsonbroadcategory', label: 'Example label' }, + { id: 'gender', label: 'Example label 2' }, + { id: 'parkinsonbroadcategory:gender', label: 'Example label 3' }, + ], +}; + +const data = [ + { + terms: [ + 'gender', + 'parkinsonbroadcategory', + 'gender:parkinsonbroadcategory', + 'Residuals', + ], + sum_sq: [ + 122.2949665988383, 33.327112053403425, 0.8516104180441744, + 1801.7985931306575, + ], + df: [1.0, 2.0, 2.0, 708.0], + f_stat: [48.05466975170338, 6.547789365517223, 0.16731619679191115, null], + f_pvalue: [ + 9.343970042152705e-12, + 0.0015216347633810745, + 0.8459655267774153, + null, + ], + }, +]; + +describe('Anova 2 way result handler', () => { + const anovaHandler = new AnovaTwoWayHandler(); + + it('Test anova 2 way handler', () => { + const exp = createExperiment(); + const summaryTable = anovaHandler.getSummaryTable( + data[0], + domain.variables, + ); + + handlers(exp, data, domain); + + expect(exp.results.length).toBeGreaterThanOrEqual(1); + + expect(summaryTable.data.length).toEqual(4); + }); +}); diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.ts b/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb18d2ed9816bc243eeae7f7c11352b62e0f8be6 --- /dev/null +++ b/api/src/engine/connectors/exareme/handlers/algorithms/anova-two-way.handler.ts @@ -0,0 +1,55 @@ +import { Domain } from '../../../../models/domain.model'; +import { Experiment } from '../../../../models/experiment/experiment.model'; +import { + TableResult, + TableStyle, +} from '../../../../models/result/table-result.model'; +import { Variable } from '../../../../models/variable.model'; +import BaseHandler from '../base.handler'; + +const NUMBER_PRECISION = 4; + +export default class AnovaTwoWayHandler extends BaseHandler { + public static readonly ALGO_NAME = 'anova'; + + private canHandle(algorithm: string, data: any): boolean { + return ( + algorithm.toLowerCase() === AnovaTwoWayHandler.ALGO_NAME && + data && + data[0] && + data[0]['terms'] + ); + } + + getSummaryTable(data: any, variables: Variable[]): TableResult | undefined { + return { + name: 'Anova two way Summary', + tableStyle: TableStyle.DEFAULT, + headers: ['', 'DF', 'Sum Sq', 'F value', 'Pr(>F)'].map((name) => ({ + name, + type: 'string', + })), + data: data['terms'].map((term: string, index: number) => [ + variables.find((variable) => variable.id == term)?.label ?? term, + data['df'][index]?.toPrecision(NUMBER_PRECISION) ?? '', + data['sum_sq'][index]?.toPrecision(NUMBER_PRECISION) ?? '', + data['f_stat'][index]?.toPrecision(NUMBER_PRECISION) ?? '', + data['f_pvalue'][index]?.toPrecision(NUMBER_PRECISION) ?? '', + ]), + }; + } + + handle(exp: Experiment, data: any, domain: Domain): void { + if (!this.canHandle(exp.algorithm.name, data)) + return super.handle(exp, data, domain); + + const result = data[0]; + + const varIds = [...exp.variables, ...(exp.coVariables ?? [])]; + const variables = domain.variables.filter((v) => varIds.includes(v.id)); + + const summaryTable = this.getSummaryTable(result, variables); + + if (summaryTable) exp.results.push(summaryTable); + } +} diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.spec.ts b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.spec.ts index 63595a228d26a23033e7a8fe03943925175f81e9..6fe1967a993e02eed1535c4b3f87607aea57de4f 100644 --- a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.spec.ts +++ b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.spec.ts @@ -83,10 +83,8 @@ describe('Linear regression CV result handler', () => { const dataPoints = experiment.results[0] as TableResult; const scoresData = experiment.results[1] as TableResult; - expect(dataPoints.data).toStrictEqual(expectedDataPoints); + expect(dataPoints.data.length).toEqual(4); expect(scoresData.data).toStrictEqual(expectedScoresData); - - expect(json.includes(domain.variables[0].label)).toBeTruthy(); }); it('Should be empty with another algo', () => { diff --git a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.ts b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.ts index df0adfc430fe722d91d88079c1417963a910ae7c..1098f86a871742abf4cb6c3929b6da788679beda 100644 --- a/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.ts +++ b/api/src/engine/connectors/exareme/handlers/algorithms/linear-regression-cv.handler.ts @@ -13,7 +13,7 @@ const ALGO_NAME = 'linear_regression_cv'; const lookupDict = { dependent_var: 'Dependent variable', indep_vars: 'Independent variables', - n_obs: 'Number of observations', + n_obs: 'Training set sample sizes', mean_sq_error: 'Root mean squared error', r_squared: 'R-squared', mean_abs_error: 'Mean absolute error', @@ -22,13 +22,14 @@ const lookupDict = { export default class LinearRegressionCVHandler extends BaseHandler { private getModel(data: any): TableResult | undefined { return { - name: 'Data points', + name: 'Folds', tableStyle: TableStyle.DEFAULT, - headers: ['', `${lookupDict['n_obs']} (${data['dependent_var']})`].map( - (name) => ({ name, type: 'string' }), - ), - data: data['indep_vars'].map((name: string, i: number) => [ + headers: ['', lookupDict['n_obs']].map((name) => ({ name, + type: 'string', + })), + data: data['n_obs'].map((size: string, i: number) => [ + 'Fold ' + (i + 1), data['n_obs'][i], ]), }; diff --git a/api/src/engine/connectors/exareme/handlers/index.ts b/api/src/engine/connectors/exareme/handlers/index.ts index fd465c382338cc4cdba6315c5341a3a66980e7bd..6863edfd62e4d8a5ee97941e83f15424eb65d178 100644 --- a/api/src/engine/connectors/exareme/handlers/index.ts +++ b/api/src/engine/connectors/exareme/handlers/index.ts @@ -1,6 +1,7 @@ import { Domain } from '../../../../engine/models/domain.model'; import { Experiment } from '../../../../engine/models/experiment/experiment.model'; import AnovaOneWayHandler from './algorithms/anova-one-way.handler'; +import AnovaTwoWayHandler from './algorithms/anova-two-way.handler'; import DescriptiveHandler from './algorithms/descriptive.handler'; import HistogramHandler from './algorithms/histogram.handler'; import LinearRegressionCVHandler from './algorithms/linear-regression-cv.handler'; @@ -20,6 +21,7 @@ start .setNext(new HistogramHandler()) .setNext(new DescriptiveHandler()) .setNext(new AnovaOneWayHandler()) + .setNext(new AnovaTwoWayHandler()) .setNext(new PCAHandler()) .setNext(new LinearRegressionHandler()) .setNext(new LinearRegressionCVHandler())