Skip to content

Commit

Permalink
Mutational count plot for mutational signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
TJMKuijpers committed Mar 9, 2023
1 parent bcd344b commit 3da7e1d
Show file tree
Hide file tree
Showing 11 changed files with 953 additions and 38 deletions.
4 changes: 4 additions & 0 deletions src/pages/patientView/PatientViewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ export class PatientViewPageInner extends React.Component<
onMutationalSignatureVersionChange(version: string) {
this.pageStore.setMutationalSignaturesVersion(version);
}
@action.bound
onSampleIdChange(sample: string) {
this.pageStore.setSampleMutationalSignatureData(sample);
}

@computed get columns(): ExtendedMutationTableColumnType[] {
const namespaceColumnNames = extractColumnNames(
Expand Down
14 changes: 13 additions & 1 deletion src/pages/patientView/PatientViewPageTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ export function tabs(
sampleManager: SampleManager | null
) {
const tabs: JSX.Element[] = [];

tabs.push(
<MSKTab key={0} id={PatientViewPageTabs.Summary} linkText="Summary">
<LoadingIndicator
Expand Down Expand Up @@ -631,6 +630,19 @@ export function tabs(
pageComponent.patientViewPageStore
.selectedMutationalSignatureVersion
}
dataCount={
pageComponent.patientViewPageStore
.mutationalSignatureCountDataGroupedByVersion.result
}
sample={
pageComponent.patientViewPageStore
.selectedSampleMutationalSignatureData
}
samples={
pageComponent.patientViewPageStore
.samplesWithCountDataAvailable
}
onSampleChange={pageComponent.onSampleIdChange}
/>
</MSKTab>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const sampleMutationalSignatureMeta = [
mutationalSignatureId: 'firstMutationalSignature',
name: 'Mutational Signature 1',
description: 'Mutational Signature 1',
url: 'url 1',
url: 'COSMIC/FakeMutationalSignature1',
category: 'category 1',
confidenceStatement:
'Signature 1, the aging signature, is detected in this case.',
Expand All @@ -21,7 +21,7 @@ const sampleMutationalSignatureMeta = [
mutationalSignatureId: 'secondMutationalSignature',
name: 'Mutational Signature 2',
description: 'Mutational Signature 2',
url: 'url 2',
url: 'COSMIC/FakeMutationalSignature2',
category: 'category 2',
confidenceStatement:
'Signature 2, the APOBEC signature, is detected in this case. This signature often coccurs with signature 13, the other APOBEC signature',
Expand Down Expand Up @@ -89,6 +89,7 @@ describe('ClinicalInformationMutationalSignatureTable', () => {
confidence: 0.8,
},
},
url: 'COSMIC/FakeMutationalSignature1',
},
{
name: 'Mutational Signature 2',
Expand All @@ -98,6 +99,7 @@ describe('ClinicalInformationMutationalSignatureTable', () => {
confidence: 0.4,
},
},
url: 'COSMIC/FakeMutationalSignature2',
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { IMutationalSignature } from '../../../shared/model/MutationalSignature'
import { getMutationalSignaturePercentage } from '../../../shared/lib/FormatUtils';
import _ from 'lodash';
import { observer } from 'mobx-react';
import { computed, makeObservable } from 'mobx';
import { action, computed, makeObservable, observable } from 'mobx';
import { MUTATIONAL_SIGNATURES_SIGNIFICANT_PVALUE_THRESHOLD } from 'shared/lib/GenericAssayUtils/MutationalSignaturesUtils';

export interface IClinicalInformationMutationalSignatureTableProps {
data: IMutationalSignature[];
parentCallback: (childData: string, visibility: boolean) => void;
}

class MutationalSignatureTable extends LazyMobXTable<IMutationalSignatureRow> {}
Expand All @@ -26,13 +27,13 @@ interface IMutationalSignatureRow {
confidence: number;
};
};
url: string;
}

export function prepareMutationalSignatureDataForTable(
mutationalSignatureData: IMutationalSignature[]
): IMutationalSignatureRow[] {
const tableData: IMutationalSignatureRow[] = [];

//group data by mutational signature
//[{id: mutationalsignatureid, samples: [{}, {}]}]
let sampleInvertedDataByMutationalSignature: Array<any> = _(
Expand All @@ -44,15 +45,17 @@ export function prepareMutationalSignatureDataForTable(
.map((mutationalSignatureSampleData, name) => ({
name,
samples: mutationalSignatureSampleData,
url: mutationalSignatureSampleData[0].meta.url,
}))
.value();

for (const mutationalSignature of sampleInvertedDataByMutationalSignature) {
let mutationalSignatureRowForTable: IMutationalSignatureRow = {
name: '',
sampleValues: {},
url: '',
};
mutationalSignatureRowForTable.name = mutationalSignature.name;
mutationalSignatureRowForTable.url = mutationalSignature.url;
for (const sample of mutationalSignature.samples) {
mutationalSignatureRowForTable.sampleValues[sample.sampleId] = {
value: sample.value,
Expand All @@ -63,17 +66,28 @@ export function prepareMutationalSignatureDataForTable(
}
return tableData;
}

@observer
export default class ClinicalInformationMutationalSignatureTable extends React.Component<
IClinicalInformationMutationalSignatureTableProps,
{}
> {
@observable selectedSignature = '';
sendData = () => {
this.props.parentCallback(this.selectedSignature, true);
};

constructor(props: IClinicalInformationMutationalSignatureTableProps) {
super(props);
makeObservable(this);
}

@action.bound getMutationalSignatureProfileData(
e: React.MouseEvent<Element, MouseEvent>
): void {
this.selectedSignature = e.currentTarget.innerHTML;
this.sendData();
}

@computed get uniqueSamples() {
return _.map(_.uniqBy(this.props.data, 'sampleId'), uniqSample => ({
id: uniqSample.sampleId,
Expand All @@ -83,13 +97,21 @@ export default class ClinicalInformationMutationalSignatureTable extends React.C
@computed get tableData() {
return prepareMutationalSignatureDataForTable(this.props.data);
}

readonly firstCol = 'name';
@computed get columns(): Column<IMutationalSignatureRow>[] {
return [
{
name: 'Mutational Signature',
render: (data: IMutationalSignatureRow) => (
<span>{data[this.firstCol]}</span>
<span
onClick={this.getMutationalSignatureProfileData.bind(
data
)}
id={'spanSignatureName'}
>
{data[this.firstCol]}
</span>
),
download: (data: IMutationalSignatureRow) =>
`${data[this.firstCol]}`,
Expand All @@ -109,6 +131,8 @@ export default class ClinicalInformationMutationalSignatureTable extends React.C
render: (data: IMutationalSignatureRow) =>
data.sampleValues[col.id].confidence <
MUTATIONAL_SIGNATURES_SIGNIFICANT_PVALUE_THRESHOLD ? ( //if it's a significant signature, bold the contribution
// Based on significant pvalue the span is created with style.mutationalSignatureValue for bold (sign)
// or normal styling (not signficant)
<span className={styles.mutationalSignatureValue}>
{getMutationalSignaturePercentage(
data.sampleValues[col.id].value
Expand Down
135 changes: 135 additions & 0 deletions src/pages/patientView/clinicalInformation/PatientViewPageStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ import {
import {
IMutationalSignature,
IMutationalSignatureMeta,
IMutationalCounts,
} from 'shared/model/MutationalSignature';
import { GenericAssayTypeConstants } from 'shared/lib/GenericAssayUtils/GenericAssayConfig';

Expand Down Expand Up @@ -702,6 +703,106 @@ export class PatientViewPageStore {
[]
);

readonly fetchAllMutationalSignatureCountMetaData = remoteData({
await: () => [this.fetchAllMutationalSignatureData],
invoke: async () => {
const mutationalSignatureCountStableIds = _.chain(
this.fetchAllMutationalSignatureData.result
)
.map((data: GenericAssayData) => data.stableId)
.uniq()
.filter(stableId =>
stableId.includes(
MutationalSignatureStableIdKeyWord.MutationalSignatureCountKeyWord
)
)
.value();
if (mutationalSignatureCountStableIds.length > 0) {
return client.fetchGenericAssayMetaUsingPOST({
genericAssayMetaFilter: {
genericAssayStableIds: mutationalSignatureCountStableIds,
} as GenericAssayMetaFilter,
});
} else {
return Promise.resolve([]);
}
},
});
readonly mutationalSignatureCountDataGroupedByVersion = remoteData(
{
await: () => [
this.fetchAllMutationalSignatureData,
this.mutationData,
this.fetchAllMutationalSignatureCountMetaData,
],
invoke: async () => {
const countData = this.fetchAllMutationalSignatureData.result.filter(
data =>
data.molecularProfileId.includes(
MutationalSignatureStableIdKeyWord.MutationalSignatureCountKeyWord
)
);
let signatureLabelMap = this.fetchAllMutationalSignatureCountMetaData.result!.map(
(metaData: GenericAssayMeta) => {
const nameSig: string =
'MUTATION_TYPE' in
metaData.genericEntityMetaProperties
? metaData.genericEntityMetaProperties[
'MUTATION_TYPE'
]
: '';
const classSig: string =
'MUTATION_CLASS' in
metaData.genericEntityMetaProperties
? metaData.genericEntityMetaProperties[
'MUTATION_CLASS'
]
: '';
const mutNameSig: string =
'NAME' in metaData.genericEntityMetaProperties
? metaData.genericEntityMetaProperties['NAME']
: '';
const signatureId = metaData.stableId;
return {
stableId: signatureId,
signatureLabel: nameSig,
signatureClass: classSig,
name: mutNameSig,
};
}
);
const result: IMutationalCounts[] = [];
// only loop the contribution data then find and fill in the paired confidence data
if (countData && countData.length > 0) {
for (const count of countData) {
let mutationalSignatureChartData: IMutationalCounts = {} as IMutationalCounts;
mutationalSignatureChartData.patientId =
count.patientId;
mutationalSignatureChartData.sampleId = count.sampleId;
mutationalSignatureChartData.studyId = count.studyId;
mutationalSignatureChartData.uniquePatientKey =
count.uniquePatientKey;
mutationalSignatureChartData.uniqueSampleKey =
count.uniqueSampleKey;
mutationalSignatureChartData.version =
_.last(count.molecularProfileId.split('_')) || '';
mutationalSignatureChartData.count = parseFloat(
count.value
);
mutationalSignatureChartData.mutationalSignatureLabel =
signatureLabelMap
.filter(obj => obj.stableId === count.stableId)
.map(obj => obj.name)[0] || '';

result.push(mutationalSignatureChartData);
}
}
return Promise.resolve(_.groupBy(result, data => data.version));
},
},
{}
);

readonly mutationalSignatureMetaGroupByStableId = remoteData<{
[stableId: string]: IMutationalSignatureMeta;
}>({
Expand Down Expand Up @@ -815,6 +916,40 @@ export class PatientViewPageStore {
[]
);

@observable _selectedSampleIdMutationalSignatureData: string;
@action
setSampleMutationalSignatureData(sample: string) {
this._selectedSampleIdMutationalSignatureData = sample;
}
@computed get selectedSampleMutationalSignatureData() {
return (
this._selectedSampleIdMutationalSignatureData ||
this.fetchAllMutationalSignatureData.result
.filter(data =>
data.molecularProfileId.includes(
MutationalSignatureStableIdKeyWord.MutationalSignatureCountKeyWord
)
)
.map(sample => sample.sampleId)[0]
);
}
@computed get selectedSampleUniqueKeyMutationalSignatureData() {
const sampleToFilter = this.selectedSampleMutationalSignatureData;
return this.samplesWithUniqueKeys.result
.filter(item => item.sampleId === sampleToFilter)
.map(item => item.uniqueSampleKey);
}
@computed get samplesWithCountDataAvailable(): string[] {
return this.fetchAllMutationalSignatureData.result
.filter(data =>
data.molecularProfileId.includes(
MutationalSignatureStableIdKeyWord.MutationalSignatureCountKeyWord
)
)
.map(sample => sample.sampleId)
.filter((value, index, self) => self.indexOf(value) === index);
}

readonly samplesWithoutCancerTypeClinicalData = remoteData(
{
await: () => [this.samples, this.clinicalDataForSamples],
Expand Down
Loading

0 comments on commit 3da7e1d

Please sign in to comment.