diff --git a/end-to-end-test/local/screenshots/reference/results_view_comparison_tab_alteration_enrichments_several_groups_element_chrome_1600x1000.png b/end-to-end-test/local/screenshots/reference/results_view_comparison_tab_alteration_enrichments_several_groups_element_chrome_1600x1000.png
index 3b9c6c08bf7..2ad3058c6eb 100644
Binary files a/end-to-end-test/local/screenshots/reference/results_view_comparison_tab_alteration_enrichments_several_groups_element_chrome_1600x1000.png and b/end-to-end-test/local/screenshots/reference/results_view_comparison_tab_alteration_enrichments_several_groups_element_chrome_1600x1000.png differ
diff --git a/packages/cbioportal-ts-api-client/src/index.tsx b/packages/cbioportal-ts-api-client/src/index.tsx
index d31a7c5dc98..3237ca62647 100644
--- a/packages/cbioportal-ts-api-client/src/index.tsx
+++ b/packages/cbioportal-ts-api-client/src/index.tsx
@@ -36,6 +36,9 @@ export {
GenericAssayDataBinFilter,
GenericAssayDataCountFilter,
GenericAssayDataCountItem,
+ GenericAssayCountSummary,
+ GenericAssayBinaryEnrichment,
+ GenericAssayCategoricalEnrichment,
GenericAssayEnrichment,
Geneset,
GenesetCorrelation,
diff --git a/src/pages/groupComparison/GenericAssayEnrichmentCollections.tsx b/src/pages/groupComparison/GenericAssayEnrichmentCollections.tsx
new file mode 100644
index 00000000000..6a003b6a6a9
--- /dev/null
+++ b/src/pages/groupComparison/GenericAssayEnrichmentCollections.tsx
@@ -0,0 +1,263 @@
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import autobind from 'autobind-decorator';
+import { MolecularProfile } from 'cbioportal-ts-api-client';
+import { MakeMobxView } from '../../shared/components/MobxView';
+import Loader from '../../shared/components/loadingIndicator/LoadingIndicator';
+import ErrorMessage from '../../shared/components/ErrorMessage';
+import { MakeEnrichmentsTabUI } from './GroupComparisonUtils';
+import _ from 'lodash';
+import ComparisonStore from '../../shared/lib/comparison/ComparisonStore';
+import GenericAssayBinaryEnrichmentsContainer from 'pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsContainer';
+import EnrichmentsDataSetDropdown from 'pages/resultsView/enrichments/EnrichmentsDataSetDropdown';
+import { makeObservable, observable } from 'mobx';
+import GenericAssayEnrichmentsContainer from 'pages/resultsView/enrichments/GenericAssayEnrichmentsContainer';
+import GenericAssayCategoricalEnrichmentsContainer from 'pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsContainer';
+
+export interface IGenericAssayEnrichmentCollectionsProps {
+ store: ComparisonStore;
+ genericAssayType: string;
+ resultsViewMode?: boolean;
+}
+
+@observer
+export default class GenericAssayEnrichmentCollections extends React.Component<
+ IGenericAssayEnrichmentCollectionsProps,
+ {}
+> {
+ constructor(props: IGenericAssayEnrichmentCollectionsProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ @observable isBinary: boolean | undefined = false;
+ @observable isCategorical: boolean | undefined = false;
+ @observable isNumerical: boolean | undefined = false;
+
+ @autobind
+ private onChangeProfile(profileMap: {
+ [studyId: string]: MolecularProfile;
+ }) {
+ const type = Object.values(profileMap)[0].datatype;
+ this.props.store.setAllGenericAssayEnrichmentProfileMap(
+ profileMap,
+ this.props.genericAssayType
+ );
+ if (type === 'BINARY') {
+ this.isBinary = true;
+ this.isCategorical = false;
+ this.isNumerical = false;
+ } else if (type === 'CATEGORICAL') {
+ this.isBinary = false;
+ this.isCategorical = true;
+ this.isNumerical = false;
+ } else {
+ this.isBinary = false;
+ this.isCategorical = false;
+ this.isNumerical = true;
+ }
+ }
+
+ readonly tabUI = MakeEnrichmentsTabUI(
+ () => this.props.store,
+ () => this.enrichmentsUI,
+ this.props.genericAssayType,
+ true,
+ true,
+ false
+ );
+
+ readonly enrichmentsUI = MakeMobxView({
+ await: () => {
+ const ret: any[] = [
+ this.props.store.gaBinaryEnrichmentDataByAssayType,
+ this.props.store
+ .selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType,
+ this.props.store.gaBinaryEnrichmentGroupsByAssayType,
+ this.props.store.gaCategoricalEnrichmentDataByAssayType,
+ this.props.store.gaCategoricalEnrichmentGroupsByAssayType,
+ this.props.store.gaEnrichmentDataByAssayType,
+ this.props.store.gaEnrichmentGroupsByAssayType,
+ this.props.store.studies,
+ ];
+ if (
+ this.props.store.gaBinaryEnrichmentDataByAssayType.isComplete &&
+ this.props.store.gaBinaryEnrichmentDataByAssayType.result![
+ this.props.genericAssayType
+ ]
+ ) {
+ ret.push(
+ this.props.store.gaBinaryEnrichmentDataByAssayType.result![
+ this.props.genericAssayType
+ ]
+ );
+ }
+
+ if (
+ this.props.store.gaCategoricalEnrichmentDataByAssayType
+ .isComplete &&
+ this.props.store.gaCategoricalEnrichmentDataByAssayType.result![
+ this.props.genericAssayType
+ ]
+ ) {
+ ret.push(
+ this.props.store.gaCategoricalEnrichmentDataByAssayType
+ .result![this.props.genericAssayType]
+ );
+ }
+ if (
+ this.props.store.gaEnrichmentDataByAssayType.isComplete &&
+ this.props.store.gaEnrichmentDataByAssayType.result![
+ this.props.genericAssayType
+ ]
+ ) {
+ ret.push(
+ this.props.store.gaEnrichmentDataByAssayType.result![
+ this.props.genericAssayType
+ ]
+ );
+ }
+ return ret;
+ },
+ render: () => {
+ // since generic assay enrichments tab is enabled only for one study, selectedGenericAssayEnrichmentProfileMap
+ // would contain only one key.
+ const studyId = Object.keys(
+ this.props.store
+ .selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType
+ .result![this.props.genericAssayType]
+ )[0];
+
+ // select the first found profile in the study as the default selection for selected genericAssayType
+ const selectedProfile = this.props.store
+ .selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType
+ .result![this.props.genericAssayType][studyId];
+
+ let profileList: MolecularProfile[] = [];
+ const genericAssayBinaryEnrichmentProfiles = this.props.store
+ .genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType
+ .result![this.props.genericAssayType];
+ const genericAssayCategoricalEnrichmentProfiles = this.props.store
+ .genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType
+ .result![this.props.genericAssayType];
+ const genericAssayEnrichmentProfiles = this.props.store
+ .genericAssayEnrichmentProfilesGroupedByGenericAssayType
+ .result![this.props.genericAssayType];
+
+ if (genericAssayBinaryEnrichmentProfiles !== undefined) {
+ profileList = profileList.concat(
+ genericAssayBinaryEnrichmentProfiles
+ );
+ }
+ if (genericAssayCategoricalEnrichmentProfiles !== undefined) {
+ profileList = profileList.concat(
+ genericAssayCategoricalEnrichmentProfiles
+ );
+ }
+ if (genericAssayEnrichmentProfiles !== undefined) {
+ profileList = profileList.concat(
+ genericAssayEnrichmentProfiles
+ );
+ }
+
+ if (selectedProfile.datatype == 'BINARY') {
+ this.isBinary = true;
+ this.isCategorical = false;
+ this.isNumerical = false;
+ } else if (selectedProfile.datatype == 'CATEGORICAL') {
+ this.isBinary = false;
+ this.isCategorical = true;
+ this.isNumerical = false;
+ } else {
+ this.isBinary = false;
+ this.isCategorical = false;
+ this.isNumerical = true;
+ }
+
+ return (
+
+
+ {this.isBinary && (
+
+ )}
+ {this.isCategorical && (
+
+ )}
+ {this.isNumerical && (
+
+ )}
+
+ );
+ },
+ renderPending: () => (
+
+ ),
+ renderError: () => ,
+ });
+
+ render() {
+ return this.tabUI.component;
+ }
+}
diff --git a/src/pages/groupComparison/GroupComparisonPage.tsx b/src/pages/groupComparison/GroupComparisonPage.tsx
index 47255ad88d1..73abcfdb05e 100644
--- a/src/pages/groupComparison/GroupComparisonPage.tsx
+++ b/src/pages/groupComparison/GroupComparisonPage.tsx
@@ -36,7 +36,6 @@ import styles from './styles.module.scss';
import { OverlapStrategy } from '../../shared/lib/comparison/ComparisonStore';
import { buildCBioPortalPageUrl } from 'shared/api/urls';
import MethylationEnrichments from './MethylationEnrichments';
-import GenericAssayEnrichments from './GenericAssayEnrichments';
import _ from 'lodash';
import AlterationEnrichments from './AlterationEnrichments';
import AlterationEnrichmentTypeSelector from '../../shared/lib/comparison/AlterationEnrichmentTypeSelector';
@@ -46,12 +45,13 @@ import {
buildCustomTabs,
prepareCustomTabConfigurations,
} from 'shared/lib/customTabs/customTabHelpers';
-import { getSortedGenericAssayTabSpecs } from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
+import { getSortedGenericAssayAllTabSpecs } from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
import { HelpWidget } from 'shared/components/HelpWidget/HelpWidget';
import GroupComparisonPathwayMapper from './pathwayMapper/GroupComparisonPathwayMapper';
import GroupComparisonMutationsTab from './GroupComparisonMutationsTab';
import GroupComparisonPathwayMapperUserSelectionStore from './pathwayMapper/GroupComparisonPathwayMapperUserSelectionStore';
import { Tour } from 'tours';
+import GenericAssayEnrichmentCollections from './GenericAssayEnrichmentCollections';
export interface IGroupComparisonPageProps {
routing: any;
@@ -135,6 +135,8 @@ export default class GroupComparisonPage extends React.Component<
this.store.methylationEnrichmentProfiles,
this.store.survivalClinicalDataExists,
this.store.genericAssayEnrichmentProfilesGroupedByGenericAssayType,
+ this.store
+ .genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType,
this.store.alterationsEnrichmentData,
this.store.alterationsEnrichmentAnalysisGroups,
this.store.genesSortedByMutationFrequency,
@@ -313,34 +315,39 @@ export default class GroupComparisonPage extends React.Component<
)}
- {this.store.showGenericAssayTab &&
- getSortedGenericAssayTabSpecs(
+ {(this.store.showGenericAssayCategoricalTab ||
+ this.store.showGenericAssayBinaryTab ||
+ this.store.showGenericAssayTab) &&
+ getSortedGenericAssayAllTabSpecs(
this.store
- .genericAssayEnrichmentProfilesGroupedByGenericAssayType
+ .genericAssayAllEnrichmentProfilesGroupedByGenericAssayType
.result
- ).map(genericAssayTabSpecs => {
+ ).map(genericAssayAllTabSpecs => {
return (
-
);
})}
-
{buildCustomTabs(this.customTabs)}
);
diff --git a/src/pages/groupComparison/GroupComparisonTabs.ts b/src/pages/groupComparison/GroupComparisonTabs.ts
index 6b77ea52f4d..83db87562b4 100644
--- a/src/pages/groupComparison/GroupComparisonTabs.ts
+++ b/src/pages/groupComparison/GroupComparisonTabs.ts
@@ -7,6 +7,8 @@ export enum GroupComparisonTab {
DNAMETHYLATION = 'dna_methylation',
ALTERATIONS = 'alterations',
GENERIC_ASSAY_PREFIX = 'generic_assay',
+ GENERIC_ASSAY_BINARY_PREFIX = 'generic_assay_binary',
+ GENERIC_ASSAY_CATEGORICAL_PREFIX = 'generic_assay_categorical',
MUTATIONS = 'mutations',
PATHWAYS = 'pathways',
}
diff --git a/src/pages/resultsView/ResultsViewPageStoreUtils.ts b/src/pages/resultsView/ResultsViewPageStoreUtils.ts
index d98a7d7aa2c..62fba902882 100644
--- a/src/pages/resultsView/ResultsViewPageStoreUtils.ts
+++ b/src/pages/resultsView/ResultsViewPageStoreUtils.ts
@@ -4,6 +4,8 @@ import {
ClinicalData,
DiscreteCopyNumberData,
GenericAssayEnrichment,
+ GenericAssayBinaryEnrichment,
+ GenericAssayCategoricalEnrichment,
MolecularProfile,
Mutation,
NumericGeneMolecularData,
@@ -749,6 +751,61 @@ export function makeGenericAssayEnrichmentDataPromise(params: {
});
}
+export function makeGenericAssayBinaryEnrichmentDataPromise(params: {
+ resultViewPageStore?: ResultsViewPageStore;
+ await: MobxPromise_await;
+ getSelectedProfileMap: () => { [studyId: string]: MolecularProfile };
+ fetchData: () => Promise;
+}): MobxPromise {
+ return remoteData({
+ await: () => {
+ const ret = params.await();
+ if (params.resultViewPageStore) {
+ ret.push(params.resultViewPageStore.selectedMolecularProfiles);
+ }
+ return ret;
+ },
+ invoke: async () => {
+ const profileMap = params.getSelectedProfileMap();
+ if (profileMap) {
+ let data = await params.fetchData();
+ return calculateQValuesAndSortEnrichmentData(
+ data,
+ sortGenericAssayEnrichmentData
+ );
+ } else {
+ return [];
+ }
+ },
+ });
+}
+
+export function makeGenericAssayCategoricalEnrichmentDataPromise(params: {
+ resultViewPageStore?: ResultsViewPageStore;
+ await: MobxPromise_await;
+ getSelectedProfileMap: () => { [studyId: string]: MolecularProfile };
+ fetchData: () => Promise;
+}): MobxPromise {
+ return remoteData({
+ await: () => {
+ const ret = params.await();
+ if (params.resultViewPageStore) {
+ ret.push(params.resultViewPageStore.selectedMolecularProfiles);
+ }
+ return ret;
+ },
+ invoke: async () => {
+ const profileMap = params.getSelectedProfileMap();
+ if (profileMap) {
+ let data = await params.fetchData();
+ return data;
+ } else {
+ return [];
+ }
+ },
+ });
+}
+
function sortGenericAssayEnrichmentData(
data: GenericAssayEnrichmentWithQ[]
): GenericAssayEnrichmentWithQ[] {
diff --git a/src/pages/resultsView/comparison/ComparisonTab.tsx b/src/pages/resultsView/comparison/ComparisonTab.tsx
index a45794a5f6d..286fb7dbe63 100644
--- a/src/pages/resultsView/comparison/ComparisonTab.tsx
+++ b/src/pages/resultsView/comparison/ComparisonTab.tsx
@@ -30,12 +30,15 @@ import GroupSelector from '../../groupComparison/groupSelector/GroupSelector';
import CaseFilterWarning from '../../../shared/components/banners/CaseFilterWarning';
import MethylationEnrichments from 'pages/groupComparison/MethylationEnrichments';
import AlterationEnrichments from 'pages/groupComparison/AlterationEnrichments';
+import GenericAssayEnrichmentCollections from 'pages/groupComparison/GenericAssayEnrichmentCollections';
import AlterationEnrichmentTypeSelector from 'shared/lib/comparison/AlterationEnrichmentTypeSelector';
-import GenericAssayEnrichments from 'pages/groupComparison/GenericAssayEnrichments';
import styles from 'pages/resultsView/comparison/styles.module.scss';
import { getServerConfig } from 'config/config';
import { AlterationFilterMenuSection } from 'pages/groupComparison/GroupComparisonUtils';
-import { getSortedGenericAssayTabSpecs } from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
+import {
+ getSortedGenericAssayAllTabSpecs,
+ getSortedGenericAssayTabSpecs,
+} from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
export interface IComparisonTabProps {
urlWrapper: ResultsViewURLWrapper;
@@ -263,28 +266,34 @@ export default class ComparisonTab extends React.Component<
/>
)}
- {this.store.showGenericAssayTab &&
- getSortedGenericAssayTabSpecs(
+ {(this.store.showGenericAssayCategoricalTab ||
+ this.store.showGenericAssayBinaryTab ||
+ this.store.showGenericAssayTab) &&
+ getSortedGenericAssayAllTabSpecs(
this.store
- .genericAssayEnrichmentProfilesGroupedByGenericAssayType
+ .genericAssayAllEnrichmentProfilesGroupedByGenericAssayType
.result
- ).map(genericAssayTabSpecs => {
+ ).map(genericAssayAllTabSpecs => {
return (
-
diff --git a/src/pages/resultsView/enrichments/EnrichmentsUtil.tsx b/src/pages/resultsView/enrichments/EnrichmentsUtil.tsx
index 95507aba0d2..64aafcc005a 100644
--- a/src/pages/resultsView/enrichments/EnrichmentsUtil.tsx
+++ b/src/pages/resultsView/enrichments/EnrichmentsUtil.tsx
@@ -2,6 +2,8 @@ import * as React from 'react';
import {
AlterationEnrichment,
GenericAssayEnrichment,
+ GenericAssayBinaryEnrichment,
+ GenericAssayCategoricalEnrichment,
GenomicEnrichment,
MolecularProfile,
} from 'cbioportal-ts-api-client';
@@ -9,6 +11,8 @@ import { AlterationEnrichmentRow } from 'shared/model/AlterationEnrichmentRow';
import {
ExpressionEnrichmentRow,
GenericAssayEnrichmentRow,
+ GenericAssayBinaryEnrichmentRow,
+ GenericAssayCategoricalEnrichmentRow,
} from 'shared/model/EnrichmentRow';
import { formatLogOddsRatio, roundLogRatio } from 'shared/lib/FormatUtils';
import _ from 'lodash';
@@ -33,6 +37,11 @@ import {
GenericAssayEnrichmentTableColumn,
GenericAssayEnrichmentTableColumnType,
} from './GenericAssayEnrichmentsTable';
+import {
+ GenericAssayBinaryEnrichmentTableColumn,
+ GenericAssayBinaryEnrichmentTableColumnType,
+} from './GenericAssayBinaryEnrichmentsTable';
+import { GenericAssayCategoricalEnrichmentTableColumn } from './GenericAssayCategoricalEnrichmentsTable';
export type AlterationEnrichmentWithQ = AlterationEnrichment & {
logRatio?: number;
@@ -73,6 +82,13 @@ export enum GeneOptionLabel {
SYNC_WITH_TABLE = 'Sync with table (up to 100 genes)',
}
+export enum GaBinaryOptionLabel {
+ HIGHEST_FREQUENCY = 'Entities with highest frequency in any group',
+ AVERAGE_FREQUENCY = 'Entities with highest average frequency',
+ SIGNIFICANT_P_VALUE = 'Entities with most significant p-value',
+ SYNC_WITH_TABLE = 'Sync with table (up to 100 entities)',
+}
+
export enum AlterationContainerType {
MUTATION = 'MUTATION',
COPY_NUMBER = 'COPY_NUMBER',
@@ -120,6 +136,14 @@ export function formatPercentage(
);
}
+export function formatGenericAssayPercentage(
+ group: string,
+ data: GenericAssayBinaryEnrichmentRow
+): string {
+ const datum = data.groupsSet[group];
+ return datum.count + ' (' + datum.alteredPercentage.toFixed(2) + '%)';
+}
+
export function getProfiledCount(
group: string,
data: AlterationEnrichmentRow
@@ -134,6 +158,20 @@ export function getAlteredCount(
return data.groupsSet[group].alteredCount;
}
+export function getCount(
+ group: string,
+ data: GenericAssayBinaryEnrichmentRow
+): number {
+ return data.groupsSet[group].count;
+}
+
+export function getTotalCount(
+ group: string,
+ data: GenericAssayBinaryEnrichmentRow
+): number {
+ return data.groupsSet[group].totalCount;
+}
+
function volcanoPlotYCoord(pValue: number) {
if (pValue === 0 || Math.log10(pValue) < -10) {
return 10;
@@ -228,6 +266,45 @@ export function getGenericAssayScatterData(
});
}
+export function getGenericAssayBinaryScatterData(
+ genericAssayBinaryEnrichments: GenericAssayBinaryEnrichmentRow[]
+): any[] {
+ return genericAssayBinaryEnrichments.map(genericAssayBinaryEnrichment => {
+ return {
+ x: roundLogRatio(Number(genericAssayBinaryEnrichment.logRatio), 10),
+ y: volcanoPlotYCoord(genericAssayBinaryEnrichment.pValue),
+ stableId: genericAssayBinaryEnrichment.stableId,
+ entityName: genericAssayBinaryEnrichment.entityName,
+ pValue: genericAssayBinaryEnrichment.pValue,
+ qValue: genericAssayBinaryEnrichment.qValue,
+ logRatio: genericAssayBinaryEnrichment.logRatio,
+ hovered: false,
+ };
+ });
+}
+
+export function getGaBinaryFrequencyScatterData(
+ genericAssayBinaryEnrichments: GenericAssayBinaryEnrichmentRow[],
+ group1: string,
+ group2: string
+): IMiniFrequencyScatterChartData[] {
+ return genericAssayBinaryEnrichments
+ .filter(a => a.pValue !== undefined && a.qValue !== undefined)
+ .map(genericAssayBinaryEnrichment => {
+ return {
+ x:
+ genericAssayBinaryEnrichment.groupsSet[group1]
+ .alteredPercentage,
+ y:
+ genericAssayBinaryEnrichment.groupsSet[group2]
+ .alteredPercentage,
+ pValue: genericAssayBinaryEnrichment.pValue!,
+ qValue: genericAssayBinaryEnrichment.qValue!,
+ hugoGeneSymbol: '',
+ logRatio: genericAssayBinaryEnrichment.logRatio!,
+ };
+ });
+}
export function getAlterationRowData(
alterationEnrichments: AlterationEnrichmentWithQ[],
queryGenes: string[],
@@ -376,11 +453,130 @@ export function getGenericAssayEnrichmentRowData(
});
}
+export function getGenericAssayBinaryEnrichmentRowData(
+ genericAssayBinaryEnrichments: GenericAssayBinaryEnrichment[],
+ groups: { name: string; nameOfEnrichmentDirection?: string }[]
+): GenericAssayBinaryEnrichmentRow[] {
+ return genericAssayBinaryEnrichments.map(genericAssayBinaryEnrichment => {
+ let countsWithAlteredPercentage = _.map(
+ genericAssayBinaryEnrichment.counts,
+ datum => {
+ const alteredPercentage =
+ datum.count > 0 && datum.totalCount > 0
+ ? (datum.count / datum.totalCount) * 100
+ : 0;
+ return {
+ ...datum,
+ alteredPercentage,
+ };
+ }
+ );
+ let enrichedGroup = '';
+ // fallback to stable id if name is not specified
+ let entityName =
+ 'NAME' in genericAssayBinaryEnrichment.genericEntityMetaProperties
+ ? genericAssayBinaryEnrichment.genericEntityMetaProperties[
+ 'NAME'
+ ]
+ : genericAssayBinaryEnrichment.stableId;
+ let logRatio: number | undefined = undefined;
+ let groupsSet = _.keyBy(
+ countsWithAlteredPercentage,
+ group => group.name
+ );
+
+ if (groups.length === 2) {
+ let group1Data = groupsSet[groups[0].name];
+ let group2Data = groupsSet[groups[1].name];
+ if (genericAssayBinaryEnrichment.pValue !== undefined) {
+ logRatio = Math.log2(
+ group1Data.alteredPercentage / group2Data.alteredPercentage
+ );
+ let group1Name =
+ groups[0].nameOfEnrichmentDirection || groups[0].name;
+ let group2Name =
+ groups[1].nameOfEnrichmentDirection || groups[1].name;
+ enrichedGroup = logRatio > 0 ? group1Name : group2Name;
+ }
+ } else if (genericAssayBinaryEnrichment.pValue !== undefined) {
+ countsWithAlteredPercentage.sort(
+ (a, b) => b.alteredPercentage - a.alteredPercentage
+ );
+ enrichedGroup = countsWithAlteredPercentage[0].name;
+ }
+
+ return {
+ checked: false,
+ disabled: false,
+ logRatio,
+ pValue: genericAssayBinaryEnrichment.pValue,
+ qValue: genericAssayBinaryEnrichment.qValue,
+ enrichedGroup,
+ stableId: genericAssayBinaryEnrichment.stableId,
+ entityName,
+ groupsSet,
+ };
+ });
+}
+
+export function getGenericAssayCategoricalEnrichmentRowData(
+ genericAssayCategoricalEnrichments: GenericAssayCategoricalEnrichment[],
+ groups: { name: string; nameOfEnrichmentDirection?: string }[]
+): GenericAssayCategoricalEnrichmentRow[] {
+ return genericAssayCategoricalEnrichments.map(
+ genericAssayCategoricalEnrichment => {
+ let enrichedGroup = '';
+ // fallback to stable id if name is not specified
+ let entityName =
+ 'NAME' in
+ genericAssayCategoricalEnrichment.genericEntityMetaProperties
+ ? genericAssayCategoricalEnrichment
+ .genericEntityMetaProperties['NAME']
+ : genericAssayCategoricalEnrichment.stableId;
+ let logRatio: number | undefined = undefined;
+ let groupsSet = _.keyBy(
+ genericAssayCategoricalEnrichment.groupsStatistics,
+ group => group.name
+ );
+
+ if (groups.length === 2) {
+ let group1Data = groupsSet[groups[0].name];
+ let group2Data = groupsSet[groups[1].name];
+ logRatio =
+ group1Data.meanExpression - group2Data.meanExpression;
+ let group1Name =
+ groups[0].nameOfEnrichmentDirection || groups[0].name;
+ let group2Name =
+ groups[1].nameOfEnrichmentDirection || groups[1].name;
+ enrichedGroup = logRatio > 0 ? group1Name : group2Name;
+ } else {
+ enrichedGroup = genericAssayCategoricalEnrichment.groupsStatistics.sort(
+ (a, b) => b.meanExpression - a.meanExpression
+ )[0].name;
+ }
+
+ return {
+ checked: false,
+ disabled: false,
+ enrichedGroup: enrichedGroup,
+ pValue: genericAssayCategoricalEnrichment.pValue,
+ qValue: genericAssayCategoricalEnrichment.qValue,
+ stableId: genericAssayCategoricalEnrichment.stableId,
+ entityName,
+ attributeType: 'Sample',
+ statisticalTest: 'Chi-squared Test',
+ groupsSet,
+ };
+ }
+ );
+}
export function getFilteredData(
data: (
| ExpressionEnrichmentRow
| AlterationEnrichmentRow
| GenericAssayEnrichmentRow
+ | GenericAssayBinaryEnrichmentRow
+ | GenericAssayCategoricalEnrichmentRow
)[],
expressedGroups: string[],
qValueFilter: boolean,
@@ -426,7 +622,10 @@ export function getFilteredData(
result =
result &&
filterFunction(
- (enrichmentDatum as GenericAssayEnrichmentRow).stableId
+ (enrichmentDatum as
+ | GenericAssayEnrichmentRow
+ | GenericAssayBinaryEnrichmentRow
+ | GenericAssayCategoricalEnrichmentRow).stableId
);
} else {
result =
@@ -442,6 +641,19 @@ export function getFilteredData(
});
}
+export function getFilteredCategoricalData(
+ data: GenericAssayCategoricalEnrichmentRow[],
+ filterFunction: (value: string) => boolean
+): GenericAssayCategoricalEnrichmentRow[] {
+ return data.filter(enrichmentDatum => {
+ let result = false;
+ result = filterFunction(
+ (enrichmentDatum as GenericAssayCategoricalEnrichmentRow).stableId
+ );
+ return result;
+ });
+}
+
export function getBarChartTooltipContent(
tooltipModel: any,
selectedGene: string
@@ -556,6 +768,22 @@ export function pickMethylationEnrichmentProfiles(
});
}
+export function pickAllGenericAssayEnrichmentProfiles(
+ profiles: MolecularProfile[]
+) {
+ // TODO: enable all patient-level profile after confirming patient-level data is compatible with enrichment feature
+ return profiles.filter(p => {
+ return (
+ p.molecularAlterationType ===
+ AlterationTypeConstants.GENERIC_ASSAY &&
+ (((p.datatype === DataTypeConstants.BINARY ||
+ p.datatype === DataTypeConstants.CATEGORICAL) &&
+ p.patientLevel === false) ||
+ p.datatype === DataTypeConstants.LIMITVALUE)
+ );
+ });
+}
+
export function pickGenericAssayEnrichmentProfiles(
profiles: MolecularProfile[]
) {
@@ -569,6 +797,30 @@ export function pickGenericAssayEnrichmentProfiles(
});
}
+export function pickGenericAssayBinaryEnrichmentProfiles(
+ profiles: MolecularProfile[]
+) {
+ return profiles.filter(p => {
+ return (
+ p.molecularAlterationType ===
+ AlterationTypeConstants.GENERIC_ASSAY &&
+ p.datatype === DataTypeConstants.BINARY
+ );
+ });
+}
+
+export function pickGenericAssayCategoricalEnrichmentProfiles(
+ profiles: MolecularProfile[]
+) {
+ return profiles.filter(p => {
+ return (
+ p.molecularAlterationType ===
+ AlterationTypeConstants.GENERIC_ASSAY &&
+ p.datatype === DataTypeConstants.CATEGORICAL &&
+ p.patientLevel === false
+ );
+ });
+}
export function getAlterationEnrichmentColumns(
groups: { name: string; description: string; color?: string }[],
alteredVsUnalteredMode?: boolean
@@ -1002,8 +1254,154 @@ export function getGenericAssayEnrichmentColumns(
return columns;
}
+export function getGenericAssayBinaryEnrichmentColumns(
+ groups: { name: string; description: string; color?: string }[],
+ alteredVsUnalteredMode?: boolean
+): GenericAssayBinaryEnrichmentTableColumn[] {
+ // minimum 2 group are required for enrichment analysis
+ if (groups.length < 2) {
+ return [];
+ }
+ let columns: GenericAssayBinaryEnrichmentTableColumn[] = [];
+ const nameToGroup = _.keyBy(groups, g => g.name);
+
+ let enrichedGroupColum: GenericAssayBinaryEnrichmentTableColumn = {
+ name: alteredVsUnalteredMode
+ ? GenericAssayBinaryEnrichmentTableColumnType.TENDENCY
+ : groups.length === 2
+ ? GenericAssayBinaryEnrichmentTableColumnType.ENRICHED
+ : GenericAssayBinaryEnrichmentTableColumnType.MOST_ENRICHED,
+ render: (d: GenericAssayBinaryEnrichmentRow) => {
+ if (d.pValue === undefined) {
+ return -;
+ }
+ let groupColor = undefined;
+ const significant = d.qValue < 0.05;
+ if (!alteredVsUnalteredMode && significant) {
+ groupColor = nameToGroup[d.enrichedGroup].color;
+ }
+ return (
+
+ {alteredVsUnalteredMode
+ ? d.enrichedGroup
+ : formatAlterationTendency(d.enrichedGroup)}
+
+ );
+ },
+ filter: (
+ d: GenericAssayBinaryEnrichmentRow,
+ filterString: string,
+ filterStringUpper: string
+ ) => (d.enrichedGroup || '').toUpperCase().includes(filterStringUpper),
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) => d.enrichedGroup || '-',
+ download: (d: GenericAssayBinaryEnrichmentRow) =>
+ d.enrichedGroup || '-',
+ tooltip: The group with the highest alteration frequency,
+ };
+
+ if (groups.length === 2) {
+ let group1 = groups[0];
+ let group2 = groups[1];
+ columns.push({
+ name: GenericAssayBinaryEnrichmentTableColumnType.LOG_RATIO,
+ render: (d: GenericAssayBinaryEnrichmentRow) => (
+ {d.logRatio ? formatLogOddsRatio(d.logRatio) : '-'}
+ ),
+ tooltip: (
+
+ Log2 based ratio of (pct in {group1.name}/ pct in{' '}
+ {group2.name})
+
+ ),
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) => Number(d.logRatio),
+ download: (d: GenericAssayBinaryEnrichmentRow) =>
+ d.logRatio ? formatLogOddsRatio(d.logRatio) : '-',
+ });
+
+ enrichedGroupColum.tooltip = (
+
+
+ Log ratio {'>'} 0 |
+ : Enriched in {group1.name} |
+
+
+ Log ratio <= 0 |
+ : Enriched in {group2.name} |
+
+
+ q-Value < 0.05 |
+ : Significant association |
+
+
+ );
+ }
+ columns.push(enrichedGroupColum);
+ groups.forEach(group => {
+ columns.push({
+ name: group.name,
+ headerRender: PERCENTAGE_IN_headerRender,
+ render: (d: GenericAssayBinaryEnrichmentRow) => {
+ let overlay = (
+
+ {getTotalCount(group.name, d)} samples in {group.name}{' '}
+ are profiled for {d.entityName},
+ {formatGenericAssayPercentage(group.name, d)} of which
+ are altered in {d.entityName}
+
+ );
+ return (
+
+
+ {formatGenericAssayPercentage(group.name, d)}
+
+
+ );
+ },
+ tooltip: (
+
+ {group.name}: {group.description}
+
+ ),
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) =>
+ getCount(group.name, d),
+ download: (d: GenericAssayBinaryEnrichmentRow) =>
+ formatGenericAssayPercentage(group.name, d),
+ });
+ });
+ return columns;
+}
+
+export function getGenericAssayCategoricalEnrichmentColumns(
+ groups: { name: string; description: string; color?: string }[],
+ alteredVsUnalteredMode?: boolean
+): GenericAssayCategoricalEnrichmentTableColumn[] {
+ // minimum 2 group are required for enrichment analysis
+ if (groups.length < 2) {
+ return [];
+ }
+ let columns: GenericAssayCategoricalEnrichmentTableColumn[] = [];
+
+ return columns;
+}
export function getEnrichmentBarPlotData(
- data: { [gene: string]: AlterationEnrichmentRow },
+ data: {
+ [gene: string]:
+ | AlterationEnrichmentRow
+ | GenericAssayBinaryEnrichmentRow;
+ },
genes: string[]
): IMultipleCategoryBarPlotData[] {
const usedGenes: { [gene: string]: boolean } = {};
@@ -1063,8 +1461,8 @@ export function getEnrichmentBarPlotData(
}
export function compareByAlterationPercentage(
- kv1: AlterationEnrichmentRow,
- kv2: AlterationEnrichmentRow
+ kv1: AlterationEnrichmentRow | GenericAssayBinaryEnrichmentRow,
+ kv2: AlterationEnrichmentRow | GenericAssayBinaryEnrichmentRow
) {
const t1 = _.reduce(
kv1.groupsSet,
@@ -1179,6 +1577,91 @@ export function getGeneListOptions(
];
}
+export function getGaBinarydataListOptions(
+ data: GenericAssayBinaryEnrichmentRow[],
+ includeAlteration?: boolean
+): { label: GaBinaryOptionLabel; entities: string[] }[] {
+ if (_.isEmpty(data)) {
+ return [
+ {
+ label: GaBinaryOptionLabel.SYNC_WITH_TABLE,
+ entities: [],
+ },
+ ];
+ }
+
+ let dataWithOptionName: (GenericAssayBinaryEnrichmentRow & {
+ optionName?: string;
+ })[] = data;
+
+ let dataSortedByAlteredPercentage = _.clone(dataWithOptionName).sort(
+ compareByAlterationPercentage
+ );
+
+ let dataSortedByAvgFrequency = _.clone(dataWithOptionName).sort(function(
+ dataItem1,
+ dataItem2
+ ) {
+ const averageAlteredPercentage1 =
+ _.sumBy(
+ _.values(dataItem1.groupsSet),
+ group => group.alteredPercentage
+ ) / _.keys(dataItem1.groupsSet).length;
+
+ const averageAlteredPercentage2 =
+ _.sumBy(
+ _.values(dataItem2.groupsSet),
+ group => group.alteredPercentage
+ ) / _.keys(dataItem2.groupsSet).length;
+
+ return averageAlteredPercentage2 - averageAlteredPercentage1;
+ });
+
+ let dataSortedBypValue = _.clone(dataWithOptionName).sort(function(
+ dataItem1,
+ dataItem2
+ ) {
+ if (dataItem1.pValue !== undefined && dataItem2.pValue !== undefined) {
+ return 0;
+ }
+ if (dataItem1.pValue !== undefined) {
+ return 1;
+ }
+ if (dataItem2.pValue !== undefined) {
+ return -1;
+ }
+ return Number(dataItem1.pValue) - Number(dataItem2.pValue);
+ });
+
+ return [
+ {
+ label: GaBinaryOptionLabel.HIGHEST_FREQUENCY,
+ entities: _.map(
+ dataSortedByAlteredPercentage,
+ datum => datum.optionName || datum.entityName
+ ),
+ },
+ {
+ label: GaBinaryOptionLabel.AVERAGE_FREQUENCY,
+ entities: _.map(
+ dataSortedByAvgFrequency,
+ datum => datum.optionName || datum.entityName
+ ),
+ },
+ {
+ label: GaBinaryOptionLabel.SIGNIFICANT_P_VALUE,
+ entities: _.map(
+ dataSortedBypValue,
+ datum => datum.optionName || datum.entityName
+ ),
+ },
+ {
+ label: GaBinaryOptionLabel.SYNC_WITH_TABLE,
+ entities: [],
+ },
+ ];
+}
+
export const ContinousDataPvalueTooltip: React.FunctionComponent = ({
groupSize,
}) => {
diff --git a/src/pages/resultsView/enrichments/GenericAssayBarPlot.tsx b/src/pages/resultsView/enrichments/GenericAssayBarPlot.tsx
new file mode 100644
index 00000000000..121741f0f27
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayBarPlot.tsx
@@ -0,0 +1,518 @@
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, action, computed, makeObservable } from 'mobx';
+import { DownloadControls, DefaultTooltip } from 'cbioportal-frontend-commons';
+import autobind from 'autobind-decorator';
+import MultipleCategoryBarPlot from 'pages/groupComparison/MultipleCategoryBarPlot';
+import ReactSelect from 'react-select';
+import _ from 'lodash';
+import {
+ getEnrichmentBarPlotData,
+ getGaBinarydataListOptions,
+ GaBinaryOptionLabel,
+} from './EnrichmentsUtil';
+import styles from './frequencyPlotStyles.module.scss';
+import { toConditionalPrecision } from 'shared/lib/NumberUtils';
+import { FormControl } from 'react-bootstrap';
+import { GenericAssayBinaryEnrichmentsTableDataStore } from './GenericAssayBinaryEnrichmentsTableDataStore';
+import { GenericAssayBinaryEnrichmentRow } from 'shared/model/EnrichmentRow';
+
+export interface IGenericAssayBarPlotProps {
+ data: GenericAssayBinaryEnrichmentRow[];
+ groupOrder?: string[];
+ isTwoGroupAnalysis?: boolean;
+ yAxisLabel: string;
+ categoryToColor?: {
+ [id: string]: string;
+ };
+ dataStore: GenericAssayBinaryEnrichmentsTableDataStore;
+}
+export declare type SingleEntityQuery = {
+ entity: string;
+};
+const DEFAULT_ENTITIES_COUNT = 10;
+
+const MAXIMUM_ALLOWED_ENTITIES = 100;
+
+const CHART_BAR_WIDTH = 10;
+
+@observer
+export default class GenericAssayBarPlot extends React.Component<
+ IGenericAssayBarPlotProps,
+ {}
+> {
+ @observable tooltipModel: any;
+ @observable.ref _entityQuery: string | undefined = undefined;
+ @observable selectedEntities: SingleEntityQuery[] | undefined;
+ @observable numberOfEntities: Number | undefined = 0;
+ @observable _label: GaBinaryOptionLabel | undefined;
+ @observable isEntitySelectionPopupVisible: boolean | undefined = false;
+ @observable private svgContainer: SVGElement | null;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ @computed get entityListOptions() {
+ return getGaBinarydataListOptions(this.props.data);
+ }
+
+ @computed get defaultOption() {
+ return this.entityListOptions.length > 1
+ ? this.entityListOptions[1]
+ : this.entityListOptions[0];
+ }
+
+ @computed get gaBinaryDataSet() {
+ return _.keyBy(this.props.data, datum => {
+ return datum.entityName;
+ });
+ }
+
+ @computed get barPlotData() {
+ return getEnrichmentBarPlotData(
+ this.gaBinaryDataSet,
+ this.barPlotOrderedEntities
+ );
+ }
+
+ @computed get barPlotOrderedEntities() {
+ let entities: string[] = [];
+ if (this._label === GaBinaryOptionLabel.SYNC_WITH_TABLE) {
+ return this.tableSelectedEntities;
+ }
+ if (!this.selectedEntities) {
+ entities = this.defaultOption.entities.slice(
+ 0,
+ DEFAULT_ENTITIES_COUNT
+ );
+ } else {
+ entities = this.selectedEntities
+ .slice(0, Number(this.numberOfEntities))
+ .map(entityWithAlteration => entityWithAlteration.entity);
+ }
+ return entities;
+ }
+
+ @computed get horzCategoryOrder() {
+ //include significant entities
+ return _.flatMap(this.barPlotOrderedEntities, entity => [
+ entity + '*',
+ entity,
+ ]);
+ }
+
+ @autobind
+ private getTooltip(datum: any) {
+ let entityName = datum.majorCategory as string;
+ // get rid of a trailing *
+ entityName = entityName.replace(/\*$/, '');
+ let entityData = this.gaBinaryDataSet[entityName];
+ //use groupOrder inorder of sorted groups
+ let groupRows = _.map(this.props.groupOrder, groupName => {
+ const group = entityData.groupsSet[groupName];
+ let style: any = {};
+ //bold row corresponding to highlighed bar
+ if (datum.minorCategory === group.name) {
+ style = { fontWeight: 'bold' };
+ }
+ return (
+
+ {group.name} |
+
+ {group.alteredPercentage.toFixed(2)}% ({group.count}/
+ {group.totalCount})
+ |
+
+ );
+ });
+
+ return (
+
+
+ {entityName} {this.props.yAxisLabel}
+
+
+
+
+
+ Group |
+ Percentage Altered |
+
+
+ {groupRows}
+
+
p-Value:{' '}
+ {entityData.pValue
+ ? toConditionalPrecision(entityData.pValue, 3, 0.01)
+ : '-'}
+
+
q-Value:{' '}
+ {entityData.qValue
+ ? toConditionalPrecision(entityData.qValue, 3, 0.01)
+ : '-'}
+
+ );
+ }
+
+ @computed private get selectedOption() {
+ if (this._label && this._entityQuery !== undefined) {
+ return {
+ label: this._label,
+ value: this._entityQuery,
+ };
+ }
+ //default option
+ return {
+ label: this.defaultOption.label,
+ value: this.defaultOption.entities
+ .slice(0, DEFAULT_ENTITIES_COUNT)
+ .join('\n'),
+ };
+ }
+
+ @computed get toolbar() {
+ return (
+
+
+ {this.selectedOption.label}
+
+
+
+
{
+ this.isEntitySelectionPopupVisible = visible;
+ }}
+ overlay={
+ {
+ this._entityQuery = value;
+ this.selectedEntities = entities;
+ this._label = label;
+ this.numberOfEntities = number;
+ this.isEntitySelectionPopupVisible = false;
+ }}
+ defaultNumberOfEntities={
+ DEFAULT_ENTITIES_COUNT
+ }
+ />
+ }
+ placement="bottomLeft"
+ >
+
+
+
+
+
this.svgContainer}
+ filename={'GroupComparisonGeneFrequencyPlot'}
+ dontFade={true}
+ type="button"
+ />
+
+
+
+ );
+ }
+
+ @computed private get tableSelectedEntities() {
+ if (this.props.dataStore.visibleData !== null) {
+ return this.props.dataStore.visibleData
+ .map(x => x.entityName)
+ .slice(0, MAXIMUM_ALLOWED_ENTITIES);
+ }
+ return [];
+ }
+
+ public render() {
+ return (
+
+ {this.toolbar}
+
+ (this.svgContainer = ref)}
+ />
+
+
+ );
+ }
+}
+
+interface IEntitySelectionProps {
+ tableData: SingleEntityQuery[];
+ options: { label: GaBinaryOptionLabel; entities: string[] }[];
+ selectedOption?: { label: GaBinaryOptionLabel; value: string };
+ onSelectedEntitiesChange: (
+ value: string,
+ orderedEntities: SingleEntityQuery[],
+ numberOfEntities: Number,
+ label: GaBinaryOptionLabel
+ ) => void;
+ defaultNumberOfEntities: number;
+ maxNumberOfEntities?: number;
+}
+
+@observer
+export class EntitySelection extends React.Component<
+ IEntitySelectionProps,
+ {}
+> {
+ static defaultProps: Partial = {
+ maxNumberOfEntities: MAXIMUM_ALLOWED_ENTITIES,
+ };
+
+ constructor(props: IEntitySelectionProps) {
+ super(props);
+ makeObservable(this);
+ (window as any).entitySelection = this;
+ }
+
+ @observable.ref _entityQuery: string | undefined = undefined;
+ @observable selectedEntitiesHasError = false;
+ @observable private numberOfEntities = this.props.defaultNumberOfEntities;
+ @observable private _selectedEntityListOption:
+ | {
+ label: GaBinaryOptionLabel;
+ value: string;
+ entities: string[];
+ }
+ | undefined;
+ @observable.ref entitiesToPlot: SingleEntityQuery[] = [];
+
+ @computed get entityListOptions() {
+ return _.map(this.props.options, option => {
+ return {
+ label: option.label,
+ value: option.entities.join('\n'),
+ };
+ });
+ }
+
+ @computed get entityOptionSet() {
+ return _.keyBy(this.props.options, option => option.label);
+ }
+
+ @computed get selectedEntityListOption() {
+ if (
+ this._selectedEntityListOption === undefined &&
+ this.props.selectedOption
+ ) {
+ const selectedOption = this.props.selectedOption;
+ return this.entityListOptions.find(opt =>
+ opt.value.startsWith(selectedOption.value)
+ );
+ }
+ return this._selectedEntityListOption;
+ }
+
+ @computed get entityQuery() {
+ return this._entityQuery === undefined && this.props.selectedOption
+ ? this.props.selectedOption.value
+ : this._entityQuery || '';
+ }
+
+ @computed get hasUnsupportedOQL() {
+ return false;
+ }
+
+ @computed get addGenesButtonDisabled() {
+ if (this.inSyncMode) {
+ return (
+ this.props.selectedOption !== undefined &&
+ this.props.selectedOption.label ===
+ GaBinaryOptionLabel.SYNC_WITH_TABLE
+ );
+ } else {
+ return (
+ this.hasUnsupportedOQL ||
+ (this.props.selectedOption &&
+ this.props.selectedOption.value === this._entityQuery) ||
+ this.selectedEntitiesHasError ||
+ _.isEmpty(this._entityQuery)
+ );
+ }
+ }
+
+ @action.bound
+ public onEntityListOptionChange(option: any) {
+ this._selectedEntityListOption = option;
+ if (option.value !== '') {
+ const entities = this.entityOptionSet[option.label].entities;
+ this._entityQuery = entities
+ .slice(0, this.numberOfEntities)
+ .join('\n');
+ } else {
+ this._entityQuery = '';
+ }
+ this.updateEntityQuery();
+ }
+
+ @computed private get inSyncMode() {
+ return (
+ this._selectedEntityListOption &&
+ this._selectedEntityListOption.label ===
+ GaBinaryOptionLabel.SYNC_WITH_TABLE
+ );
+ }
+
+ @action.bound
+ private handleTotalInputChange(e: any) {
+ const newCount: number = e.target.value.replace(/[^0-9]/g, '');
+ if (newCount <= this.props.maxNumberOfEntities!) {
+ this.numberOfEntities = newCount;
+ }
+ }
+
+ @action.bound
+ private handleTotalInputKeyPress(target: any) {
+ if (target.charCode === 13) {
+ if (isNaN(this.numberOfEntities)) {
+ this.numberOfEntities = 0;
+ return;
+ }
+ this.updateEntityQuery();
+ }
+ }
+
+ @action.bound
+ private onBlur() {
+ if (isNaN(this.numberOfEntities)) {
+ this.numberOfEntities = 0;
+ return;
+ }
+ this.updateEntityQuery();
+ }
+
+ @action.bound
+ private updateEntityQuery() {
+ //removes leading 0s
+ this.numberOfEntities = Number(this.numberOfEntities);
+ if (this.selectedEntityListOption) {
+ const label = this.selectedEntityListOption.label;
+ const entities = this.entityOptionSet[label].entities;
+ if (entities.length > 0) {
+ this._entityQuery = entities
+ .slice(0, this.numberOfEntities)
+ .join('\n');
+ const tmp: SingleEntityQuery[] = [];
+ for (const s of entities.slice(0, this.numberOfEntities)) {
+ tmp.push({
+ entity: s,
+ });
+ }
+ this.entitiesToPlot = tmp;
+ }
+ }
+ }
+
+ public render() {
+ return (
+
+ {this.props.options.length > 0 && (
+
+
+
+ )}
+ {!this.inSyncMode && (
+
+
+
+
+
+
+
+ )}
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsContainer.tsx b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsContainer.tsx
new file mode 100644
index 00000000000..9497271a7f5
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsContainer.tsx
@@ -0,0 +1,528 @@
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, computed, action, makeObservable } from 'mobx';
+import styles from './styles.module.scss';
+import {
+ MolecularProfile,
+ Sample,
+ GenericAssayBinaryEnrichment,
+} from 'cbioportal-ts-api-client';
+import {
+ getGenericAssayBinaryEnrichmentRowData,
+ getGenericAssayBinaryEnrichmentColumns,
+ getGenericAssayBinaryScatterData,
+ getFilteredData,
+ getGaBinaryFrequencyScatterData,
+} from 'pages/resultsView/enrichments/EnrichmentsUtil';
+import _ from 'lodash';
+import autobind from 'autobind-decorator';
+import { MiniOncoprint } from 'shared/components/miniOncoprint/MiniOncoprint';
+import {
+ CheckedSelect,
+ Option,
+ DefaultTooltip,
+} from 'cbioportal-frontend-commons';
+import GenericAssayBinaryEnrichmentsTable, {
+ GenericAssayBinaryEnrichmentTableColumnType,
+} from './GenericAssayBinaryEnrichmentsTable';
+import { GenericAssayBinaryEnrichmentsTableDataStore } from './GenericAssayBinaryEnrichmentsTableDataStore';
+import { GenericAssayBinaryEnrichmentRow } from 'shared/model/EnrichmentRow';
+import { EnrichmentAnalysisComparisonGroup } from 'pages/groupComparison/GroupComparisonUtils';
+import GenericAssayMiniScatterChart from './GenericAssayMiniScatterChart';
+import MiniFrequencyScatterChart from './MiniFrequencyScatterChart';
+import WindowStore from 'shared/components/window/WindowStore';
+import GenericAssayBarPlot from './GenericAssayBarPlot';
+
+export interface IGenericAssayBinaryEnrichmentsContainerProps {
+ data: GenericAssayBinaryEnrichment[];
+ selectedProfile: MolecularProfile;
+ groups: EnrichmentAnalysisComparisonGroup[];
+ sampleKeyToSample: {
+ [uniqueSampleKey: string]: Sample;
+ };
+ genericAssayType: string;
+ alteredVsUnalteredMode?: boolean;
+ patientLevelEnrichments: boolean;
+ onSetPatientLevelEnrichments: (patientLevel: boolean) => void;
+}
+
+@observer
+export default class GenericAssayBinaryEnrichmentsContainer extends React.Component<
+ IGenericAssayBinaryEnrichmentsContainerProps,
+ {}
+> {
+ constructor(props: IGenericAssayBinaryEnrichmentsContainerProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ static defaultProps: Partial<
+ IGenericAssayBinaryEnrichmentsContainerProps
+ > = {
+ alteredVsUnalteredMode: true,
+ };
+
+ @observable significanceFilter: boolean = false;
+ @observable.ref clickedEntityStableId: string;
+ @observable.ref selectedStableIds: string[] | null;
+ @observable.ref highlightedRow: GenericAssayBinaryEnrichmentRow | undefined;
+ @observable.ref _enrichedGroups: string[] = this.props.groups.map(
+ group => group.name
+ );
+
+ @computed get data(): GenericAssayBinaryEnrichmentRow[] {
+ return getGenericAssayBinaryEnrichmentRowData(
+ this.props.data,
+ this.props.groups
+ );
+ }
+
+ @computed get filteredData(): GenericAssayBinaryEnrichmentRow[] {
+ return getFilteredData(
+ this.data,
+ this._enrichedGroups,
+ this.significanceFilter,
+ this.filterByStableId,
+ true
+ );
+ }
+
+ @autobind
+ private filterByStableId(stableId: string) {
+ if (this.selectedStableIds) {
+ return this.selectedStableIds.includes(stableId);
+ } else {
+ // no need to filter the data since there is no selection
+ return true;
+ }
+ }
+
+ @autobind
+ private toggleSignificanceFilter() {
+ this.significanceFilter = !this.significanceFilter;
+ }
+
+ @autobind
+ private onEntityClick(stableId: string) {
+ this.clickedEntityStableId = stableId;
+ }
+
+ @autobind
+ private onSelection(stableIds: string[]) {
+ this.selectedStableIds = stableIds;
+ }
+
+ @autobind
+ private onSelectionCleared() {
+ this.selectedStableIds = null;
+ }
+
+ private dataStore = new GenericAssayBinaryEnrichmentsTableDataStore(
+ () => {
+ return this.filteredData;
+ },
+ () => {
+ return this.highlightedRow;
+ },
+ (c: GenericAssayBinaryEnrichmentRow) => {
+ this.highlightedRow = c;
+ }
+ );
+
+ //used in 2 groups analysis
+ @computed get group1() {
+ return this.props.groups[0];
+ }
+
+ //used in 2 groups analysis
+ @computed get group2() {
+ return this.props.groups[1];
+ }
+
+ @computed get group1QueriedCasesCount() {
+ let caseIds: Set = new Set(
+ this.group1.samples.map(sample =>
+ this.props.patientLevelEnrichments
+ ? sample.uniquePatientKey
+ : sample.uniqueSampleKey
+ )
+ );
+ return caseIds.size;
+ }
+
+ @computed get group2QueriedCasesCount() {
+ let caseIds: Set = new Set(
+ this.group2.samples.map(sample =>
+ this.props.patientLevelEnrichments
+ ? sample.uniquePatientKey
+ : sample.uniqueSampleKey
+ )
+ );
+ return caseIds.size;
+ }
+
+ @computed get selectedEntitiesSet() {
+ return _.keyBy(this.selectedStableIds || []);
+ }
+
+ @computed get isTwoGroupAnalysis(): boolean {
+ return this.props.groups.length == 2;
+ }
+
+ @computed get customColumns() {
+ const cols = getGenericAssayBinaryEnrichmentColumns(
+ this.props.groups,
+ this.props.alteredVsUnalteredMode
+ );
+ if (this.isTwoGroupAnalysis) {
+ cols.push({
+ name: 'Alteration Overlap',
+ headerRender: () => Co-occurrence Pattern,
+ render: data => {
+ if (data.pValue === undefined) {
+ return -;
+ }
+ const groups = _.map(data.groupsSet);
+ // we want to order groups according to order in prop.groups
+ const group1 = groups.find(
+ group => group.name === this.props.groups[0].name
+ )!;
+ const group2 = groups.find(
+ group => group.name === this.props.groups[1].name
+ )!;
+
+ if (!group1 || !group2) {
+ throw 'No matching groups in Alteration Overlap Cell';
+ }
+
+ const totalQueriedCases =
+ this.group1QueriedCasesCount +
+ this.group2QueriedCasesCount;
+ const group1Width =
+ (this.group1QueriedCasesCount / totalQueriedCases) *
+ 100;
+ const group2Width = 100 - group1Width;
+ const group1Unprofiled =
+ ((this.group1QueriedCasesCount - group1.totalCount) /
+ totalQueriedCases) *
+ 100;
+ const group1Unaltered =
+ ((group1.totalCount - group1.count) /
+ totalQueriedCases) *
+ 100;
+ const group2Unprofiled =
+ ((this.group2QueriedCasesCount - group2.totalCount) /
+ totalQueriedCases) *
+ 100;
+ const group1Altered =
+ (group1.count / totalQueriedCases) * 100;
+ const group2Altered =
+ (group2.count / totalQueriedCases) * 100;
+
+ const alterationLanguage = 'alterations';
+
+ const overlay = () => {
+ return (
+
+
+ {data.entityName} {alterationLanguage} in:
+
+
+
+
+
+ {group1.name}:
+ |
+
+ {group1.count} of{' '}
+ {group1.totalCount} of profiled{' '}
+ {this.props
+ .patientLevelEnrichments
+ ? 'patients'
+ : 'samples'}{' '}
+ (
+ {numeral(
+ group1.alteredPercentage
+ ).format('0.0')}
+ %)
+ |
+
+
+
+ {group2.name}:
+ |
+
+ {group2.count} of{' '}
+ {group2.totalCount} of profiled{' '}
+ {this.props
+ .patientLevelEnrichments
+ ? 'patients'
+ : 'samples'}{' '}
+ (
+ {numeral(
+ group2.alteredPercentage
+ ).format('0.0')}
+ %)
+ |
+
+
+
+
+ );
+ };
+
+ return (
+
+
+
+
+
+ );
+ },
+ tooltip: (
+
+
+ Upper row |
+
+ :{' '}
+ {this.props.patientLevelEnrichments
+ ? 'Patients'
+ : 'Samples'}{' '}
+ colored according to group.
+ |
+
+
+ Lower row |
+
+ :{' '}
+ {this.props.patientLevelEnrichments
+ ? 'Patients'
+ : 'Samples'}{' '}
+ with an alteration in the listed gene are
+ highlighted.
+ |
+
+
+ ),
+ });
+ }
+
+ return cols;
+ }
+
+ @computed private get entityPlotMaxWidth() {
+ //820 include width of two scatter plots
+ return WindowStore.size.width - (this.isTwoGroupAnalysis ? 820 : 40);
+ }
+
+ @computed private get gaBarplotYAxislabel() {
+ return this.props.selectedProfile.name + ' altered ratio';
+ }
+
+ @computed private get categoryToColor() {
+ return _.reduce(
+ this.props.groups,
+ (acc, next) => {
+ if (next.color) {
+ acc[next.name] = next.color;
+ }
+ return acc;
+ },
+ {} as { [id: string]: string }
+ );
+ }
+ @computed get visibleOrderedColumnNames() {
+ const columns = [];
+ columns.push(GenericAssayBinaryEnrichmentTableColumnType.ENTITY_ID);
+
+ this.props.groups.forEach(group => {
+ columns.push(group.name);
+ });
+
+ if (this.isTwoGroupAnalysis) {
+ columns.push('Alteration Overlap');
+ columns.push(GenericAssayBinaryEnrichmentTableColumnType.LOG_RATIO);
+ }
+
+ columns.push(
+ GenericAssayBinaryEnrichmentTableColumnType.P_VALUE,
+ GenericAssayBinaryEnrichmentTableColumnType.Q_VALUE
+ );
+
+ if (this.isTwoGroupAnalysis) {
+ columns.push(
+ this.props.alteredVsUnalteredMode
+ ? GenericAssayBinaryEnrichmentTableColumnType.TENDENCY
+ : GenericAssayBinaryEnrichmentTableColumnType.ENRICHED
+ );
+ } else {
+ columns.push(
+ GenericAssayBinaryEnrichmentTableColumnType.MOST_ENRICHED
+ );
+ }
+ return columns;
+ }
+
+ @action.bound
+ onChange(values: { value: string }[]) {
+ this._enrichedGroups = _.map(values, datum => datum.value);
+ }
+
+ @computed get selectedValues() {
+ return this._enrichedGroups.map(id => ({ value: id }));
+ }
+
+ @computed get options(): Option[] {
+ return _.map(this.props.groups, group => {
+ return {
+ label: group.nameOfEnrichmentDirection
+ ? group.nameOfEnrichmentDirection
+ : group.name,
+ value: group.name,
+ };
+ });
+ }
+
+ @computed get selectedRow() {
+ if (this.clickedEntityStableId) {
+ return this.props.data.filter(
+ d => d.stableId === this.clickedEntityStableId
+ )[0];
+ }
+ return undefined;
+ }
+
+ public render() {
+ if (this.props.data.length === 0) {
+ return (
+
+ No data/result available
+
+ );
+ }
+
+ const data: any[] = getGenericAssayBinaryScatterData(this.data);
+ const maxData: any = _.maxBy(data, d => {
+ return Math.ceil(Math.abs(d.x));
+ });
+
+ return (
+
+
+ {this.isTwoGroupAnalysis && (
+
+ )}
+ {this.isTwoGroupAnalysis && (
+
+ )}
+
+ group.name
+ )}
+ yAxisLabel={this.gaBarplotYAxislabel}
+ categoryToColor={this.categoryToColor}
+ dataStore={this.dataStore}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
column.name
+ )}
+ genericAssayType={this.props.genericAssayType}
+ groupSize={this.props.groups.length}
+ />
+
+
+ );
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTable.tsx b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTable.tsx
new file mode 100644
index 00000000000..a29980eab2d
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTable.tsx
@@ -0,0 +1,164 @@
+import * as React from 'react';
+import _ from 'lodash';
+import LazyMobXTable, {
+ Column,
+} from '../../../shared/components/lazyMobXTable/LazyMobXTable';
+import { observer } from 'mobx-react';
+import { computed, makeObservable } from 'mobx';
+import { formatSignificanceValueWithStyle } from 'shared/lib/FormatUtils';
+import { toConditionalPrecision } from 'shared/lib/NumberUtils';
+import styles from './styles.module.scss';
+import autobind from 'autobind-decorator';
+import { GenericAssayBinaryEnrichmentsTableDataStore } from './GenericAssayBinaryEnrichmentsTableDataStore';
+import { GenericAssayBinaryEnrichmentRow } from 'shared/model/EnrichmentRow';
+import { GENERIC_ASSAY_CONFIG } from 'shared/lib/GenericAssayUtils/GenericAssayConfig';
+import {
+ deriveDisplayTextFromGenericAssayType,
+ formatGenericAssayCompactLabelByNameAndId,
+} from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
+import { ContinousDataPvalueTooltip } from './EnrichmentsUtil';
+
+export interface IGenericAssayBinaryEnrichmentTableProps {
+ genericAssayType: string;
+ visibleOrderedColumnNames?: string[];
+ customColumns?: { [id: string]: GenericAssayBinaryEnrichmentTableColumn };
+ data: GenericAssayBinaryEnrichmentRow[];
+ initialSortColumn?: string;
+ dataStore: GenericAssayBinaryEnrichmentsTableDataStore;
+ onEntityClick?: (stableId: string) => void;
+ mutexTendency?: boolean;
+ groupSize?: number;
+}
+
+export enum GenericAssayBinaryEnrichmentTableColumnType {
+ ENTITY_ID = 'Entity ID',
+ LOG_RATIO = 'Log Ratio',
+ P_VALUE = 'P_VALUE',
+ Q_VALUE = 'Q_VALUE',
+ TENDENCY = 'Tendency',
+ EXPRESSED = 'Higher in',
+ MEAN_SUFFIX = ' mean',
+ STANDARD_DEVIATION_SUFFIX = ' standard deviation',
+ ENRICHED = 'Enriched in',
+ MOST_ENRICHED = 'Most enriched in',
+}
+
+export type GenericAssayBinaryEnrichmentTableColumn = Column<
+ GenericAssayBinaryEnrichmentRow
+> & { order?: number };
+
+@observer
+export default class GenericAssayBinaryEnrichmentsTable extends React.Component<
+ IGenericAssayBinaryEnrichmentTableProps,
+ {}
+> {
+ constructor(props: IGenericAssayBinaryEnrichmentTableProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ public static defaultProps = {
+ columns: [
+ GenericAssayBinaryEnrichmentTableColumnType.ENTITY_ID,
+ GenericAssayBinaryEnrichmentTableColumnType.P_VALUE,
+ GenericAssayBinaryEnrichmentTableColumnType.Q_VALUE,
+ ],
+ initialSortColumn: 'q-Value',
+ mutexTendency: true,
+ };
+
+ @autobind
+ private onRowClick(d: GenericAssayBinaryEnrichmentRow) {
+ this.props.onEntityClick!(d.stableId);
+ this.props.dataStore.setHighlighted(d);
+ }
+
+ private get entityTitle() {
+ return (
+ GENERIC_ASSAY_CONFIG.genericAssayConfigByType[
+ this.props.genericAssayType
+ ]?.globalConfig?.entityTitle ||
+ deriveDisplayTextFromGenericAssayType(this.props.genericAssayType)
+ );
+ }
+
+ @computed get columns(): {
+ [columnEnum: string]: GenericAssayBinaryEnrichmentTableColumn;
+ } {
+ const columns: {
+ [columnEnum: string]: GenericAssayBinaryEnrichmentTableColumn;
+ } = this.props.customColumns || {};
+
+ columns[GenericAssayBinaryEnrichmentTableColumnType.ENTITY_ID] = {
+ name: this.entityTitle,
+ render: (d: GenericAssayBinaryEnrichmentRow) => {
+ return (
+
+
+ {formatGenericAssayCompactLabelByNameAndId(
+ d.stableId,
+ d.entityName
+ )}
+
+
+ );
+ },
+ tooltip: {this.entityTitle},
+ filter: (
+ d: GenericAssayBinaryEnrichmentRow,
+ filterString: string,
+ filterStringUpper: string
+ ) => d.entityName.toUpperCase().includes(filterStringUpper),
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) => d.entityName,
+ download: (d: GenericAssayBinaryEnrichmentRow) => d.entityName,
+ };
+
+ columns[GenericAssayBinaryEnrichmentTableColumnType.P_VALUE] = {
+ name: 'p-Value',
+ render: (d: GenericAssayBinaryEnrichmentRow) => (
+
+ {toConditionalPrecision(d.pValue, 3, 0.01)}
+
+ ),
+ tooltip: (
+
+ ),
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) => d.pValue,
+ download: (d: GenericAssayBinaryEnrichmentRow) =>
+ toConditionalPrecision(d.pValue, 3, 0.01),
+ };
+
+ columns[GenericAssayBinaryEnrichmentTableColumnType.Q_VALUE] = {
+ name: 'q-Value',
+ render: (d: GenericAssayBinaryEnrichmentRow) => (
+
+ {formatSignificanceValueWithStyle(d.qValue)}
+
+ ),
+ tooltip: Derived from Benjamini-Hochberg procedure,
+ sortBy: (d: GenericAssayBinaryEnrichmentRow) => d.qValue,
+ download: (d: GenericAssayBinaryEnrichmentRow) =>
+ toConditionalPrecision(d.qValue, 3, 0.01),
+ };
+ return columns;
+ }
+
+ public render() {
+ const orderedColumns = _.sortBy(
+ this.props.visibleOrderedColumnNames!.map(
+ column => this.columns[column]
+ ),
+ (c: GenericAssayBinaryEnrichmentTableColumn) => c.order
+ );
+ return (
+
+ );
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTableDataStore.tsx b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTableDataStore.tsx
new file mode 100644
index 00000000000..b58a4232356
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayBinaryEnrichmentsTableDataStore.tsx
@@ -0,0 +1,18 @@
+import { SimpleGetterLazyMobXTableApplicationDataStore } from 'shared/lib/ILazyMobXTableApplicationDataStore';
+import { GenericAssayBinaryEnrichmentRow } from 'shared/model/EnrichmentRow';
+
+export class GenericAssayBinaryEnrichmentsTableDataStore extends SimpleGetterLazyMobXTableApplicationDataStore<
+ GenericAssayBinaryEnrichmentRow
+> {
+ constructor(
+ getData: () => GenericAssayBinaryEnrichmentRow[],
+ getHighlighted: () => GenericAssayBinaryEnrichmentRow | undefined,
+ public setHighlighted: (c: GenericAssayBinaryEnrichmentRow) => void
+ ) {
+ super(getData);
+ this.dataHighlighter = (d: GenericAssayBinaryEnrichmentRow) => {
+ const highlighted = getHighlighted();
+ return !!(highlighted && d.stableId === highlighted.stableId);
+ };
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsContainer.tsx b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsContainer.tsx
new file mode 100644
index 00000000000..1568dd0001a
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsContainer.tsx
@@ -0,0 +1,795 @@
+import * as React from 'react';
+import { observer, Observer } from 'mobx-react';
+import { observable, computed, action, makeObservable } from 'mobx';
+import {
+ MolecularProfile,
+ Sample,
+ GenericAssayCategoricalEnrichment,
+} from 'cbioportal-ts-api-client';
+import client from 'shared/api/cbioportalClientInstance';
+import {
+ getGenericAssayCategoricalEnrichmentRowData,
+ getGenericAssayCategoricalEnrichmentColumns,
+ getFilteredCategoricalData,
+} from 'pages/resultsView/enrichments/EnrichmentsUtil';
+import LoadingIndicator from 'shared/components/loadingIndicator/LoadingIndicator';
+import ReactSelect from 'react-select1';
+import _ from 'lodash';
+import autobind from 'autobind-decorator';
+import ScrollBar from 'shared/components/Scrollbar/ScrollBar';
+import {
+ Option,
+ DownloadControls,
+ remoteData,
+} from 'cbioportal-frontend-commons';
+import GenericAssayCategoricalEnrichmentsTable, {
+ GenericAssayCategoricalEnrichmentTableColumnType,
+} from './GenericAssayCategoricalEnrichmentsTable';
+import { GenericAssayCategoricalEnrichmentsTableDataStore } from './GenericAssayCategoricalEnrichmentsTableDataStore';
+import { GenericAssayCategoricalEnrichmentRow } from 'shared/model/EnrichmentRow';
+import { getRemoteDataGroupStatus } from 'cbioportal-utils';
+import { EnrichmentAnalysisComparisonGroup } from 'pages/groupComparison/GroupComparisonUtils';
+import {
+ IAxisData,
+ IAxisLogScaleParams,
+ IStringAxisData,
+} from 'pages/resultsView/plots/PlotsTabUtils';
+import CategoryPlot, {
+ CategoryPlotType,
+} from 'pages/groupComparison/CategoryPlot';
+import { OncoprintJS } from 'oncoprintjs';
+import ComparisonStore, {
+ OverlapStrategy,
+} from 'shared/lib/comparison/ComparisonStore';
+import { RESERVED_CLINICAL_VALUE_COLORS } from 'shared/lib/Colors';
+import {
+ filterSampleList,
+ getComparisonCategoricalNaValue,
+} from 'pages/groupComparison/ClinicalDataUtils';
+
+export interface IGenericAssayCategoricalEnrichmentsContainerProps {
+ data: GenericAssayCategoricalEnrichment[];
+ selectedProfile: MolecularProfile;
+ groups: EnrichmentAnalysisComparisonGroup[];
+ sampleKeyToSample: {
+ [uniqueSampleKey: string]: Sample;
+ };
+ genericAssayType: string;
+ alteredVsUnalteredMode?: boolean;
+ patientLevelEnrichments: boolean;
+ onSetPatientLevelEnrichments: (patientLevel: boolean) => void;
+ dataStore: ComparisonStore;
+}
+export enum CategoricalNumericalVisualisationType {
+ Plot = 'Plot',
+ Table = 'Table',
+}
+export const categoryPlotTypeOptions = [
+ { value: CategoryPlotType.Bar, label: 'Bar chart' },
+ { value: CategoryPlotType.StackedBar, label: 'Stacked bar chart' },
+ {
+ value: CategoryPlotType.PercentageStackedBar,
+ label: '100% stacked bar chart',
+ },
+ { value: CategoryPlotType.Heatmap, label: 'Heatmap' },
+];
+
+const SVG_ID = 'categorical-plot-svg';
+function isNumerical(datatype?: string) {
+ return datatype && datatype.toLowerCase() === 'number';
+}
+
+export const numericalVisualisationTypeOptions = [
+ { value: CategoricalNumericalVisualisationType.Plot, label: 'Plot' },
+ { value: CategoricalNumericalVisualisationType.Table, label: 'Table' },
+];
+
+@observer
+export default class GenericAssayCategoricalEnrichmentsContainer extends React.Component<
+ IGenericAssayCategoricalEnrichmentsContainerProps,
+ {}
+> {
+ constructor(props: IGenericAssayCategoricalEnrichmentsContainerProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ static defaultProps: Partial<
+ IGenericAssayCategoricalEnrichmentsContainerProps
+ > = {
+ alteredVsUnalteredMode: true,
+ };
+ // TODO: modify judgement
+ @computed get isNumericalPlot() {
+ return isNumerical(this.highlightedRow!.attributeType);
+ }
+
+ @computed get showLogScaleControls() {
+ return this.isNumericalPlot;
+ }
+
+ @computed get showPAndQControls() {
+ return !this.isTable && !this.isHeatmap;
+ }
+
+ @computed get showHorizontalBarControls() {
+ return !this.showLogScaleControls && !this.isHeatmap;
+ }
+
+ @computed get showSwapAxisControls() {
+ return !this.isTable;
+ }
+
+ @computed get isTable() {
+ return (
+ this.isNumericalPlot &&
+ this.numericalVisualisationType ===
+ CategoricalNumericalVisualisationType.Table
+ );
+ }
+
+ @computed get isHeatmap() {
+ return (
+ !this.isNumericalPlot &&
+ this.categoryPlotType === CategoryPlotType.Heatmap
+ );
+ }
+ @computed get horzLabel() {
+ return this.swapAxes
+ ? `${this.highlightedRow!.attributeType}${
+ this.logScale ? ' (log2)' : ''
+ }`
+ : `Group`;
+ }
+
+ @computed get vertLabel() {
+ return this.swapAxes
+ ? 'Group'
+ : `${this.highlightedRow!.attributeType}${
+ this.logScale ? ' (log2)' : ''
+ }`;
+ }
+ @observable significanceFilter: boolean = false;
+ @observable.ref clickedEntityStableId: string;
+ @observable.ref selectedStableIds: string[] | null;
+ @observable.ref highlightedRow:
+ | GenericAssayCategoricalEnrichmentRow
+ | undefined;
+ @observable.ref _enrichedGroups: string[] = this.props.groups.map(
+ group => group.name
+ );
+ @observable private logScale = false;
+ @observable logScaleFunction: IAxisLogScaleParams | undefined;
+ @observable swapAxes = false;
+ @observable showPAndQ = false;
+ @observable horizontalBars = false;
+ @observable showNA = true;
+
+ private scrollPane: HTMLDivElement;
+
+ private oncoprintJs: OncoprintJS | null = null;
+ @autobind
+ private oncoprintJsRef(oncoprint: OncoprintJS) {
+ this.oncoprintJs = oncoprint;
+ }
+
+ @observable categoryPlotType: CategoryPlotType =
+ CategoryPlotType.PercentageStackedBar;
+
+ @observable
+ numericalVisualisationType: CategoricalNumericalVisualisationType =
+ CategoricalNumericalVisualisationType.Plot;
+
+ @action.bound
+ private onPlotTypeSelect(option: any) {
+ this.categoryPlotType = option.value;
+ }
+
+ @action.bound
+ private onNumericalVisualisationTypeSelect(option: any) {
+ this.numericalVisualisationType = option.value;
+ }
+
+ @action.bound
+ private onClickLogScale() {
+ this.logScale = !this.logScale;
+ if (this.logScale) {
+ const MIN_LOG_ARGUMENT = 0.01;
+ this.logScaleFunction = {
+ label: 'log2',
+ fLogScale: (x: number, offset: number) =>
+ Math.log2(Math.max(x, MIN_LOG_ARGUMENT)),
+ fInvLogScale: (x: number) => Math.pow(2, x),
+ };
+ } else {
+ this.logScaleFunction = undefined;
+ }
+ }
+
+ @action.bound
+ private onClickSwapAxes() {
+ this.swapAxes = !this.swapAxes;
+ }
+
+ @action.bound
+ private onClickTogglePAndQ() {
+ this.showPAndQ = !this.showPAndQ;
+ }
+
+ @action.bound
+ private onClickHorizontalBars() {
+ this.horizontalBars = !this.horizontalBars;
+ }
+
+ @action.bound
+ private onClickShowNA() {
+ this.showNA = !this.showNA;
+ }
+
+ @autobind
+ private getSvg() {
+ if (this.categoryPlotType === CategoryPlotType.Heatmap) {
+ return this.oncoprintJs && this.oncoprintJs.toSVG(true);
+ }
+ return document.getElementById(SVG_ID) as SVGElement | null;
+ }
+
+ @autobind
+ private toolbar() {
+ if (this.isTable) {
+ return <>>;
+ }
+ return (
+
+
+
+ );
+ }
+
+ @autobind
+ private assignScrollPaneRef(el: HTMLDivElement) {
+ this.scrollPane = el;
+ }
+
+ @computed get data(): GenericAssayCategoricalEnrichmentRow[] {
+ return getGenericAssayCategoricalEnrichmentRowData(
+ this.props.data,
+ this.props.groups
+ );
+ }
+
+ @computed get filteredData(): GenericAssayCategoricalEnrichmentRow[] {
+ return getFilteredCategoricalData(this.data, this.filterByStableId);
+ }
+
+ public readonly groupMembershipAxisData = remoteData({
+ await: () => [],
+ invoke: async () => {
+ const categoryOrder = _.map(this.props.groups, group => group.name);
+ const axisData = {
+ data: [],
+ datatype: 'string',
+ categoryOrder,
+ } as IStringAxisData;
+
+ const sampleKeyToGroupSampleData = _.reduce(
+ this.props.groups,
+ (acc, group) => {
+ group.samples.forEach(sample => {
+ const uniqueSampleKey = sample.uniqueSampleKey;
+ if (acc[uniqueSampleKey] === undefined) {
+ acc[uniqueSampleKey] = {
+ uniqueSampleKey,
+ value: [],
+ };
+ }
+ acc[uniqueSampleKey].value.push(group.name);
+ });
+ return acc;
+ },
+ {} as {
+ [uniqueSampleKey: string]: {
+ uniqueSampleKey: string;
+ value: string[];
+ };
+ }
+ );
+
+ axisData.data = _.values(sampleKeyToGroupSampleData);
+ return Promise.resolve(axisData);
+ },
+ });
+
+ readonly gaCategoricalAxisData = remoteData({
+ invoke: async () => {
+ const axisData: IAxisData = { data: [], datatype: 'string' };
+ let normalizedCategory: { [id: string]: string } = {};
+ if (this.highlightedRow !== undefined) {
+ const molecularData = await client.fetchGenericAssayDataInMolecularProfileUsingPOST(
+ {
+ molecularProfileId: this.props.selectedProfile
+ .molecularProfileId,
+ genericAssayDataFilter: {
+ genericAssayStableIds: [
+ (this
+ .highlightedRow as GenericAssayCategoricalEnrichmentRow)
+ .stableId,
+ ],
+ sampleIds: _.map(
+ this.props.sampleKeyToSample,
+ sample => sample.sampleId
+ ),
+ } as any,
+ }
+ );
+ for (const d of molecularData) {
+ const lowerCaseValue = d.value.toLowerCase();
+ if (normalizedCategory[lowerCaseValue] === undefined) {
+ //consider first value as category value
+ normalizedCategory[lowerCaseValue] = d.value;
+ }
+ }
+
+ const axisData_Data = axisData.data;
+
+ for (const d of molecularData) {
+ const value = d.value;
+ axisData_Data.push({
+ uniqueSampleKey: d.uniqueSampleKey,
+ value: normalizedCategory[d.value.toLowerCase()],
+ });
+ }
+ }
+ return Promise.resolve(axisData);
+ },
+ });
+
+ private readonly gaCategoricalAxisDataFiltered = remoteData({
+ await: () => [this.gaCategoricalAxisData, this.props.dataStore.samples],
+ invoke: () => {
+ const axisData = this.gaCategoricalAxisData.result!;
+ const sampleList: Sample[] =
+ this.props.dataStore.overlapStrategy === OverlapStrategy.EXCLUDE
+ ? filterSampleList(
+ this.props.dataStore.samples.result!,
+ this.props.dataStore._activeGroupsNotOverlapRemoved
+ .result!
+ )
+ : this.props.dataStore.samples.result!;
+ if (
+ this.showNA &&
+ axisData.datatype === 'string' &&
+ sampleList.length > 0
+ ) {
+ const naSamples = _.difference(
+ _.uniq(sampleList.map(x => x.uniqueSampleKey)),
+ _.uniq(axisData.data.map(x => x.uniqueSampleKey))
+ );
+ return Promise.resolve({
+ ...axisData,
+ data: axisData.data.concat(
+ naSamples.map(x => ({
+ uniqueSampleKey: x,
+ value: 'NA',
+ }))
+ ),
+ });
+ } else {
+ // filter out NA-like values (e.g. unknown)
+ return Promise.resolve({
+ ...axisData,
+ data: axisData.data.filter(
+ x =>
+ typeof x.value !== 'string' ||
+ _.every(
+ getComparisonCategoricalNaValue(),
+ naValue =>
+ naValue.toLowerCase() !==
+ (x.value as string).toLowerCase()
+ )
+ ),
+ });
+ }
+ },
+ });
+ @autobind
+ private getScrollPane() {
+ return this.scrollPane;
+ }
+
+ @computed private get getUtilitiesMenu() {
+ if (!this.highlightedRow) {
+ return ;
+ }
+ return (
+
+ );
+ }
+
+ @computed get vertAxisDataPromise() {
+ return this.swapAxes
+ ? this.groupMembershipAxisData
+ : this.gaCategoricalAxisDataFiltered;
+ }
+
+ @computed get horzAxisDataPromise() {
+ return this.swapAxes
+ ? this.gaCategoricalAxisDataFiltered
+ : this.groupMembershipAxisData;
+ }
+
+ @computed get categoryToColor() {
+ //add group colors and reserved category colors
+ return _.reduce(
+ this.props.dataStore.uidToGroup.result!,
+ (acc, next) => {
+ acc[next.nameWithOrdinal] = next.color;
+ return acc;
+ },
+ RESERVED_CLINICAL_VALUE_COLORS
+ );
+ }
+
+ @computed get groupToColor() {
+ let groups = this.props.groups;
+ if (!groups) {
+ return {};
+ }
+ return groups.reduce((result, ag) => {
+ result[ag.name as string] = ag.color;
+ return result;
+ }, {} as any);
+ }
+
+ @computed get plot() {
+ if (!this.highlightedRow) {
+ this.highlightedRow = this.filteredData[0];
+ }
+ if (this.filteredData.length === 0 || !this.highlightedRow) {
+ return ;
+ }
+ const promises = [this.horzAxisDataPromise, this.vertAxisDataPromise];
+ const groupStatus = getRemoteDataGroupStatus(...promises);
+ const isPercentage =
+ this.categoryPlotType === CategoryPlotType.PercentageStackedBar;
+ const isStacked =
+ isPercentage ||
+ this.categoryPlotType === CategoryPlotType.StackedBar;
+ switch (groupStatus) {
+ case 'pending':
+ return (
+
+ );
+ case 'error':
+ return Error loading plot data.;
+ default: {
+ if (!this.horzAxisDataPromise || !this.vertAxisDataPromise) {
+ return Error loading plot data.;
+ }
+
+ let plotElt: any = null;
+ plotElt = (
+
+ );
+
+ return (
+
+
+
{this.toolbar}
+
+ {plotElt}
+
+
+ );
+ }
+ }
+ }
+
+ @autobind
+ private filterByStableId(stableId: string) {
+ if (this.selectedStableIds) {
+ return this.selectedStableIds.includes(stableId);
+ } else {
+ // no need to filter the data since there is no selection
+ return true;
+ }
+ }
+
+ @autobind
+ private onEntityClick(stableId: string) {
+ this.clickedEntityStableId = stableId;
+ }
+
+ @autobind
+ private onSelection(stableIds: string[]) {
+ this.selectedStableIds = stableIds;
+ }
+
+ @autobind
+ private onSelectionCleared() {
+ this.selectedStableIds = null;
+ }
+
+ private tableDataStore = new GenericAssayCategoricalEnrichmentsTableDataStore(
+ () => {
+ return this.filteredData;
+ },
+ () => {
+ return this.highlightedRow;
+ },
+ (c: GenericAssayCategoricalEnrichmentRow) => {
+ this.highlightedRow = c;
+ }
+ );
+
+ //used in 2 groups analysis
+ @computed get group1() {
+ return this.props.groups[0];
+ }
+
+ //used in 2 groups analysis
+ @computed get group2() {
+ return this.props.groups[1];
+ }
+
+ @computed get selectedEntitiesSet() {
+ return _.keyBy(this.selectedStableIds || []);
+ }
+
+ @computed get isTwoGroupAnalysis(): boolean {
+ return this.props.groups.length == 2;
+ }
+
+ @computed get customColumns() {
+ return getGenericAssayCategoricalEnrichmentColumns(
+ this.props.groups,
+ this.props.alteredVsUnalteredMode
+ );
+ }
+ @computed get visibleOrderedColumnNames() {
+ const columns = [];
+ columns.push(
+ GenericAssayCategoricalEnrichmentTableColumnType.ENTITY_ID
+ );
+
+ columns.push(
+ GenericAssayCategoricalEnrichmentTableColumnType.ATTRIBUTE_TYPE,
+ GenericAssayCategoricalEnrichmentTableColumnType.STATISTICAL_TEST_NAME,
+ GenericAssayCategoricalEnrichmentTableColumnType.P_VALUE,
+ GenericAssayCategoricalEnrichmentTableColumnType.Q_VALUE
+ );
+
+ return columns;
+ }
+
+ @action.bound
+ onChange(values: { value: string }[]) {
+ this._enrichedGroups = _.map(values, datum => datum.value);
+ }
+
+ @computed get selectedValues() {
+ return this._enrichedGroups.map(id => ({ value: id }));
+ }
+
+ @computed get options(): Option[] {
+ return _.map(this.props.groups, group => {
+ return {
+ label: group.nameOfEnrichmentDirection
+ ? group.nameOfEnrichmentDirection
+ : group.name,
+ value: group.name,
+ };
+ });
+ }
+
+ @computed get selectedRow() {
+ if (this.clickedEntityStableId) {
+ return this.props.data.filter(
+ d => d.stableId === this.clickedEntityStableId
+ )[0];
+ }
+ return undefined;
+ }
+
+ public render() {
+ if (this.props.data.length === 0) {
+ return (
+
+ No data/result available
+
+ );
+ }
+
+ return (
+
+
+ column.name
+ )}
+ genericAssayType={this.props.genericAssayType}
+ groupSize={this.props.groups.length}
+ />
+
+
+ {this.getUtilitiesMenu}
+ {this.plot}
+
+
+ );
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTable.tsx b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTable.tsx
new file mode 100644
index 00000000000..c1356ac8118
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTable.tsx
@@ -0,0 +1,198 @@
+import * as React from 'react';
+import _ from 'lodash';
+import LazyMobXTable, {
+ Column,
+} from '../../../shared/components/lazyMobXTable/LazyMobXTable';
+import { observer } from 'mobx-react';
+import { computed, makeObservable } from 'mobx';
+import { formatSignificanceValueWithStyle } from 'shared/lib/FormatUtils';
+import { toConditionalPrecision } from 'shared/lib/NumberUtils';
+import styles from './styles.module.scss';
+import autobind from 'autobind-decorator';
+import { GenericAssayCategoricalEnrichmentsTableDataStore } from './GenericAssayCategoricalEnrichmentsTableDataStore';
+import { GenericAssayCategoricalEnrichmentRow } from 'shared/model/EnrichmentRow';
+import { GENERIC_ASSAY_CONFIG } from 'shared/lib/GenericAssayUtils/GenericAssayConfig';
+import {
+ deriveDisplayTextFromGenericAssayType,
+ formatGenericAssayCompactLabelByNameAndId,
+} from 'shared/lib/GenericAssayUtils/GenericAssayCommonUtils';
+import { ContinousDataPvalueTooltip } from './EnrichmentsUtil';
+
+export interface IGenericAssayCategoricalEnrichmentTableProps {
+ genericAssayType: string;
+ visibleOrderedColumnNames?: string[];
+ customColumns?: {
+ [id: string]: GenericAssayCategoricalEnrichmentTableColumn;
+ };
+ data: GenericAssayCategoricalEnrichmentRow[];
+ initialSortColumn?: string;
+ dataStore: GenericAssayCategoricalEnrichmentsTableDataStore;
+ onEntityClick?: (stableId: string) => void;
+ mutexTendency?: boolean;
+ groupSize?: number;
+}
+
+export enum GenericAssayCategoricalEnrichmentTableColumnType {
+ ENTITY_ID = 'Entity ID',
+ P_VALUE = 'P_VALUE',
+ Q_VALUE = 'Q_VALUE',
+ ATTRIBUTE_TYPE = 'Attribute Type',
+ STATISTICAL_TEST_NAME = 'Statistical Test',
+}
+
+export type GenericAssayCategoricalEnrichmentTableColumn = Column<
+ GenericAssayCategoricalEnrichmentRow
+> & { order?: number };
+
+@observer
+export default class GenericAssayCategoricalEnrichmentsTable extends React.Component<
+ IGenericAssayCategoricalEnrichmentTableProps,
+ {}
+> {
+ constructor(props: IGenericAssayCategoricalEnrichmentTableProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ public static defaultProps = {
+ columns: [
+ GenericAssayCategoricalEnrichmentTableColumnType.ENTITY_ID,
+ GenericAssayCategoricalEnrichmentTableColumnType.ATTRIBUTE_TYPE,
+ GenericAssayCategoricalEnrichmentTableColumnType.STATISTICAL_TEST_NAME,
+ GenericAssayCategoricalEnrichmentTableColumnType.P_VALUE,
+ GenericAssayCategoricalEnrichmentTableColumnType.Q_VALUE,
+ ],
+ initialSortColumn: 'q-Value',
+ mutexTendency: true,
+ };
+
+ @autobind
+ private onRowClick(d: GenericAssayCategoricalEnrichmentRow) {
+ this.props.onEntityClick!(d.stableId);
+ this.props.dataStore.setHighlighted(d);
+ }
+
+ private get entityTitle() {
+ return (
+ GENERIC_ASSAY_CONFIG.genericAssayConfigByType[
+ this.props.genericAssayType
+ ]?.globalConfig?.entityTitle ||
+ deriveDisplayTextFromGenericAssayType(this.props.genericAssayType)
+ );
+ }
+
+ @computed get columns(): {
+ [columnEnum: string]: GenericAssayCategoricalEnrichmentTableColumn;
+ } {
+ const columns: {
+ [columnEnum: string]: GenericAssayCategoricalEnrichmentTableColumn;
+ } = {};
+
+ columns[GenericAssayCategoricalEnrichmentTableColumnType.ENTITY_ID] = {
+ name: this.entityTitle,
+ render: (d: GenericAssayCategoricalEnrichmentRow) => {
+ return (
+
+
+ {formatGenericAssayCompactLabelByNameAndId(
+ d.stableId,
+ d.entityName
+ )}
+
+
+ );
+ },
+ tooltip: {this.entityTitle},
+ filter: (
+ d: GenericAssayCategoricalEnrichmentRow,
+ filterString: string,
+ filterStringUpper: string
+ ) => d.entityName.toUpperCase().includes(filterStringUpper),
+ sortBy: (d: GenericAssayCategoricalEnrichmentRow) => d.entityName,
+ download: (d: GenericAssayCategoricalEnrichmentRow) => d.entityName,
+ };
+
+ columns[
+ GenericAssayCategoricalEnrichmentTableColumnType.ATTRIBUTE_TYPE
+ ] = {
+ name:
+ GenericAssayCategoricalEnrichmentTableColumnType.ATTRIBUTE_TYPE,
+ render: (d: GenericAssayCategoricalEnrichmentRow) => (
+ {d.attributeType}
+ ),
+ tooltip: Attribute Type,
+ sortBy: (d: GenericAssayCategoricalEnrichmentRow) =>
+ String(d.attributeType),
+ download: (d: GenericAssayCategoricalEnrichmentRow) =>
+ d.attributeType,
+ };
+
+ columns[
+ GenericAssayCategoricalEnrichmentTableColumnType.STATISTICAL_TEST_NAME
+ ] = {
+ name:
+ GenericAssayCategoricalEnrichmentTableColumnType.STATISTICAL_TEST_NAME,
+ render: (d: GenericAssayCategoricalEnrichmentRow) => (
+ {d.statisticalTest}
+ ),
+ tooltip: Statistic Test,
+ sortBy: (d: GenericAssayCategoricalEnrichmentRow) =>
+ String(d.statisticalTest),
+ download: (d: GenericAssayCategoricalEnrichmentRow) =>
+ d.statisticalTest,
+ };
+
+ columns[GenericAssayCategoricalEnrichmentTableColumnType.P_VALUE] = {
+ name: 'p-Value',
+ render: (d: GenericAssayCategoricalEnrichmentRow) => (
+
+ {toConditionalPrecision(d.pValue, 3, 0.01)}
+
+ ),
+ tooltip: (
+
+ ),
+ sortBy: (d: GenericAssayCategoricalEnrichmentRow) => d.pValue,
+ download: (d: GenericAssayCategoricalEnrichmentRow) =>
+ toConditionalPrecision(d.pValue, 3, 0.01),
+ };
+
+ columns[GenericAssayCategoricalEnrichmentTableColumnType.Q_VALUE] = {
+ name: 'q-Value',
+ render: (d: GenericAssayCategoricalEnrichmentRow) => (
+
+ {formatSignificanceValueWithStyle(d.qValue)}
+
+ ),
+ tooltip: Derived from Benjamini-Hochberg procedure,
+ sortBy: (d: GenericAssayCategoricalEnrichmentRow) => d.qValue,
+ download: (d: GenericAssayCategoricalEnrichmentRow) =>
+ toConditionalPrecision(d.qValue, 3, 0.01),
+ };
+ return columns;
+ }
+
+ public render() {
+ const orderedColumns = _.sortBy(
+ this.props.visibleOrderedColumnNames!.map(
+ column => this.columns[column]
+ ),
+ (c: GenericAssayCategoricalEnrichmentTableColumn) => c.order
+ );
+
+ return (
+
+ );
+ }
+}
diff --git a/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTableDataStore.tsx b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTableDataStore.tsx
new file mode 100644
index 00000000000..3c670f8d1d6
--- /dev/null
+++ b/src/pages/resultsView/enrichments/GenericAssayCategoricalEnrichmentsTableDataStore.tsx
@@ -0,0 +1,18 @@
+import { SimpleGetterLazyMobXTableApplicationDataStore } from 'shared/lib/ILazyMobXTableApplicationDataStore';
+import { GenericAssayCategoricalEnrichmentRow } from 'shared/model/EnrichmentRow';
+
+export class GenericAssayCategoricalEnrichmentsTableDataStore extends SimpleGetterLazyMobXTableApplicationDataStore<
+ GenericAssayCategoricalEnrichmentRow
+> {
+ constructor(
+ getData: () => GenericAssayCategoricalEnrichmentRow[],
+ getHighlighted: () => GenericAssayCategoricalEnrichmentRow | undefined,
+ public setHighlighted: (c: GenericAssayCategoricalEnrichmentRow) => void
+ ) {
+ super(getData);
+ this.dataHighlighter = (d: GenericAssayCategoricalEnrichmentRow) => {
+ const highlighted = getHighlighted();
+ return !!(highlighted && d.stableId === highlighted.stableId);
+ };
+ }
+}
diff --git a/src/pages/resultsView/enrichments/MiniFrequencyScatterChart.tsx b/src/pages/resultsView/enrichments/MiniFrequencyScatterChart.tsx
index d4215c60941..0c1c098c5c1 100644
--- a/src/pages/resultsView/enrichments/MiniFrequencyScatterChart.tsx
+++ b/src/pages/resultsView/enrichments/MiniFrequencyScatterChart.tsx
@@ -40,6 +40,7 @@ export interface IMiniFrequencyScatterChartProps {
onSelection: (hugoGeneSymbols: string[]) => void;
onSelectionCleared: () => void;
selectedGenesSet: { [hugoGeneSymbol: string]: any };
+ yAxisLablePrefix?: string;
}
const MAX_DOT_SIZE = 10;
@@ -104,7 +105,8 @@ export default class MiniFrequencyScatterChart extends React.Component<
}
@computed get yLabel() {
- return `Altered Frequency in ${truncateWithEllipsis(
+ const prefix = this.props.yAxisLablePrefix || 'Altered Frequency';
+ return `${prefix} in ${truncateWithEllipsis(
this.props.yGroupName,
this.maxLabelWidth,
'Arial',
diff --git a/src/shared/featureFlags.ts b/src/shared/featureFlags.ts
index 1e87701342b..06c8db19ccf 100644
--- a/src/shared/featureFlags.ts
+++ b/src/shared/featureFlags.ts
@@ -1,4 +1,5 @@
export enum FeatureFlagEnum {
STUDY_VIEW_STRUCT_VAR_TABLE = 'STUDY_VIEW_STRUCT_VAR_TABLE',
LEFT_TRUNCATION_ADJUSTMENT = 'LEFT_TRUNCATION_ADJUSTMENT',
+ GENERIC_ASSAY_GROUP_COMPARISON = 'GENERIC_ASSAY_GROUP_COMPARISON',
}
diff --git a/src/shared/lib/GenericAssayUtils/GenericAssayCommonUtils.ts b/src/shared/lib/GenericAssayUtils/GenericAssayCommonUtils.ts
index 9ad20456eeb..97acfddba2e 100644
--- a/src/shared/lib/GenericAssayUtils/GenericAssayCommonUtils.ts
+++ b/src/shared/lib/GenericAssayUtils/GenericAssayCommonUtils.ts
@@ -397,3 +397,20 @@ export function getSortedGenericAssayTabSpecs(
return _.sortBy(genericAssayTabSpecs, specs => specs.linkText);
}
+
+export function getSortedGenericAssayAllTabSpecs(
+ genericAssayAllEnrichmentProfilesGroupedByGenericAssayType: {
+ [key: string]: MolecularProfile[];
+ } = {}
+): { genericAssayType: string; linkText: string }[] {
+ const genericAssayAllTabSpecs: {
+ genericAssayType: string;
+ linkText: string;
+ }[] = _.keys(
+ genericAssayAllEnrichmentProfilesGroupedByGenericAssayType
+ ).map(genericAssayType => ({
+ genericAssayType,
+ linkText: deriveDisplayTextFromGenericAssayType(genericAssayType),
+ }));
+ return _.sortBy(genericAssayAllTabSpecs, specs => specs.linkText);
+}
diff --git a/src/shared/lib/comparison/ComparisonStore.ts b/src/shared/lib/comparison/ComparisonStore.ts
index aa2649c6909..6f4784a120e 100644
--- a/src/shared/lib/comparison/ComparisonStore.ts
+++ b/src/shared/lib/comparison/ComparisonStore.ts
@@ -44,7 +44,10 @@ import _ from 'lodash';
import {
compareByAlterationPercentage,
getAlterationRowData,
+ pickAllGenericAssayEnrichmentProfiles,
pickCopyNumberEnrichmentProfiles,
+ pickGenericAssayBinaryEnrichmentProfiles,
+ pickGenericAssayCategoricalEnrichmentProfiles,
pickGenericAssayEnrichmentProfiles,
pickMethylationEnrichmentProfiles,
pickMRNAEnrichmentProfiles,
@@ -55,6 +58,8 @@ import {
import {
makeEnrichmentDataPromise,
makeGenericAssayEnrichmentDataPromise,
+ makeGenericAssayBinaryEnrichmentDataPromise,
+ makeGenericAssayCategoricalEnrichmentDataPromise,
} from '../../../pages/resultsView/ResultsViewPageStoreUtils';
import internalClient from '../../api/cbioportalInternalClientInstance';
import autobind from 'autobind-decorator';
@@ -107,6 +112,7 @@ import { AlterationEnrichmentRow } from 'shared/model/AlterationEnrichmentRow';
import AnalysisStore from './AnalysisStore';
import { AnnotatedMutation } from 'shared/model/AnnotatedMutation';
import { compileMutations } from './AnalysisStoreUtils';
+import { FeatureFlagEnum } from 'shared/featureFlags';
export enum OverlapStrategy {
INCLUDE = 'Include',
@@ -175,6 +181,12 @@ export default abstract class ComparisonStore extends AnalysisStore
GroupComparisonTab.GENERIC_ASSAY_PREFIX
) || this.showGenericAssayTab
);
+ this.tabHasBeenShown.set(
+ GroupComparisonTab.GENERIC_ASSAY_BINARY_PREFIX,
+ !!this.tabHasBeenShown.get(
+ GroupComparisonTab.GENERIC_ASSAY_BINARY_PREFIX
+ ) || this.showGenericAssayBinaryTab
+ );
this.tabHasBeenShown.set(
GroupComparisonTab.ALTERATIONS,
!!this.tabHasBeenShown.get(
@@ -596,6 +608,28 @@ export default abstract class ComparisonStore extends AnalysisStore
),
});
+ public readonly genericAssayAllEnrichmentProfilesGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [this.molecularProfilesInActiveStudies],
+ invoke: () => {
+ const availableProfiles = this.appStore.featureFlagStore.has(
+ FeatureFlagEnum.GENERIC_ASSAY_GROUP_COMPARISON
+ )
+ ? this.molecularProfilesInActiveStudies.result!
+ : pickGenericAssayEnrichmentProfiles(
+ this.molecularProfilesInActiveStudies.result!
+ );
+ return Promise.resolve(
+ _.groupBy(
+ pickAllGenericAssayEnrichmentProfiles(
+ availableProfiles
+ ),
+ profile => profile.genericAssayType
+ )
+ );
+ },
+ }
+ );
public readonly genericAssayEnrichmentProfilesGroupedByGenericAssayType = remoteData(
{
await: () => [this.molecularProfilesInActiveStudies],
@@ -611,6 +645,36 @@ export default abstract class ComparisonStore extends AnalysisStore
}
);
+ public readonly genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [this.molecularProfilesInActiveStudies],
+ invoke: () =>
+ Promise.resolve(
+ _.groupBy(
+ pickGenericAssayBinaryEnrichmentProfiles(
+ this.molecularProfilesInActiveStudies.result!
+ ),
+ profile => profile.genericAssayType
+ )
+ ),
+ }
+ );
+
+ public readonly genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [this.molecularProfilesInActiveStudies],
+ invoke: () =>
+ Promise.resolve(
+ _.groupBy(
+ pickGenericAssayCategoricalEnrichmentProfiles(
+ this.molecularProfilesInActiveStudies.result!
+ ),
+ profile => profile.genericAssayType
+ )
+ ),
+ }
+ );
+
@observable.ref private _mutationEnrichmentProfileMap: {
[studyId: string]: MolecularProfile;
} = {};
@@ -630,12 +694,29 @@ export default abstract class ComparisonStore extends AnalysisStore
[studyId: string]: MolecularProfile;
} = {};
@observable.ref
+ private _selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType: {
+ [geneircAssayType: string]: {
+ [studyId: string]: MolecularProfile;
+ };
+ } = {};
+ @observable.ref
private _selectedGenericAssayEnrichmentProfileMapGroupedByGenericAssayType: {
[geneircAssayType: string]: {
[studyId: string]: MolecularProfile;
};
} = {};
-
+ @observable.ref
+ private _selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType: {
+ [geneircAssayType: string]: {
+ [studyId: string]: MolecularProfile;
+ };
+ } = {};
+ @observable.ref
+ private _selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType: {
+ [geneircAssayType: string]: {
+ [studyId: string]: MolecularProfile;
+ };
+ } = {};
readonly selectedStudyMutationEnrichmentProfileMap = remoteData({
await: () => [this.mutationEnrichmentProfiles],
invoke: () => {
@@ -797,7 +878,45 @@ export default abstract class ComparisonStore extends AnalysisStore
}
},
});
-
+ readonly selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [
+ this.genericAssayAllEnrichmentProfilesGroupedByGenericAssayType,
+ ],
+ invoke: () => {
+ if (
+ _.isEmpty(
+ this
+ ._selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType
+ )
+ ) {
+ return Promise.resolve(
+ _.mapValues(
+ this
+ .genericAssayAllEnrichmentProfilesGroupedByGenericAssayType
+ .result!,
+ genericAssayEnrichmentProfiles => {
+ const molecularProfilesbyStudyId = _.groupBy(
+ genericAssayEnrichmentProfiles,
+ profile => profile.studyId
+ );
+ // Select only one molecular profile for each study
+ return _.mapValues(
+ molecularProfilesbyStudyId,
+ molecularProfiles => molecularProfiles[0]
+ );
+ }
+ )
+ );
+ } else {
+ return Promise.resolve(
+ this
+ ._selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType
+ );
+ }
+ },
+ }
+ );
readonly selectedGenericAssayEnrichmentProfileMapGroupedByGenericAssayType = remoteData(
{
await: () => [
@@ -838,6 +957,87 @@ export default abstract class ComparisonStore extends AnalysisStore
}
);
+ readonly selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [
+ this
+ .genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType,
+ ],
+ invoke: () => {
+ if (
+ _.isEmpty(
+ this
+ ._selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType
+ )
+ ) {
+ return Promise.resolve(
+ _.mapValues(
+ this
+ .genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType
+ .result!,
+ genericAssayEnrichmentProfiles => {
+ const molecularProfilesbyStudyId = _.groupBy(
+ genericAssayEnrichmentProfiles,
+ profile => profile.studyId
+ );
+ // Select only one molecular profile for each study
+ return _.mapValues(
+ molecularProfilesbyStudyId,
+ molecularProfiles => molecularProfiles[0]
+ );
+ }
+ )
+ );
+ } else {
+ return Promise.resolve(
+ this
+ ._selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType
+ );
+ }
+ },
+ }
+ );
+
+ readonly selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType = remoteData(
+ {
+ await: () => [
+ this
+ .genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType,
+ ],
+ invoke: () => {
+ if (
+ _.isEmpty(
+ this
+ ._selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType
+ )
+ ) {
+ return Promise.resolve(
+ _.mapValues(
+ this
+ .genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType
+ .result!,
+ genericAssayEnrichmentProfiles => {
+ const molecularProfilesbyStudyId = _.groupBy(
+ genericAssayEnrichmentProfiles,
+ profile => profile.studyId
+ );
+ // Select only one molecular profile for each study
+ return _.mapValues(
+ molecularProfilesbyStudyId,
+ molecularProfiles => molecularProfiles[0]
+ );
+ }
+ )
+ );
+ } else {
+ return Promise.resolve(
+ this
+ ._selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType
+ );
+ }
+ },
+ }
+ );
@action
public setMutationEnrichmentProfileMap(profileMap: {
[studyId: string]: MolecularProfile;
@@ -880,6 +1080,23 @@ export default abstract class ComparisonStore extends AnalysisStore
this._methylationEnrichmentProfileMap = profileMap;
}
+ @action
+ public setAllGenericAssayEnrichmentProfileMap(
+ profileMap: {
+ [studyId: string]: MolecularProfile;
+ },
+ genericAssayType: string
+ ) {
+ const clonedMap = _.clone(
+ this
+ .selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType
+ .result!
+ );
+ clonedMap[genericAssayType] = profileMap;
+ // trigger the function to recompute
+ this._selectedAllGenericAssayEnrichmentProfileMapGroupedByGenericAssayType = clonedMap;
+ }
+
@action
public setGenericAssayEnrichmentProfileMap(
profileMap: {
@@ -1532,6 +1749,106 @@ export default abstract class ComparisonStore extends AnalysisStore
},
});
+ readonly gaBinaryEnrichmentGroupsByAssayType = remoteData({
+ await: () => [
+ this
+ .selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType,
+ this.enrichmentAnalysisGroups,
+ ],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this
+ .selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType
+ .result!,
+ selectedGenericAssayBinaryEnrichmentProfileMap => {
+ let studyIds = Object.keys(
+ selectedGenericAssayBinaryEnrichmentProfileMap
+ );
+ // assumes single study for now
+ if (studyIds.length === 1) {
+ return this.enrichmentAnalysisGroups.result!.reduce(
+ (
+ acc: EnrichmentAnalysisComparisonGroup[],
+ group
+ ) => {
+ // filter samples having mutation profile
+ const filteredSamples = group.samples.filter(
+ sample =>
+ selectedGenericAssayBinaryEnrichmentProfileMap[
+ sample.studyId
+ ] !== undefined
+ );
+ if (filteredSamples.length > 0) {
+ acc.push({
+ ...group,
+ samples: filteredSamples,
+ description: `samples in ${group.name}`,
+ });
+ }
+ return acc;
+ },
+ []
+ );
+ } else {
+ return [];
+ }
+ }
+ )
+ );
+ },
+ });
+
+ readonly gaCategoricalEnrichmentGroupsByAssayType = remoteData({
+ await: () => [
+ this
+ .selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType,
+ this.enrichmentAnalysisGroups,
+ ],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this
+ .selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType
+ .result!,
+ selectedGenericAssayCategoricalEnrichmentProfileMap => {
+ let studyIds = Object.keys(
+ selectedGenericAssayCategoricalEnrichmentProfileMap
+ );
+ // assumes single study for now
+ if (studyIds.length === 1) {
+ return this.enrichmentAnalysisGroups.result!.reduce(
+ (
+ acc: EnrichmentAnalysisComparisonGroup[],
+ group
+ ) => {
+ // filter samples having mutation profile
+ const filteredSamples = group.samples.filter(
+ sample =>
+ selectedGenericAssayCategoricalEnrichmentProfileMap[
+ sample.studyId
+ ] !== undefined
+ );
+ if (filteredSamples.length > 0) {
+ acc.push({
+ ...group,
+ samples: filteredSamples,
+ description: `samples in ${group.name}`,
+ });
+ }
+ return acc;
+ },
+ []
+ );
+ } else {
+ return [];
+ }
+ }
+ )
+ );
+ },
+ });
+
readonly gaEnrichmentDataQueryByAssayType = remoteData({
await: () => [
this.gaEnrichmentGroupsByAssayType,
@@ -1570,6 +1887,81 @@ export default abstract class ComparisonStore extends AnalysisStore
},
});
+ readonly gaBinaryEnrichmentDataQueryByAssayType = remoteData({
+ await: () => [
+ this.gaBinaryEnrichmentGroupsByAssayType,
+ this
+ .selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType,
+ ],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this.gaBinaryEnrichmentGroupsByAssayType.result!,
+ (
+ genericAssayEnrichmentAnalysisGroups,
+ genericAssayType
+ ) => {
+ return genericAssayEnrichmentAnalysisGroups.map(
+ group => {
+ const molecularProfileCaseIdentifiers = group.samples.map(
+ sample => ({
+ caseId: sample.sampleId,
+ molecularProfileId: this
+ .selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType
+ .result![genericAssayType][
+ sample.studyId
+ ].molecularProfileId,
+ })
+ );
+ return {
+ name: group.name,
+ molecularProfileCaseIdentifiers,
+ };
+ }
+ );
+ }
+ )
+ );
+ },
+ });
+
+ readonly gaCategoricalEnrichmentDataQueryByAssayType = remoteData({
+ await: () => [
+ this.gaCategoricalEnrichmentGroupsByAssayType,
+ this
+ .selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType,
+ ],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this.gaCategoricalEnrichmentGroupsByAssayType.result!,
+ (
+ genericAssayEnrichmentAnalysisGroups,
+ genericAssayType
+ ) => {
+ return genericAssayEnrichmentAnalysisGroups.map(
+ group => {
+ const molecularProfileCaseIdentifiers = group.samples.map(
+ sample => ({
+ caseId: sample.sampleId,
+ molecularProfileId: this
+ .selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType
+ .result![genericAssayType][
+ sample.studyId
+ ].molecularProfileId,
+ })
+ );
+ return {
+ name: group.name,
+ molecularProfileCaseIdentifiers,
+ };
+ }
+ );
+ }
+ )
+ );
+ },
+ });
readonly gaEnrichmentDataByAssayType = remoteData({
await: () => [this.gaEnrichmentDataQueryByAssayType],
invoke: () => {
@@ -1609,6 +2001,86 @@ export default abstract class ComparisonStore extends AnalysisStore
},
});
+ readonly gaBinaryEnrichmentDataByAssayType = remoteData({
+ await: () => [this.gaBinaryEnrichmentDataQueryByAssayType],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this.gaBinaryEnrichmentDataQueryByAssayType.result!,
+ (
+ genericAssayEnrichmentDataRequestGroups,
+ genericAssayType
+ ) => {
+ return makeGenericAssayBinaryEnrichmentDataPromise({
+ await: () => [],
+ getSelectedProfileMap: () =>
+ this
+ .selectedGenericAssayBinaryEnrichmentProfileMapGroupedByGenericAssayType
+ .result![genericAssayType], // returns an empty array if the selected study doesn't have any generic assay profiles
+ fetchData: () => {
+ if (
+ genericAssayEnrichmentDataRequestGroups &&
+ genericAssayEnrichmentDataRequestGroups.length >
+ 1
+ ) {
+ return internalClient.fetchGenericAssayBinaryDataEnrichmentInMultipleMolecularProfilesUsingPOST(
+ {
+ enrichmentType: 'SAMPLE',
+ groups: genericAssayEnrichmentDataRequestGroups,
+ }
+ );
+ } else {
+ return Promise.resolve([]);
+ }
+ },
+ });
+ }
+ )
+ );
+ },
+ });
+
+ readonly gaCategoricalEnrichmentDataByAssayType = remoteData({
+ await: () => [this.gaCategoricalEnrichmentDataQueryByAssayType],
+ invoke: () => {
+ return Promise.resolve(
+ _.mapValues(
+ this.gaCategoricalEnrichmentDataQueryByAssayType.result!,
+ (
+ genericAssayEnrichmentDataRequestGroups,
+ genericAssayType
+ ) => {
+ return makeGenericAssayCategoricalEnrichmentDataPromise(
+ {
+ await: () => [],
+ getSelectedProfileMap: () =>
+ this
+ .selectedGenericAssayCategoricalEnrichmentProfileMapGroupedByGenericAssayType
+ .result![genericAssayType], // returns an empty array if the selected study doesn't have any generic assay profiles
+ fetchData: () => {
+ if (
+ genericAssayEnrichmentDataRequestGroups &&
+ genericAssayEnrichmentDataRequestGroups.length >
+ 1
+ ) {
+ return internalClient.fetchGenericAssayCategoricalDataEnrichmentInMultipleMolecularProfilesUsingPOST(
+ {
+ enrichmentType: 'SAMPLE',
+ groups: genericAssayEnrichmentDataRequestGroups,
+ }
+ );
+ } else {
+ return Promise.resolve([]);
+ }
+ },
+ }
+ );
+ }
+ )
+ );
+ },
+ });
+
@computed get survivalTabShowable() {
return (
this.survivalClinicalDataExists.isComplete &&
@@ -1780,6 +2252,31 @@ export default abstract class ComparisonStore extends AnalysisStore
);
}
+ @computed get genericAssayBinaryTabShowable() {
+ return (
+ this.genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType
+ .isComplete &&
+ _.size(
+ this
+ .genericAssayBinaryEnrichmentProfilesGroupedByGenericAssayType
+ .result!
+ ) > 0
+ );
+ }
+
+ @computed get genericAssayCategoricalTabShowable() {
+ return (
+ this
+ .genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType
+ .isComplete &&
+ _.size(
+ this
+ .genericAssayCategoricalEnrichmentProfilesGroupedByGenericAssayType
+ .result!
+ ) > 0
+ );
+ }
+
@computed get showGenericAssayTab() {
return !!(
this.genericAssayTabShowable ||
@@ -1791,6 +2288,28 @@ export default abstract class ComparisonStore extends AnalysisStore
);
}
+ @computed get showGenericAssayBinaryTab() {
+ return !!(
+ this.genericAssayBinaryTabShowable ||
+ (this.activeGroups.isComplete &&
+ this.activeGroups.result!.length === 0 &&
+ this.tabHasBeenShown.get(
+ GroupComparisonTab.GENERIC_ASSAY_BINARY_PREFIX
+ ))
+ );
+ }
+
+ @computed get showGenericAssayCategoricalTab() {
+ return !!(
+ this.genericAssayCategoricalTabShowable ||
+ (this.activeGroups.isComplete &&
+ this.activeGroups.result!.length === 0 &&
+ this.tabHasBeenShown.get(
+ GroupComparisonTab.GENERIC_ASSAY_CATEGORICAL_PREFIX
+ ))
+ );
+ }
+
@computed get genericAssayTabUnavailable() {
return (
(this.activeGroups.isComplete &&
@@ -1801,6 +2320,26 @@ export default abstract class ComparisonStore extends AnalysisStore
);
}
+ @computed get genericAssayBinaryTabUnavailable() {
+ return (
+ (this.activeGroups.isComplete &&
+ this.activeGroups.result.length < 2) || //less than two active groups
+ (this.activeStudyIds.isComplete &&
+ this.activeStudyIds.result.length > 1) || //more than one active study
+ !this.genericAssayBinaryTabShowable
+ );
+ }
+
+ @computed get genericAssayCategoricalTabUnavailable() {
+ return (
+ (this.activeGroups.isComplete &&
+ this.activeGroups.result.length < 2) || //less than two active groups
+ (this.activeStudyIds.isComplete &&
+ this.activeStudyIds.result.length > 1) || //more than one active study
+ !this.genericAssayCategoricalTabShowable
+ );
+ }
+
public readonly sampleMap = remoteData({
await: () => [this.samples],
invoke: () => {
diff --git a/src/shared/model/EnrichmentRow.ts b/src/shared/model/EnrichmentRow.ts
index 9c36fd18868..07bfe31666c 100644
--- a/src/shared/model/EnrichmentRow.ts
+++ b/src/shared/model/EnrichmentRow.ts
@@ -1,4 +1,7 @@
-import { GroupStatistics } from 'cbioportal-ts-api-client';
+import {
+ GenericAssayCountSummary,
+ GroupStatistics,
+} from 'cbioportal-ts-api-client';
export interface BaseEnrichmentRow {
checked: boolean;
@@ -20,3 +23,30 @@ export interface GenericAssayEnrichmentRow extends BaseEnrichmentRow {
stableId: string;
entityName: string;
}
+
+export interface GenericAssayBinaryEnrichmentRow {
+ checked: boolean;
+ disabled: boolean;
+ logRatio?: number;
+ pValue: number;
+ qValue: number;
+ enrichedGroup: string;
+ stableId: string;
+ entityName: string;
+ groupsSet: {
+ [id: string]: GenericAssayCountSummary & { alteredPercentage: number };
+ };
+}
+
+export interface GenericAssayCategoricalEnrichmentRow {
+ checked: boolean;
+ disabled: boolean;
+ enrichedGroup: string;
+ pValue: number;
+ qValue: number;
+ stableId: string;
+ entityName: string;
+ attributeType: string;
+ statisticalTest: string;
+ groupsSet: { [id: string]: GroupStatistics };
+}