import * as React from 'react';
import {
AlterationEnrichment,
GenomicEnrichment,
MolecularProfile,
} from 'cbioportal-ts-api-client';
import { AlterationEnrichmentRow } from 'shared/model/AlterationEnrichmentRow';
import {
ExpressionEnrichmentRow,
GenericAssayEnrichmentRow,
} from 'shared/model/EnrichmentRow';
import { formatLogOddsRatio, roundLogRatio } from 'shared/lib/FormatUtils';
import _ from 'lodash';
import { AlterationTypeConstants, DataTypeConstants } from 'shared/constants';
import { filterAndSortProfiles } from '../coExpression/CoExpressionTabUtils';
import { IMiniFrequencyScatterChartData } from './MiniFrequencyScatterChart';
import {
AlterationEnrichmentTableColumn,
AlterationEnrichmentTableColumnType,
} from './AlterationEnrichmentsTable';
import styles from './styles.module.scss';
import classNames from 'classnames';
import { IMultipleCategoryBarPlotData } from 'pages/groupComparison/MultipleCategoryBarPlot';
import { getTextColor } from '../../groupComparison/OverlapUtils';
import { TruncatedText } from 'cbioportal-frontend-commons';
import {
ExpressionEnrichmentTableColumn,
ExpressionEnrichmentTableColumnType,
} from './ExpressionEnrichmentsTable';
import { Datalabel } from 'shared/lib/DataUtils';
import { GenericAssayEnrichment } from 'cbioportal-ts-api-client/dist/generated/CBioPortalAPIInternal';
import {
GenericAssayEnrichmentTableColumn,
GenericAssayEnrichmentTableColumnType,
} from './GenericAssayEnrichmentsTable';
export type AlterationEnrichmentWithQ = AlterationEnrichment & {
logRatio?: number;
qValue: number;
value?: number /* used for copy number in group comparison */;
};
export type ExpressionEnrichmentWithQ = GenomicEnrichment & {
qValue: number;
};
export type GenericAssayEnrichmentWithQ = GenericAssayEnrichment & {
qValue: number;
};
export type ContinousDataPvalueTooltipProps = {
groupSize?: number;
};
export const CNA_AMP_VALUE = 2;
export const CNA_HOMDEL_VALUE = -2;
export const CNA_TO_ALTERATION: { [cna: number]: string } = {
[CNA_AMP_VALUE]: 'AMP',
[CNA_HOMDEL_VALUE]: 'HOMDEL',
};
export const PVALUE_TEST_GROUP_SIZE_THRESHOLD = 3;
export enum GeneOptionLabel {
USER_DEFINED_OPTION = 'User-defined genes',
HIGHEST_FREQUENCY = 'Genes with highest frequency in any group',
AVERAGE_FREQUENCY = 'Genes with highest average frequency',
SIGNIFICANT_P_VALUE = 'Genes with most significant p-value',
SYNC_WITH_TABLE = 'Sync with table (up to 100 genes)',
}
export enum AlterationContainerType {
MUTATION = 'MUTATION',
COPY_NUMBER = 'COPY_NUMBER',
ALTERATIONS = 'ALTERATIONS',
}
export enum EnrichmentType {
MRNA_EXPRESSION = 'mRNA expression',
PROTEIN_EXPRESSION = 'protein expression',
DNA_METHYLATION = 'DNA methylation',
}
export function PERCENTAGE_IN_headerRender(name: string) {
return (
);
}
export function STAT_IN_headerRender(stat: string, name: string) {
return (
{stat} in
);
}
export function calculateExpressionTendency(logOddsRatio: number): string {
return logOddsRatio > 0 ? 'Over-expressed' : 'Under-expressed';
}
export function formatAlterationTendency(text: string) {
return ;
}
export function formatPercentage(
group: string,
data: AlterationEnrichmentRow
): string {
const datum = data.groupsSet[group];
return (
datum.alteredCount + ' (' + datum.alteredPercentage.toFixed(2) + '%)'
);
}
export function getAlteredCount(
group: string,
data: AlterationEnrichmentRow
): number {
return data.groupsSet[group].alteredCount;
}
function volcanoPlotYCoord(pValue: number) {
if (pValue === 0 || Math.log10(pValue) < -10) {
return 10;
} else {
return -Math.log10(pValue);
}
}
export function getAlterationScatterData(
alterationEnrichments: AlterationEnrichmentRow[],
queryGenes: string[]
): any[] {
return alterationEnrichments
.filter(
a =>
a.pValue !== undefined &&
a.qValue !== undefined &&
!queryGenes.includes(a.hugoGeneSymbol)
)
.map(alterationEnrichment => {
return {
x: roundLogRatio(Number(alterationEnrichment.logRatio), 10),
y: volcanoPlotYCoord(alterationEnrichment.pValue!),
hugoGeneSymbol: alterationEnrichment.hugoGeneSymbol,
pValue: alterationEnrichment.pValue,
qValue: alterationEnrichment.qValue,
logRatio: alterationEnrichment.logRatio,
hovered: false,
};
});
}
export function getAlterationFrequencyScatterData(
alterationEnrichments: AlterationEnrichmentRow[],
queryGenes: string[],
group1: string,
group2: string
): IMiniFrequencyScatterChartData[] {
return alterationEnrichments
.filter(
a =>
a.pValue !== undefined &&
a.qValue !== undefined &&
!queryGenes.includes(a.hugoGeneSymbol)
)
.map(alterationEnrichment => {
return {
x: alterationEnrichment.groupsSet[group1].alteredPercentage,
y: alterationEnrichment.groupsSet[group2].alteredPercentage,
hugoGeneSymbol: alterationEnrichment.hugoGeneSymbol,
pValue: alterationEnrichment.pValue!,
qValue: alterationEnrichment.qValue!,
logRatio: alterationEnrichment.logRatio!,
};
});
}
export function getExpressionScatterData(
expressionEnrichments: ExpressionEnrichmentRow[],
queryGenes: string[]
): any[] {
return expressionEnrichments
.filter(a => !queryGenes.includes(a.hugoGeneSymbol))
.map(expressionEnrichment => {
return {
x: expressionEnrichment.logRatio,
y: volcanoPlotYCoord(expressionEnrichment.pValue),
hugoGeneSymbol: expressionEnrichment.hugoGeneSymbol,
entrezGeneId: expressionEnrichment.entrezGeneId,
pValue: expressionEnrichment.pValue,
qValue: expressionEnrichment.qValue,
logRatio: expressionEnrichment.logRatio,
hovered: false,
};
});
}
export function getGenericAssayScatterData(
genericAssayEnrichments: GenericAssayEnrichmentRow[]
): any[] {
return genericAssayEnrichments.map(genericAssayEnrichment => {
return {
x: genericAssayEnrichment.logRatio,
y: volcanoPlotYCoord(genericAssayEnrichment.pValue),
stableId: genericAssayEnrichment.stableId,
entityName: genericAssayEnrichment.entityName,
pValue: genericAssayEnrichment.pValue,
qValue: genericAssayEnrichment.qValue,
logRatio: genericAssayEnrichment.logRatio,
hovered: false,
};
});
}
export function getAlterationRowData(
alterationEnrichments: AlterationEnrichmentWithQ[],
queryGenes: string[],
groups: { name: string; nameOfEnrichmentDirection?: string }[]
): AlterationEnrichmentRow[] {
return alterationEnrichments.map(alterationEnrichment => {
let countsWithAlteredPercentage = _.map(
alterationEnrichment.counts,
datum => {
const alteredPercentage =
datum.alteredCount > 0 && datum.profiledCount > 0
? (datum.alteredCount / datum.profiledCount) * 100
: 0;
return {
...datum,
alteredPercentage,
};
}
);
let groupsSet = _.keyBy(
countsWithAlteredPercentage,
count => count.name
);
let enrichedGroup: string | undefined = undefined;
let logRatio: number | undefined = undefined;
if (groups.length === 2) {
let group1Data = groupsSet[groups[0].name];
let group2Data = groupsSet[groups[1].name];
if (alterationEnrichment.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 (alterationEnrichment.pValue !== undefined) {
countsWithAlteredPercentage.sort(
(a, b) => b.alteredPercentage - a.alteredPercentage
);
enrichedGroup = countsWithAlteredPercentage[0].name;
}
return {
checked: queryGenes.includes(alterationEnrichment.hugoGeneSymbol),
disabled: queryGenes.includes(alterationEnrichment.hugoGeneSymbol),
hugoGeneSymbol: alterationEnrichment.hugoGeneSymbol,
entrezGeneId: alterationEnrichment.entrezGeneId,
cytoband: alterationEnrichment.cytoband,
pValue: alterationEnrichment.pValue,
qValue: alterationEnrichment.qValue,
enrichedGroup,
groupsSet,
logRatio,
value: alterationEnrichment.value,
};
});
}
export function getExpressionRowData(
expressionEnrichments: ExpressionEnrichmentWithQ[],
queryGenes: string[],
groups: { name: string; nameOfEnrichmentDirection?: string }[]
): ExpressionEnrichmentRow[] {
return expressionEnrichments.map(expressionEnrichment => {
let enrichedGroup = '';
let logRatio: number | undefined = undefined;
let groupsSet = _.keyBy(
expressionEnrichment.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 = expressionEnrichment.groupsStatistics.sort(
(a, b) => b.meanExpression - a.meanExpression
)[0].name;
}
return {
checked: queryGenes.includes(expressionEnrichment.hugoGeneSymbol),
disabled: queryGenes.includes(expressionEnrichment.hugoGeneSymbol),
hugoGeneSymbol: expressionEnrichment.hugoGeneSymbol,
entrezGeneId: expressionEnrichment.entrezGeneId,
cytoband: expressionEnrichment.cytoband,
pValue: expressionEnrichment.pValue,
qValue: expressionEnrichment.qValue,
enrichedGroup,
groupsSet,
logRatio,
};
});
}
export function getGenericAssayEnrichmentRowData(
genericAssayEnrichments: GenericAssayEnrichmentWithQ[],
groups: { name: string; nameOfEnrichmentDirection?: string }[]
): GenericAssayEnrichmentRow[] {
return genericAssayEnrichments.map(genericAssayEnrichment => {
let enrichedGroup = '';
// fallback to stable id if name is not specified
let entityName =
'NAME' in genericAssayEnrichment.genericEntityMetaProperties
? genericAssayEnrichment.genericEntityMetaProperties['NAME']
: genericAssayEnrichment.stableId;
let logRatio: number | undefined = undefined;
let groupsSet = _.keyBy(
genericAssayEnrichment.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 = genericAssayEnrichment.groupsStatistics.sort(
(a, b) => b.meanExpression - a.meanExpression
)[0].name;
}
return {
checked: false,
disabled: false,
stableId: genericAssayEnrichment.stableId,
entityName,
pValue: genericAssayEnrichment.pValue,
qValue: genericAssayEnrichment.qValue,
enrichedGroup,
groupsSet,
logRatio,
};
});
}
export function getFilteredData(
data: (
| ExpressionEnrichmentRow
| AlterationEnrichmentRow
| GenericAssayEnrichmentRow
)[],
expressedGroups: string[],
qValueFilter: boolean,
filterFunction: (value: string) => boolean,
isGenericAssayData: boolean = false
): any[] {
return data.filter(enrichmentDatum => {
let result = false;
expressedGroups.forEach(enrichedGroup => {
const enrichedGroupData = enrichmentDatum.groupsSet[
enrichedGroup
] as any;
if (!enrichedGroupData) {
return false;
}
let enrichedGroupAlteredPercentage =
enrichedGroupData.meanExpression ||
enrichedGroupData.alteredPercentage;
let res = _.reduce(
enrichmentDatum.groupsSet,
(acc, next, group) => {
if (enrichedGroup !== group) {
let alteredPercentage =
(next as any).meanExpression ||
(next as any).alteredPercentage;
acc =
acc &&
enrichedGroupAlteredPercentage >= alteredPercentage;
}
return acc;
},
true
);
if (res) {
result = res;
return false;
}
});
if (qValueFilter && enrichmentDatum.qValue) {
result = result && enrichmentDatum.qValue < 0.05;
}
if (isGenericAssayData) {
result =
result &&
filterFunction(
(enrichmentDatum as GenericAssayEnrichmentRow).stableId
);
} else {
result =
result &&
filterFunction(
(enrichmentDatum as
| ExpressionEnrichmentRow
| AlterationEnrichmentRow).hugoGeneSymbol
);
}
return result;
});
}
export function getBarChartTooltipContent(
tooltipModel: any,
selectedGene: string
): string {
let tooltipContent = '';
if (tooltipModel != null) {
const datum = tooltipModel.datum;
tooltipContent += 'Query Genes ';
if (datum.x == 2) {
if (datum.index == 0) {
tooltipContent += 'Altered: ';
} else {
tooltipContent += 'Unaltered: ';
}
} else {
if (datum.index == 0) {
tooltipContent += 'Altered, ' + selectedGene + ' Unaltered: ';
} else if (datum.index == 1) {
tooltipContent += 'Altered, ' + selectedGene + ' Altered: ';
} else if (datum.index == 2) {
tooltipContent += 'Unaltered, ' + selectedGene + ' Altered: ';
} else if (datum.index == 3) {
tooltipContent += 'Unaltered, ' + selectedGene + ' Unaltered: ';
}
}
tooltipContent += datum.y;
}
return tooltipContent;
}
export function getAlterationsTooltipContent(alterations: any[]): string {
let result: string = '';
let currentGene: string;
alterations.forEach(a => {
const hugoGeneSymbol = a.gene.hugoGeneSymbol;
if (hugoGeneSymbol != currentGene) {
result += hugoGeneSymbol + ': ';
}
if (a.alterationType === 'MUTATION_EXTENDED') {
result += 'MUT';
} else if (a.alterationType === 'PROTEIN_LEVEL') {
result += 'RPPA-' + a.alterationSubType.toUpperCase();
} else {
result += a.alterationSubType.toUpperCase();
}
result += '; ';
currentGene = hugoGeneSymbol;
});
return result;
}
export function pickMutationEnrichmentProfiles(profiles: MolecularProfile[]) {
return _.filter(
profiles,
(profile: MolecularProfile) =>
profile.molecularAlterationType ===
AlterationTypeConstants.MUTATION_EXTENDED
);
}
export function pickStructuralVariantEnrichmentProfiles(
profiles: MolecularProfile[]
) {
return _.filter(
profiles,
(profile: MolecularProfile) =>
profile.molecularAlterationType ===
AlterationTypeConstants.STRUCTURAL_VARIANT ||
profile.molecularAlterationType === AlterationTypeConstants.FUSION
);
}
export function pickCopyNumberEnrichmentProfiles(profiles: MolecularProfile[]) {
return _.filter(
profiles,
(profile: MolecularProfile) =>
profile.molecularAlterationType ===
AlterationTypeConstants.COPY_NUMBER_ALTERATION &&
profile.datatype === 'DISCRETE'
);
}
export function pickMRNAEnrichmentProfiles(profiles: MolecularProfile[]) {
const mrnaProfiles = profiles.filter(p => {
return (
p.molecularAlterationType ===
AlterationTypeConstants.MRNA_EXPRESSION
);
});
return filterAndSortProfiles(mrnaProfiles);
}
export function pickProteinEnrichmentProfiles(profiles: MolecularProfile[]) {
const protProfiles = profiles.filter(p => {
return (
p.molecularAlterationType === AlterationTypeConstants.PROTEIN_LEVEL
);
});
return filterAndSortProfiles(protProfiles);
}
export function pickMethylationEnrichmentProfiles(
profiles: MolecularProfile[]
) {
return profiles.filter(p => {
return (
p.molecularAlterationType === AlterationTypeConstants.METHYLATION
);
});
}
export function pickGenericAssayEnrichmentProfiles(
profiles: MolecularProfile[]
) {
// TODO: Pick profiles from all Generic Assay dataTypes after we implement related features
return profiles.filter(p => {
return (
p.molecularAlterationType ===
AlterationTypeConstants.GENERIC_ASSAY &&
p.datatype === DataTypeConstants.LIMITVALUE
);
});
}
export function getAlterationEnrichmentColumns(
groups: { name: string; description: string; color?: string }[],
alteredVsUnalteredMode?: boolean
): AlterationEnrichmentTableColumn[] {
let columns: AlterationEnrichmentTableColumn[] = [];
const nameToGroup = _.keyBy(groups, g => g.name);
let enrichedGroupColum: AlterationEnrichmentTableColumn = {
name: alteredVsUnalteredMode
? AlterationEnrichmentTableColumnType.TENDENCY
: groups.length === 2
? AlterationEnrichmentTableColumnType.ENRICHED
: AlterationEnrichmentTableColumnType.MOST_ENRICHED,
render: (d: AlterationEnrichmentRow) => {
if (d.enrichedGroup === undefined || d.qValue === 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: AlterationEnrichmentRow,
filterString: string,
filterStringUpper: string
) => (d.enrichedGroup || '').toUpperCase().includes(filterStringUpper),
sortBy: (d: AlterationEnrichmentRow) => d.enrichedGroup || '-',
download: (d: AlterationEnrichmentRow) => d.enrichedGroup || '-',
tooltip: The group with the highest alteration frequency,
};
//minimum 2 group are required for enrichment analysis
if (groups.length < 2) {
return [];
}
if (groups.length === 2) {
let group1 = groups[0];
let group2 = groups[1];
columns.push({
name: AlterationEnrichmentTableColumnType.LOG_RATIO,
render: (d: AlterationEnrichmentRow) => (
{d.logRatio ? formatLogOddsRatio(d.logRatio) : '-'}
),
tooltip: (
Log2 based ratio of (pct in {group1.name}/ pct in{' '}
{group2.name})
),
sortBy: (d: AlterationEnrichmentRow) => Number(d.logRatio),
download: (d: AlterationEnrichmentRow) =>
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: AlterationEnrichmentRow) => (
{formatPercentage(group.name, d)}
),
tooltip: (
{group.name}: {group.description}
),
sortBy: (d: AlterationEnrichmentRow) =>
getAlteredCount(group.name, d),
download: (d: AlterationEnrichmentRow) =>
formatPercentage(group.name, d),
});
});
return columns;
}
export function getEnrichmentColumns(
groups: { name: string; description: string; color?: string }[],
enrichmentType: EnrichmentType,
alteredVsUnalteredMode?: boolean
): ExpressionEnrichmentTableColumn[] {
// minimum 2 group are required for enrichment analysis
if (groups.length < 2) {
return [];
}
let columns: ExpressionEnrichmentTableColumn[] = [];
const nameToGroup = _.keyBy(groups, g => g.name);
const isMethylation = enrichmentType === EnrichmentType.DNA_METHYLATION;
const typeOfEnrichment = isMethylation ? 'methylation' : 'expression';
let enrichedGroupColum: ExpressionEnrichmentTableColumn = {
name: alteredVsUnalteredMode
? ExpressionEnrichmentTableColumnType.TENDENCY
: isMethylation
? ExpressionEnrichmentTableColumnType.METHYLATION
: ExpressionEnrichmentTableColumnType.EXPRESSED,
render: (d: ExpressionEnrichmentRow) => {
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: ExpressionEnrichmentRow,
filterString: string,
filterStringUpper: string
) => d.enrichedGroup.toUpperCase().includes(filterStringUpper),
sortBy: (d: ExpressionEnrichmentRow) => d.enrichedGroup,
download: (d: ExpressionEnrichmentRow) => d.enrichedGroup,
tooltip: (
The group with the highest {typeOfEnrichment} frequency
),
};
if (groups.length === 2) {
let group1 = groups[0];
let group2 = groups[1];
columns.push({
name: ExpressionEnrichmentTableColumnType.LOG_RATIO,
render: (d: ExpressionEnrichmentRow) => (
{formatLogOddsRatio(d.logRatio!)}
),
tooltip: (
Log2 of ratio of {isMethylation ? '' : '(unlogged)'} mean in{' '}
{group1.name} to {isMethylation ? '' : '(unlogged)'} mean in{' '}
{group2.name}
),
sortBy: (d: ExpressionEnrichmentRow) => Number(d.logRatio),
download: (d: ExpressionEnrichmentRow) =>
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: (name: string) => STAT_IN_headerRender('μ', name),
render: (d: ExpressionEnrichmentRow) => (
{d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression.toFixed(2)
: Datalabel.NA}
),
tooltip: (
Mean {isMethylation ? '' : 'log2'} {typeOfEnrichment} of the
listed gene in {group.description}
),
sortBy: (d: ExpressionEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression
: null,
download: (d: ExpressionEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression.toFixed(2)
: Datalabel.NA,
uniqueName:
group.name + ExpressionEnrichmentTableColumnType.MEAN_SUFFIX,
});
columns.push({
name: group.name,
headerRender: (name: string) => STAT_IN_headerRender('σ', name),
render: (d: ExpressionEnrichmentRow) => (
{d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation.toFixed(2)
: Datalabel.NA}
),
tooltip: (
Standard deviation of {isMethylation ? '' : 'log2'}{' '}
{typeOfEnrichment} of the listed gene in {group.description}
),
sortBy: (d: ExpressionEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation
: null,
download: (d: ExpressionEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation.toFixed(2)
: Datalabel.NA,
uniqueName:
group.name +
ExpressionEnrichmentTableColumnType.STANDARD_DEVIATION_SUFFIX,
});
});
return columns;
}
export function getGenericAssayEnrichmentColumns(
groups: { name: string; description: string; color?: string }[],
alteredVsUnalteredMode?: boolean
): GenericAssayEnrichmentTableColumn[] {
// minimum 2 group are required for enrichment analysis
if (groups.length < 2) {
return [];
}
let columns: GenericAssayEnrichmentTableColumn[] = [];
const nameToGroup = _.keyBy(groups, g => g.name);
let enrichedGroupColum: GenericAssayEnrichmentTableColumn = {
name: alteredVsUnalteredMode
? GenericAssayEnrichmentTableColumnType.TENDENCY
: GenericAssayEnrichmentTableColumnType.EXPRESSED,
render: (d: GenericAssayEnrichmentRow) => {
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: GenericAssayEnrichmentRow,
filterString: string,
filterStringUpper: string
) => d.enrichedGroup.toUpperCase().includes(filterStringUpper),
sortBy: (d: GenericAssayEnrichmentRow) => d.enrichedGroup,
download: (d: GenericAssayEnrichmentRow) => d.enrichedGroup,
tooltip: The group with the highest frequency,
};
if (groups.length === 2) {
let group1 = groups[0];
let group2 = groups[1];
columns.push({
name: GenericAssayEnrichmentTableColumnType.LOG_RATIO,
render: (d: GenericAssayEnrichmentRow) => (
{formatLogOddsRatio(d.logRatio!)}
),
tooltip: (
Log2 of ratio of mean in {group1.name} to mean in{' '}
{group2.name}
),
sortBy: (d: GenericAssayEnrichmentRow) => Number(d.logRatio),
download: (d: GenericAssayEnrichmentRow) =>
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: (name: string) => STAT_IN_headerRender('μ', name),
render: (d: GenericAssayEnrichmentRow) => (
{d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression.toFixed(2)
: Datalabel.NA}
),
tooltip: (
Mean of the listed entity in {group.description}
),
sortBy: (d: GenericAssayEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression
: null,
download: (d: GenericAssayEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].meanExpression.toFixed(2)
: Datalabel.NA,
uniqueName:
group.name + GenericAssayEnrichmentTableColumnType.MEAN_SUFFIX,
});
columns.push({
name: group.name,
headerRender: (name: string) => STAT_IN_headerRender('σ', name),
render: (d: GenericAssayEnrichmentRow) => (
{d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation.toFixed(2)
: Datalabel.NA}
),
tooltip: (
Standard deviation of the listed entity in{' '}
{group.description}
),
sortBy: (d: GenericAssayEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation
: null,
download: (d: GenericAssayEnrichmentRow) =>
d.groupsSet[group.name]
? d.groupsSet[group.name].standardDeviation.toFixed(2)
: Datalabel.NA,
uniqueName:
group.name +
GenericAssayEnrichmentTableColumnType.STANDARD_DEVIATION_SUFFIX,
});
});
return columns;
}
export function getEnrichmentBarPlotData(
data: { [gene: string]: AlterationEnrichmentRow },
genes: string[]
): IMultipleCategoryBarPlotData[] {
const usedGenes: { [gene: string]: boolean } = {};
if (_.isEmpty(genes)) {
return [];
}
const groupToGeneCounts = _.reduce(
genes,
(acc, gene) => {
const datum = data[gene];
if (datum) {
_.each(datum.groupsSet, group => {
const groupName = group.name;
if (!acc[groupName]) {
acc[groupName] = {};
}
const displayedGeneName =
gene + (datum.qValue && datum.qValue < 0.05 ? '*' : '');
acc[groupName][displayedGeneName] = group.alteredPercentage;
usedGenes[displayedGeneName] = true;
});
}
return acc;
},
{} as { [group: string]: { [gene: string]: number } }
);
const geneTotalCounts: { [id: string]: number } = {};
// ensure entries for all used minor categories - we need 0 entries for those major/minor combos we didnt see
_.forEach(usedGenes, (z, gene) => {
let totalCount = 0;
_.forEach(groupToGeneCounts, geneCounts => {
geneCounts[gene] = geneCounts[gene] || 0;
totalCount += geneCounts[gene];
});
geneTotalCounts[gene] = totalCount;
});
return _.map(
groupToGeneCounts,
(geneCounts: { [gene: string]: number }, group) => {
let counts = _.map(geneCounts, (count, gene) => {
const percentage = (count / geneTotalCounts[gene]) * 100;
return {
majorCategory: gene,
count: count,
percentage: parseFloat(percentage.toFixed(2)),
};
});
return {
minorCategory: group,
counts,
};
}
);
}
export function compareByAlterationPercentage(
kv1: AlterationEnrichmentRow,
kv2: AlterationEnrichmentRow
) {
const t1 = _.reduce(
kv1.groupsSet,
(acc, next) => {
acc = next.alteredPercentage > acc ? next.alteredPercentage : acc;
return acc;
},
0
);
const t2 = _.reduce(
kv2.groupsSet,
(acc, next) => {
acc = next.alteredPercentage > acc ? next.alteredPercentage : acc;
return acc;
},
0
);
return t2 - t1;
}
export function getGeneListOptions(
data: AlterationEnrichmentRow[],
includeAlteration?: boolean
): { label: GeneOptionLabel; genes: string[] }[] {
if (_.isEmpty(data)) {
return [
{
label: GeneOptionLabel.USER_DEFINED_OPTION,
genes: [],
},
];
}
let dataWithOptionName: (AlterationEnrichmentRow & {
optionName?: string;
})[] = data;
if (includeAlteration) {
dataWithOptionName = _.map(dataWithOptionName, datum => {
return {
...datum,
optionName:
datum.hugoGeneSymbol +
`: ${CNA_TO_ALTERATION[datum.value!]}`,
};
});
}
let dataSortedByAlteredPercentage = _.clone(dataWithOptionName).sort(
compareByAlterationPercentage
);
let dataSortedByAvgFrequency = _.clone(dataWithOptionName).sort(function(
kv1,
kv2
) {
const t1 =
_.sumBy(_.values(kv1.groupsSet), count => count.alteredPercentage) /
_.keys(kv1.groupsSet).length;
const t2 =
_.sumBy(_.values(kv2.groupsSet), count => count.alteredPercentage) /
_.keys(kv2.groupsSet).length;
return t2 - t1;
});
let dataSortedBypValue = _.clone(dataWithOptionName).sort(function(
kv1,
kv2
) {
if (kv1.pValue !== undefined && kv2.pValue !== undefined) {
return 0;
}
if (kv1.pValue !== undefined) {
return 1;
}
if (kv2.pValue !== undefined) {
return -1;
}
return Number(kv1.pValue) - Number(kv2.pValue);
});
return [
{
label: GeneOptionLabel.USER_DEFINED_OPTION,
genes: [],
},
{
label: GeneOptionLabel.HIGHEST_FREQUENCY,
genes: _.map(
dataSortedByAlteredPercentage,
datum => datum.optionName || datum.hugoGeneSymbol
),
},
{
label: GeneOptionLabel.AVERAGE_FREQUENCY,
genes: _.map(
dataSortedByAvgFrequency,
datum => datum.optionName || datum.hugoGeneSymbol
),
},
{
label: GeneOptionLabel.SIGNIFICANT_P_VALUE,
genes: _.map(
dataSortedBypValue,
datum => datum.optionName || datum.hugoGeneSymbol
),
},
{
label: GeneOptionLabel.SYNC_WITH_TABLE,
genes: [],
},
];
}
export const ContinousDataPvalueTooltip: React.FunctionComponent = ({
groupSize,
}) => {
return (
{groupSize && groupSize >= PVALUE_TEST_GROUP_SIZE_THRESHOLD
? 'Derived from one-way ANOVA'
: "Derived from Student's t-test"}
);
};