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

Mutation pie chart and multi-selection table #4794

Merged
merged 29 commits into from
Apr 5, 2024

Conversation

7xuanlu
Copy link
Contributor

@7xuanlu 7xuanlu commented Dec 7, 2023

Fix cBioPortal/cbioportal#6759

This is breakdown of cBioPortal/cbioportal#6759. It covers only mutation data as part of this story.
Screenshot 2024-01-19 at 12 57 07 PM

A pie chart and a multi-selection table will be displayed.

The slices for MUTATED pie chart:
Mutated
Wild Type
NA

The data for EVENT table:
Missense_Mutation
...

Screenshot 2024-01-19 at 12 56 46 PM

Backend updates

Try adding new study-page api to get plot data instead of doing it in frontend
Support mutationDataFilter object in StudyViewFilter object
Frontend updates

  • Adding mutation discrete profile to gene specific charts dropdown
  • Main piece - plot visibility and filtering
  • Intersection and union selection on EVENT table
  • Necessary changes to show user selection
  • Disable Download functionality temporarily
  • Showing it in manage Charts
  • Disable group comparison is working for the time being
  • Changes to include it in Virtual study description and Group name

@7xuanlu 7xuanlu added the feature label Dec 7, 2023
@7xuanlu 7xuanlu self-assigned this Dec 7, 2023
@7xuanlu 7xuanlu changed the title Mutation pie chart Mutation pie chart and multi-selection table Dec 7, 2023
@inodb
Copy link
Member

inodb commented Dec 8, 2023

@h164654156465 is this ready for review or in progress? Charts don't seem to work yet:

image

@7xuanlu
Copy link
Contributor Author

7xuanlu commented Dec 8, 2023

@h164654156465 is this ready for review or in progress? Charts don't seem to work yet:

image

Hi @inodb, this frontend PR needs cBioPortal/cbioportal#10395 to be able to work. It is ready for review, thanks.

@inodb
Copy link
Member

inodb commented Dec 14, 2023

@h164654156465 do you have a specific timeline for needing this out or is it ok if we review in a few weeks? We have a lot of backend movement happening with the spring boot refactoring

@7xuanlu
Copy link
Contributor Author

7xuanlu commented Dec 18, 2023

@h164654156465 do you have a specific timeline for needing this out or is it ok if we review in a few weeks? We have a lot of backend movement happening with the spring boot refactoring

Let's review this on early to mid January next year. We're not in a hurry, but we do want to close it out as best as we can. As this PR is closely related to backend revamp. We might need to merge those changes as well. Thank you.

Copy link

netlify bot commented Jan 9, 2024

Deploy Preview for cbioportalfrontend ready!

Name Link
🔨 Latest commit 62abd1e
🔍 Latest deploy log https://app.netlify.com/sites/cbioportalfrontend/deploys/661027ad737aa300086751f4
😎 Deploy Preview https://deploy-preview-4794--cbioportalfrontend.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@kalletlak
Copy link
Member

Need fixes:

  1. Filter names (remove underscores)
  2. Bookmark links not working
  3. Saving charts to user preference

}

@action.bound
updateMutationDataFilters(uniqueKey: string, values: string[][]): void {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we make the name "values" more specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed it to be valueArrays so that we know it's a two dimensional array.

if (values.every(valueArray => valueArray.length === 0)) {
this._mutationDataFilterSet.delete(uniqueKey);
} else {
const dataFilterValues: DataFilterValue[][] = values.map(
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe a comment explaining why this is necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comments added like so:
valueArrays represent a two-dimensional array that supports union and
intersection selection on samples

for the first condition, I also added "delete mutationDataFilter if valueArrays is empty"

...params,
$queryParameters: {
projection:
chartInfo.mutationOptionType ===
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's reason for this? put comment explaining why one case needs DETAILED

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no DETAILED condition in here, code updated

params = {
    ...params,
    $queryParameters: {
        projection: 'SUMMARY'
    },
};

},
] as any,
studyViewFilter: this.filters,
let result = [];
Copy link
Collaborator

Choose a reason for hiding this comment

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

we need to avoid adding a lot of logic/bloat to the store, which already HUGE. Lets put this code inside named helper functions, which will also document it better. we need to explain the basic fork: type==MUTATION_EXTENDED or otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved to util module

@@ -8948,6 +9225,38 @@ export class StudyViewPageStore
} else return '';
}

public async getMutationTypesDownloadData(
Copy link
Collaborator

Choose a reason for hiding this comment

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

move to util file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also updated similar download functions.

@@ -124,18 +177,59 @@ export default class GeneLevelSelection extends React.Component<
return [];
}

private get subOptions() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

no reason to make this a class member. it's a constant really

Copy link
Contributor Author

Choose a reason for hiding this comment

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

extracted to constant under the same file

@7xuanlu 7xuanlu closed this Feb 9, 2024
@7xuanlu 7xuanlu deleted the mutation-pie-chart branch February 9, 2024 17:42
@7xuanlu 7xuanlu restored the mutation-pie-chart branch February 9, 2024 17:57
@7xuanlu 7xuanlu reopened this Feb 9, 2024
this.store.updateScatterPlotFilterByValues(
props.chartMeta!.uniqueKey
);
const props: any = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@7xuanlu can you fix this type? you can see that is used to be set to Partial< > something other

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

@7xuanlu 7xuanlu requested a review from alisman March 7, 2024 21:58
getDisplayedValue?: (value: string) => string,
getDisplayedColor?: (value: string) => string
): ClinicalDataCountSummary[] {
return getClinicalDataCountWithColorByClinicalDataCount(counts).map(
Copy link
Collaborator

Choose a reason for hiding this comment

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

in the name of reducing the bloat of this file we should move this to another file. if you would rather just put a TODO we can follow up with that when do the refactor discussed a few weeks ago. i've actually already started this.

Copy link
Collaborator

@alisman alisman Mar 11, 2024

Choose a reason for hiding this comment

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

additionally, this logic should probably should have unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is not part of this PR although I did try to move it to util file but decided not to. The thing is it's using lots of internal data such as this.chartToUsedColors in StudyViewPageStore. I prefer to delaying it to another PR that address this.

I agree that we should have unit tests once we move it to util file.

} else if (
newChart.mutationOptionType &&
newChart.mutationOptionType ===
MutationOptionConstants.MUTATION_TYPE
Copy link
Collaborator

Choose a reason for hiding this comment

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

correct me if i'm wrong but it seems like we use mutation_type, mutation_event and mutationData terminology interchangeably. is it possibly to make this consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We make mutation_type as the final say both in frontend and backend.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

let me change all occurences in frontend

var words = eventType.split('_');

// Capitalize the first letter of each word
var capitalizedWords = words.map(function(word) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think lodash has _.capitalize that does this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

chartInfo: GenericAssayChart,
filters: StudyViewFilter
) {
let result: GenericAssayDataCountItem[] = [];
Copy link
Collaborator

Choose a reason for hiding this comment

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

why set it to empty array and then overwrite?

const result = await internalClient ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

stableId = data.stableId;
}

return { stableId: stableId, counts: counts };
Copy link
Collaborator

@alisman alisman Mar 11, 2024

Choose a reason for hiding this comment

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

slightly weird that it will return empty string as stableId if no matching data is found. seems like an error case yeah?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made it to return undefined if no data matched

profileName: option.label,
};
}
let options: MolecularProfileOption[] = this.props
Copy link
Collaborator

Choose a reason for hiding this comment

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

try to avoid using let uncessarily.

this will be clearer and more concise

const options = this.props.xxx.result!.map

Copy link
Contributor Author

Choose a reason for hiding this comment

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

np problem modified

@@ -175,7 +265,8 @@ export default class GeneLevelSelection extends React.Component<
<div
style={{
flex: 1,
marginRight: 15,
width: '50%',
Copy link
Collaborator

Choose a reason for hiding this comment

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

concerns me slightly. why would it be 50%?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not comfortable with CSS but I'm aligning molecular profile dropdown with the sub profile dropdown. Feel free to change it if you have better idea.

chartMeta: ChartMeta,
props: Partial<IChartContainerProps>
) => ({
[ChartTypeEnum.PIE_CHART]: () => ({
Copy link
Collaborator

@alisman alisman Mar 11, 2024

Choose a reason for hiding this comment

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

what's reason for putting these inside of callbacks?

Copy link
Contributor Author

@7xuanlu 7xuanlu Mar 11, 2024

Choose a reason for hiding this comment

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

I'm trying to make sure every subTypeProps can be created successfully and used consistently.

Some of the subtype needs callback to enable this.store.getMutationDataFiltersByUniqueKey( chartMeta.uniqueKey ) to evaluate to some values. Otherwise, it would be an undefined/error.

[ChartTypeEnum.MUTATION_EVENT_TYPE_COUNTS_TABLE]: () => ({
    filters: this.store.getMutationDataFiltersByUniqueKey(
        chartMeta.uniqueKey
    ),
    promise: this.store.getMutationEventChartDataCount(chartMeta),
    onValueSelection: this.handlers.onAddMutationDataValues,
    onResetSelection: this.handlers.onSetMutationDataValues,
    id: 'mutation-type-counts-table',
    title: this.store.getChartTitle(
        ChartTypeEnum.MUTATION_EVENT_TYPE_COUNTS_TABLE,
        props.title
    ),
    getData: () =>
        getMutationTypesDownloadData(
            this.store.getMutationEventChartDataCount(chartMeta)
        ),
    downloadTypes: ['Data'],
    onChangeCancerGeneFilter: this.store
        .updateMutatedGenesTableByCancerGenesFilter,
}),

That's why I make [ChartTypeEnum.PIE_CHART] also a callback, that way we can

const subTypeProps = this.chartTypeConfig(chartMeta, props)[
    chartType
]();

@tmazor
Copy link
Contributor

tmazor commented Mar 22, 2024

I haven't have time to fully test this yet, but from what I've done so far, I have some thoughts about colors:

If I add a gene that's on the gene panel for some but not all samples, the mutated-vs-not chart is using the same color for both not mutation & not profiled:
image

And the more I test, it seems like these colors can change - now I have these colors:
image

And I can also get to the point where Mutated is blue:
image

Given that this is essentially yes/no/NA, I think we need to set standard colors for these to help with interpretability. I would suggest grey for 'Not Profiled' (like the color we use for N/A). Do we have default colors for binary data? If not, I'd suggest green for mutated (since that aligns with coloring in OncoPrint).

@7xuanlu
Copy link
Contributor Author

7xuanlu commented Mar 29, 2024

I haven't have time to fully test this yet, but from what I've done so far, I have some thoughts about colors:

If I add a gene that's on the gene panel for some but not all samples, the mutated-vs-not chart is using the same color for both not mutation & not profiled: image

And the more I test, it seems like these colors can change - now I have these colors: image

And I can also get to the point where Mutated is blue: image

Given that this is essentially yes/no/NA, I think we need to set standard colors for these to help with interpretability. I would suggest grey for 'Not Profiled' (like the color we use for N/A). Do we have default colors for binary data? If not, I'd suggest green for mutated (since that aligns with coloring in OncoPrint).

Thank you for the feedback. I think we do have default colors for binary data, let me update it as well as NOT_PROFILED

@alisman alisman merged commit 075074e into cBioPortal:master Apr 5, 2024
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants