Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: conditionally enable regression methods based on term type and s… #2586

Merged
merged 2 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 47 additions & 9 deletions client/mass/charts.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,43 @@ class MassCharts {
!d.hide && this.state.currentCohortChartTypes.includes(d.chartType) ? '' : 'none'
)
}

generateRegressionButtonObject(state) {
// the default obj for regression, this obj is suitable for a button that will launch a menu with multiple regression methods
const obj = {
label: 'Regression Analysis',
chartType: 'regression',
clickTo: this.loadChartSpecificMenu
}
/* following detects if the ds has just one method, if so will customize obj{} so that the button directly show the available method name, and click on it to directly launch the ui without showing a menu
alternatively, this logic could be implemented inside makeChartBtnMenu() of the chart itself and eliminate chart-specific logic in here (ideal)
but there lacks a way for makeChartBtnMenu to customize the button label
*/
const lst = getCurrentCohortChartTypes(state)
if (!lst.includes('regression')) return obj // regression is hidden. still return obj to maintain proper charts array and the button will be hidden later
const availableMethods = []
if (lst.includes('linear')) availableMethods.push('linear')
if (lst.includes('logistic')) availableMethods.push('logistic')
if (lst.includes('cox')) availableMethods.push('cox')
if (availableMethods.length > 1) return obj // more than 1 regression methods. do not modify original obj
if (availableMethods.length == 1) {
// has only one method. customized button label and click behavior
const m = availableMethods[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel in our codebase we should refrain from using alphabets like 'm' to assign values. I understand that it's within the scope of that block but it's in several places in the codebase and searching becomes difficult if used in several scripts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

making an improvement for this and backend

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done please check again

obj.label = (m == 'linear' ? 'Linear' : m == 'cox' ? 'Cox' : 'Logistic') + ' Regression'
obj.clickTo = () => {
this.dom.tip.hide()
this.prepPlot({
config: {
chartType: 'regression',
regressionType: m,
independent: []
}
})
}
}
// no method (exception?)
return obj
}
}

export const chartsInit = getCompInit(MassCharts)
Expand Down Expand Up @@ -72,6 +109,8 @@ function getChartTypeList(self, state) {
each char type will generate a button under the nav bar
a dataset can support a subset of these charts

allow some chart button objects to be dynamically generated based on state

design goal is that chart specific logic should not leak into mass UI

design idea is that a button click will trigger a callback to do one of following things
Expand Down Expand Up @@ -114,8 +153,12 @@ function getChartTypeList(self, state) {

.updateActionBySelectedTerms:
optional callback. used for geneExpression and metabolicIntensity "intermediary" chart types which do not correspond to actual chart, but will route to an actual chart (summary/scatter/hierclust) based on number of selected terms. this callback will update the action based on selected terms to do the routing

TODO fixed order of buttons. may allow to customize order
*/
const [logged, site, user] = getProfileLogin() //later on replace with jwt login

const [logged, site, user] = getProfileLogin() // XXX later on replace with jwt login
xzhou82 marked this conversation as resolved.
Show resolved Hide resolved

const buttons = [
////////////////////// PROFILE PLOTS START //////////////////////
{
Expand Down Expand Up @@ -178,25 +221,20 @@ function getChartTypeList(self, state) {
clickTo: self.loadChartSpecificMenu
},
{
// should only show for official dataset, but not custom
label: 'Cumulative Incidence',
chartType: 'cuminc',
clickTo: self.showTree_select1term,
usecase: { target: 'cuminc', detail: 'term' }
},
{
// should only show for official dataset, but not custom
label: 'Survival',
chartType: 'survival',
clickTo: self.showTree_select1term,
usecase: { target: 'survival', detail: 'term' }
},
{
// should only show for official dataset, but not custom
label: 'Regression Analysis',
chartType: 'regression',
clickTo: self.loadChartSpecificMenu
},

self.generateRegressionButtonObject(state), // returns an object like other buttons but tailored by current state

{
label: 'Sample Matrix',
chartType: 'matrix',
Expand Down
3 changes: 2 additions & 1 deletion release.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

Fixes:
- conditionally enable regression methods based on term type and show customized Regression chart button if a single method is available
12 changes: 9 additions & 3 deletions server/src/termdb.server.init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,9 +582,15 @@ const defaultCommonCharts: isSupportedChartCallbacks = {
summary: () => true,
matrix: () => true,
regression: () => true,
linear: () => true,
logistic: () => true,
cox: () => true,
// linear/logistic/cox can be considered "child types" for regression, their availability can be separately determined to be more user friendly
linear: ({ cohortTermTypes }) => {
return cohortTermTypes.numeric > 0 // numeric term present and could be used as linear outcome
},
logistic: () => true, // consider both numeric & categorical can be logistic outcome
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can add 'binary' outcome

cox: ({ cohortTermTypes }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can add example, 'time to event'

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what u mean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can add examples for comments I meant:
I can add them in if you'd like.

linear: ({ cohortTermTypes }) => {
    // Check if there is at least one numeric term present, which can be used as a linear outcome
    // Example: outcome is a continuous variable.
    return cohortTermTypes.numeric > 0;
},
logistic: () => true, 
    // Allow both numeric and categorical terms to be considered as logistic outcomes
    // Example: Predicting whether a patient has a disease (yes/no), where the outcome is a binary variable
},
cox: ({ cohortTermTypes }) => {
    // Ensure there is at least one survival or condition term present, which is required for a Cox outcome
    // Example: Analyzing the time until a patient relapses after treatment, where the outcome is the time until the event occurs
    return (cohortTermTypes.survival || 0) + (cohortTermTypes.condition || 0) > 0;
},

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

survival and condition terms are time-to-event variables and can be used as Cox outcome. these are facts outside of our code thus not really explained

// requires either survival or condition term as cox outcome
return (cohortTermTypes.survival || 0) + (cohortTermTypes.condition || 0) > 0
},
facet: () => true,
survival: ({ cohortTermTypes }) => cohortTermTypes.survival > 0,
cuminc: ({ cohortTermTypes }) => cohortTermTypes.condition > 0,
Expand Down
Loading