From f263738c4b0564a9546e3e9ee399df4603814490 Mon Sep 17 00:00:00 2001 From: Richard Wolfmayr Date: Tue, 19 Sep 2023 19:41:41 +0200 Subject: [PATCH] cleanup errors and todos for cohort_creation_experiments branch --- src/Cohort.ts | 22 +- src/Taskview/tasks/Filter.ts | 6 +- .../visualizations/AVegaVisualization.ts | 14 +- src/Taskview/visualizations/AreaChart.ts | 8 +- src/Taskview/visualizations/GroupedBoxplot.ts | 6 +- src/Taskview/visualizations/Scatterplot.ts | 193 +++++++++--------- src/base/rest.ts | 48 ++--- src/data/Attribute.ts | 1 - 8 files changed, 135 insertions(+), 163 deletions(-) diff --git a/src/Cohort.ts b/src/Cohort.ts index 116b073..fbe2391 100644 --- a/src/Cohort.ts +++ b/src/Cohort.ts @@ -32,7 +32,7 @@ import { dataDBCohortWithEqualsFilter, dataDBCohortWithNumFilter, getCohortData, - getCohortSize, getDBCohortData, recommendSplit, + getCohortSize, sizeDBCohortDepletionScoreFilter, sizeDBCohortGeneWithEqualsFilter, sizeDBCohortGeneWithNumFilter, @@ -133,14 +133,6 @@ function getLegacyRangeFilter(parentFilters: IAllFilters, attribute: string, ran return mergeTwoAllFilters(parentFilters, thisFilter); // merge the existing with the new filter } -// TODO implement correctly, or remove if different solution is better -function getAutoSplitFilter(parentFilters: IAllFilters, attribute: string) { - const thisFilter: IAllFilters = { normal: {}, lt: {}, lte: {}, gt: {}, gte: {} }; - thisFilter['gte'][attribute] = "label1"; // TODO: remove, this is only for now for development of autosplit - thisFilter['lt'][attribute] = "label2"; - return mergeTwoAllFilters(parentFilters, thisFilter); // merge the existing with the new filter -} - export async function createCohortWithEqualsFilter( parentCohort: ICohort, labelOne: string, @@ -268,14 +260,8 @@ export async function createCohortAutoSplit( attribute, }; log.debug('try new cohort num filter: ', params); - // const dbId = await cohortCreationDBHandler(createDBCohortAutomatically, params); // TODO: NOT needed. The ids are already known. - const dbId = newCohortId; - // let dummyrange = [this.getGeneralNumericalFilter(0 , 50 , NumRangeOperators.gte, NumRangeOperators.lte)]; // TODO: remove, just for development of autosplit - - - // const newFilter = getAutoSplitFilter(parentCohort.filters, attribute); - const newFilter = null; // not needed + const dbId = newCohortId; // ATTENTION : database != databaseName const cht = new Cohort( @@ -289,9 +275,7 @@ export async function createCohortAutoSplit( idType: parentCohort.idType, idColumn: parentCohort.idColumn, }, - newFilter, ); - log.debug('new cohort with num filter: ', cht); return cht; } @@ -485,7 +469,7 @@ async function cohortCreationDBHandler( return dbId; } -function combineLabelsForDB(labelOne: string, labelTwo: string): string { +export function combineLabelsForDB(labelOne: string, labelTwo: string): string { return `${labelOne}${valueListDelimiter}${labelTwo}`; } diff --git a/src/Taskview/tasks/Filter.ts b/src/Taskview/tasks/Filter.ts index a56c46b..453676f 100644 --- a/src/Taskview/tasks/Filter.ts +++ b/src/Taskview/tasks/Filter.ts @@ -12,7 +12,7 @@ import { KaplanMeierPlot } from '../visualizations/KaplanMeierPlot'; import { Scatterplot } from '../visualizations/Scatterplot'; import { ATask } from './ATask'; import tippy from "tippy.js"; -import {createDBCohortAutomatically, ICohortMultiAttrDBDataParams, attributesJSON} from "../../base"; +import {createDBCohortAutomatically, ICohortMultiAttrDBDataParams} from "../../base"; import {AutoSplitEvent} from "../../base/events"; export class Filter extends ATask { @@ -149,7 +149,7 @@ export class Filter extends ATask { private async showTsne(attributes: IAttribute[], cohorts: ICohort[]) { this.$visContainer.innerHTML = 'Currently, we only support the visualization of up to two attributes.'; - // this.addControls(); // TODO: why does it not add if I add here, only if I add it in show()? + // this.addControls(); // why does it not add if I add here, only if I add it in show()? this.attributes = attributes; // this.$visContainer.innerHTML += ''; // TODO #647 fix tsne implementation @@ -186,7 +186,7 @@ export class Filter extends ATask { }); } - async createAutomatically(useNumberOfClusters: boolean = false) { + async createAutomatically(useNumberOfClusters = false) { console.log("createAutomatically 3 or more attributes"); console.log("cohorts ", this.cohorts); console.log("attributes ", this.attributes); diff --git a/src/Taskview/visualizations/AVegaVisualization.ts b/src/Taskview/visualizations/AVegaVisualization.ts index 9d5f528..74bb483 100644 --- a/src/Taskview/visualizations/AVegaVisualization.ts +++ b/src/Taskview/visualizations/AVegaVisualization.ts @@ -14,8 +14,8 @@ import { DATA_LABEL } from './constants'; import { IVisualization } from './IVisualization'; import { IAttribute, IdValuePair } from '../../data/IAttribute'; import { - ICohortDBDataParams, ICohortDBDataRecommendSplitParams, - ICohortDBWithNumFilterParams, ICohortMultiAttrDBDataParams, + ICohortDBDataRecommendSplitParams, + ICohortMultiAttrDBDataParams, IEqualsList, INumRange, NumRangeOperators @@ -255,8 +255,8 @@ export abstract class AVegaVisualization implements IVegaVisualization { abstract show(container: HTMLDivElement, attributes: IAttribute[], cohorts: ICohort[]); abstract filter(): void; abstract split(): void; - abstract createAutomatically?(useNumberOfClusters: boolean = false): void; - abstract recommendSplit?(useNumberOfClusters: boolean = false): void; + abstract createAutomatically?(useNumberOfClusters: boolean): void; + abstract recommendSplit?(useNumberOfClusters: boolean): void; abstract showImpl(chart: HTMLDivElement, data: Array); // probably the method impl from SingleAttributeVisualization can be moved here destroy() { @@ -461,7 +461,7 @@ export abstract class SingleAttributeVisualization extends AVegaVisualization { /** Calls the recommendSplit webservice and sets the bins according to the returned results */ - async recommendSplit(useNumberOfClusters: boolean = false) { + async recommendSplit(useNumberOfClusters = false) { console.log("recommendSplit"); let numberOfClusters = 0; @@ -481,7 +481,7 @@ export abstract class SingleAttributeVisualization extends AVegaVisualization { // recommendSplit recommends splits that are used for ALL cohorts, so it would not make sense to use it on multiple cohorts // 1 cohort, 1 category - let filter: INumRange[] | IEqualsList = []; + let filter: INumRange[] = []; filterDesc.push({ cohort: cohorts[0], @@ -665,7 +665,7 @@ export abstract class SingleAttributeVisualization extends AVegaVisualization { } /** Calls the createAutomatically webservice and creates cohorts according to the returned results */ - async createAutomatically(useNumberOfClusters: boolean = false) { + async createAutomatically(useNumberOfClusters = false) { console.log("createAutomatically"); // get the information on HOW MANY new cohorts to create here already // call the webservice that creates the cohorts, then I have the number of cohorts and can create the filters diff --git a/src/Taskview/visualizations/AreaChart.ts b/src/Taskview/visualizations/AreaChart.ts index e602dff..6a711d6 100644 --- a/src/Taskview/visualizations/AreaChart.ts +++ b/src/Taskview/visualizations/AreaChart.ts @@ -17,11 +17,9 @@ export class AreaChart extends MultiAttributeVisualization { super(vegaLiteOptions); } - async createAutomatically(useNumberOfClusters: boolean = false) { + async createAutomatically(useNumberOfClusters = false) { console.log("createAutomatically scatterplot"); - // AttributeType = 'categorical' | 'number' | 'string'; TODO send it with the data - let numberOfClusters = 0; if (useNumberOfClusters) { // select the bins field @@ -145,6 +143,10 @@ export class AreaChart extends MultiAttributeVisualization { log.error('split is not implemented'); } + async recommendSplit(useNumberOfClusters = false) { + log.error('recommendSplit is not implemented'); + } + addControls() { // log.info('no controls for Area Chart yet'); this.controls.insertAdjacentHTML( diff --git a/src/Taskview/visualizations/GroupedBoxplot.ts b/src/Taskview/visualizations/GroupedBoxplot.ts index 0444df8..971c244 100644 --- a/src/Taskview/visualizations/GroupedBoxplot.ts +++ b/src/Taskview/visualizations/GroupedBoxplot.ts @@ -186,6 +186,10 @@ export class GroupedBoxplot extends MultiAttributeVisualization { this.container.dispatchEvent(new FilterEvent(filterDescs)); } + async recommendSplit(useNumberOfClusters = false) { + log.error('recommendSplit is not implemented'); + } + split() { const categories = this.vegaView.data('row_domain').map((row) => row[this.catAttribute.dataKey]); const [minX, maxX] = this.vegaView.scale('x').domain(); @@ -242,7 +246,7 @@ export class GroupedBoxplot extends MultiAttributeVisualization { this.container.dispatchEvent(new SplitEvent(filterDescs)); } - async createAutomatically(useNumberOfClusters: boolean = false) { + async createAutomatically(useNumberOfClusters = false) { console.log("createAutomatically GroupedBoxplot"); let numberOfClusters = 0; diff --git a/src/Taskview/visualizations/Scatterplot.ts b/src/Taskview/visualizations/Scatterplot.ts index 1111092..40eb64f 100644 --- a/src/Taskview/visualizations/Scatterplot.ts +++ b/src/Taskview/visualizations/Scatterplot.ts @@ -880,107 +880,104 @@ export class Scatterplot extends MultiAttributeVisualization { } + // unused, because it does not make sense for more than 1 attribute /** Calls the recommendSplit webservice and sets the bins according to the returned results */ - async recommendSplit(useNumberOfClusters: boolean = false) { - console.log("recommendSplit scatterplot"); - - let numberOfClusters = 0; - if (useNumberOfClusters) { - // select the bins field - // binsCount0 = (this.controls.querySelector(`#split input.bins[data-axis=x]`) as HTMLInputElement).valueAsNumber; - // console.log("binsCountX", binsCount0); - // binsCount1 = (this.controls.querySelector(`#split input.bins[data-axis=y]`) as HTMLInputElement).valueAsNumber; - // console.log("binsCountY", binsCount1); - numberOfClusters = (this.controls.querySelector(`#split #recommendSplitControls input.clusters`) as HTMLInputElement).valueAsNumber; - console.log("useNumberOfClusters", useNumberOfClusters); - } - - const cohorts = this.cohorts; - - let filterDesc: IFilterDesc[] = []; - if (cohorts.length === 1) { - // it does not make sense to do a recommendSplit on multiple cohorts at once - // createAutomatically looks at the data of one cohort and creates new cohorts based on that - // recommendSplit recommends splits that are used for ALL cohorts, so it would not make sense to use it on multiple cohorts - - // 1 cohort, 1 category - let filter: INumRange[] | IEqualsList = []; - - filterDesc.push({ - cohort: cohorts[0], - filter: [ - { - attr: this.attributes[0], - range: filter, - }, - ], - }); - - filterDesc.push({ - cohort: cohorts[0], - filter: [ - { - attr: this.attributes[1], - range: filter, - }, - ], - }); - - const params: ICohortMultiAttrDBDataRecommendSplitParams = { - cohortId: filterDesc[0].cohort.dbId, - attribute0: this.attributes[0].dataKey, - attribute1: this.attributes[1].dataKey, - numberOfClusters: numberOfClusters, - }; - - //TODO show results - const data = await recommendSplitDB(params); - console.log("recommendSplit", data); - - // set the bins for x-axis - let splitValues = []; - for (let i = 0; i < data[this.attributes[0].dataKey].length; i++) { - // get the int val of the data[i] - const binBorder = Number(data[this.attributes[0].dataKey][i]); - splitValues.push(binBorder); - } - // this.vegaView.data(AVegaVisualization.SPLITVALUE_DATA_STORE, cloneDeep(this.splitValues)); // set a defensive copy - // TODO this is different for the scatterplot - this.vegaView.data(`splitvalues_x`, splitValues.slice()); // set a defensive copy - console.log("split values x", splitValues.slice()); - // this.vegaView.runAsync(); // update the view - this.vegaView.runAsync().then( - ( - vegaView, // defer adding signallistener until the new data is set internally - ) => vegaView.addDataListener(`splitvalues_x`, this.vegaSplitListener), // add listener again - ); - - - // set the bins for y-axis - splitValues = []; - for (let i = 0; i < data[this.attributes[1].dataKey].length; i++) { - // get the int val of the data[i] - const binBorder = Number(data[this.attributes[1].dataKey][i]); - splitValues.push(binBorder); - } - // this.vegaView.data(AVegaVisualization.SPLITVALUE_DATA_STORE, cloneDeep(this.splitValues)); // set a defensive copy - // TODO this is different for the scatterplot - this.vegaView.data(`splitvalues_y`, splitValues.slice()); // set a defensive copy - console.log("split values y", splitValues.slice()); - // this.vegaView.runAsync(); // update the view - this.vegaView.runAsync().then( - ( - vegaView, // defer adding signallistener until the new data is set internally - ) => vegaView.addDataListener(`splitvalues_y`, this.vegaSplitListener), // add listener again - ); - } + async recommendSplit(useNumberOfClusters = false) { + console.log("does not make sense for more than 1 attribute"); + // console.log("recommendSplit scatterplot"); + // + // let numberOfClusters = 0; + // if (useNumberOfClusters) { + // // select the bins field + // // binsCount0 = (this.controls.querySelector(`#split input.bins[data-axis=x]`) as HTMLInputElement).valueAsNumber; + // // console.log("binsCountX", binsCount0); + // // binsCount1 = (this.controls.querySelector(`#split input.bins[data-axis=y]`) as HTMLInputElement).valueAsNumber; + // // console.log("binsCountY", binsCount1); + // numberOfClusters = (this.controls.querySelector(`#split #recommendSplitControls input.clusters`) as HTMLInputElement).valueAsNumber; + // console.log("useNumberOfClusters", useNumberOfClusters); + // } + // + // const cohorts = this.cohorts; + // + // let filterDesc: IFilterDesc[] = []; + // if (cohorts.length === 1) { + // // it does not make sense to do a recommendSplit on multiple cohorts at once + // // createAutomatically looks at the data of one cohort and creates new cohorts based on that + // // recommendSplit recommends splits that are used for ALL cohorts, so it would not make sense to use it on multiple cohorts + // + // // 1 cohort, 1 category + // let filter: INumRange[] | IEqualsList = []; + // + // filterDesc.push({ + // cohort: cohorts[0], + // filter: [ + // { + // attr: this.attributes[0], + // range: filter, + // }, + // ], + // }); + // + // filterDesc.push({ + // cohort: cohorts[0], + // filter: [ + // { + // attr: this.attributes[1], + // range: filter, + // }, + // ], + // }); + // + // const params: ICohortMultiAttrDBDataRecommendSplitParams = { + // cohortId: filterDesc[0].cohort.dbId, + // attribute0: this.attributes[0].dataKey, + // attribute1: this.attributes[1].dataKey, + // numberOfClusters: numberOfClusters, + // }; + // + // const data = await recommendSplitDB(params); + // console.log("recommendSplit", data); + // + // // set the bins for x-axis + // let splitValues = []; + // for (let i = 0; i < data[this.attributes[0].dataKey].length; i++) { + // // get the int val of the data[i] + // const binBorder = Number(data[this.attributes[0].dataKey][i]); + // splitValues.push(binBorder); + // } + // + // this.vegaView.data(`splitvalues_x`, splitValues.slice()); // set a defensive copy + // console.log("split values x", splitValues.slice()); + // // this.vegaView.runAsync(); // update the view + // this.vegaView.runAsync().then( + // ( + // vegaView, // defer adding signallistener until the new data is set internally + // ) => vegaView.addDataListener(`splitvalues_x`, this.vegaSplitListener), // add listener again + // ); + // + // + // // set the bins for y-axis + // splitValues = []; + // for (let i = 0; i < data[this.attributes[1].dataKey].length; i++) { + // // get the int val of the data[i] + // const binBorder = Number(data[this.attributes[1].dataKey][i]); + // splitValues.push(binBorder); + // } + // + // this.vegaView.data(`splitvalues_y`, splitValues.slice()); // set a defensive copy + // console.log("split values y", splitValues.slice()); + // // this.vegaView.runAsync(); // update the view + // this.vegaView.runAsync().then( + // ( + // vegaView, // defer adding signallistener until the new data is set internally + // ) => vegaView.addDataListener(`splitvalues_y`, this.vegaSplitListener), // add listener again + // ); + // } } - async createAutomatically(useNumberOfClusters: boolean = false) { + async createAutomatically(useNumberOfClusters = false) { console.log("createAutomatically scatterplot"); - // AttributeType = 'categorical' | 'number' | 'string'; TODO send it with the data - let numberOfClusters = 0; if (useNumberOfClusters) { // select the bins field @@ -1379,6 +1376,10 @@ export class TsneScatterplot extends Scatterplot { this.container.dispatchEvent(new FilterEvent(filterDescs)); } + async recommendSplit(useNumberOfClusters = false) { + console.log("does not make sense for more than 1 attribute"); + } + split() { const filterDescs: IFilterDesc[] = []; for (const cohort of this.cohorts) { diff --git a/src/base/rest.ts b/src/base/rest.ts index d13cda3..d29aa51 100644 --- a/src/base/rest.ts +++ b/src/base/rest.ts @@ -33,6 +33,8 @@ import { mapMutationCat, valueListDelimiter, } from './interfaces'; +import {niceName} from "../utils/labels"; +import {combineLabelsForDB} from "../Cohort"; // maps the database value to a display name function mapDataBaseValueToDisplayName( @@ -149,16 +151,6 @@ export function createDBCohortWithNumFilter(params: ICohortDBWithNumFilterParams return getCohortDataImpl(CohortRoutes.createUseNumFilter, newParams, assignIds); } -// export function createDBCohortAutomatically(params: ICohortDBWithNumFilterParams, assignIds = false): Promise { -// const newParams: IParams = { -// cohortId: params.cohortId, -// name: params.name, -// attribute: params.attribute, -// ranges: convertNumRanges(params.ranges), -// }; -// return getCohortDataImpl(CohortRoutes.createAutomatically, newParams, assignIds); -// } - export function recommendSplitDB(params: ICohortDBDataRecommendSplitParams, assignIds = false): Promise { const url = `/cohortdb/db/${'recommendSplit'}`; const encoded = Ajax.encodeParams(params); @@ -169,38 +161,28 @@ export function recommendSplitDB(params: ICohortDBDataRecommendSplitParams, assi return AppContext.getInstance().getAPIJSON(url, params); } -// TODO: remove this? not used? export function createDBCohortAutomatically(params: ICohortMultiAttrDBDataParams, assignIds = false): Promise { let newParams: IParams = {}; // check if params is ICohortDBDataParams - if("attributes" in params){ - newParams = { - cohortId: params.cohortId, - name: "TODO: create name", - attribute0: params.attribute0, - attribute1: params.attribute1, - attribute0type: params.attribute0type, - attribute1type: params.attribute1type, - numberOfClusters: params.numberOfClusters, - attributes: params.attributes - }; - } else { - newParams = { - cohortId: params.cohortId, - name: "TODO: create name", - attribute0: params.attribute0, - attribute1: params.attribute1, - attribute0type: params.attribute0type, - attribute1type: params.attribute1type, - numberOfClusters: params.numberOfClusters, - }; + + let labelOne = []; + let attributes = JSON.parse(params.attributes); + for (const att of attributes) { + labelOne.push(niceName(att["dataKey"])); } + let label = labelOne.join(', '); + let name = combineLabelsForDB(label, "Mixed - Autosplit"); + newParams = { + cohortId: params.cohortId, + name: name, + numberOfClusters: params.numberOfClusters, + attributes: params.attributes + }; return getCohortDataImpl(CohortRoutes.createAutomatically, newParams, assignIds); } - export function createDBCohortWithGeneNumFilter(params: ICohortDBWithGeneNumFilterParams, assignIds = false): Promise { const newParams: IParams = { cohortId: params.cohortId, diff --git a/src/data/Attribute.ts b/src/data/Attribute.ts index 414af9a..408fd8c 100644 --- a/src/data/Attribute.ts +++ b/src/data/Attribute.ts @@ -110,7 +110,6 @@ export class ServerColumnAttribute extends Attribute { if (autofilter) { if (Array.isArray(filter)) { - const label = rangeLabel || "label"; return createCohortAutoSplit(cht, niceName(this.id), "label", this.id, newCohortId); } }