Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/shared/components/oncoprint/OncoprintUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,68 @@ describe('OncoprintUtils', () => {
// then
assert.equal(info.sequenced, 1);
});

it('counts altered samples that are not profiled for a given gene', () => {
// given - SAMPLE1 and SAMPLE2 have alterations (non-empty arrays),
// but only SAMPLE1 is profiled for BRCA1
// SAMPLE3 and SAMPLE4 are profiled but not altered
const dataByCase = {
samples: {
SAMPLE1: [{} as any], // has alteration, profiled
SAMPLE2: [{} as any], // has alteration, not profiled
SAMPLE3: [], // no alteration, profiled
SAMPLE4: [], // no alteration, profiled
},
patients: {},
};
const sequencedSampleKeysByGene = {
BRCA1: ['SAMPLE1', 'SAMPLE3', 'SAMPLE4'],
};
// when
const info = alterationInfoForCaseAggregatedDataByOQLLine(
true,
{ cases: dataByCase, oql: { gene: 'BRCA1' } },
sequencedSampleKeysByGene,
{}
);
// then - SAMPLE1 is altered and sequenced, SAMPLE2 is altered but not sequenced
// SAMPLE3 and SAMPLE4 are sequenced but not altered
assert.equal(info.sequenced, 3);
assert.equal(info.alteredAndSequenced, 1);
assert.equal(info.altered, 2);
assert.equal(info.percent, '33%'); // 1 altered&sequenced / 3 sequenced = 33%
});

it('counts altered patients that are not profiled for a given gene', () => {
// given - PATIENT1 and PATIENT2 have alterations (non-empty arrays),
// but only PATIENT1 is profiled for TP53
// PATIENT3 and PATIENT4 are profiled but not altered
const dataByCase = {
samples: {},
patients: {
PATIENT1: [{} as any], // has alteration, profiled
PATIENT2: [{} as any], // has alteration, not profiled
PATIENT3: [], // no alteration, profiled
PATIENT4: [], // no alteration, profiled
},
};
const sequencedPatientKeysByGene = {
TP53: ['PATIENT1', 'PATIENT3', 'PATIENT4'],
};
// when
const info = alterationInfoForCaseAggregatedDataByOQLLine(
false,
{ cases: dataByCase, oql: { gene: 'TP53' } },
{},
sequencedPatientKeysByGene
);
// then - PATIENT1 is altered and sequenced, PATIENT2 is altered but not sequenced
// PATIENT3 and PATIENT4 are sequenced but not altered
assert.equal(info.sequenced, 3);
assert.equal(info.alteredAndSequenced, 1);
assert.equal(info.altered, 2);
assert.equal(info.percent, '33%'); // 1 altered&sequenced / 3 sequenced = 33%
});
});

describe('makeGeneticTrackWith', () => {
Expand Down
114 changes: 78 additions & 36 deletions src/shared/components/oncoprint/OncoprintUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,28 +552,54 @@ export function percentAltered(altered: number, sequenced: number) {
return fixed + '%';
}

function getAlterationInfoSequenced(
function getAlterationInfo(
sampleMode: boolean,
oql: { gene: string } | string[],
sequencedSampleKeysByGene: { [hugoGeneSymbol: string]: string[] },
sequencedPatientKeysByGene: { [hugoGeneSymbol: string]: string[] }
sequencedPatientKeysByGene: { [hugoGeneSymbol: string]: string[] },
alteredKeys: Set<string>
) {
const geneSymbolArray = oql instanceof Array ? oql : [oql.gene];
const sequenced = sampleMode
? _.uniq(
_.flatMap(
geneSymbolArray,
symbol => sequencedSampleKeysByGene[symbol]
)
).length
: _.uniq(
_.flatMap(
geneSymbolArray,
symbol => sequencedPatientKeysByGene[symbol]
)
).length;

return sequenced;
const report = {
sequenced: 0,
alteredAndSequenced: 0,
altered: alteredKeys.size,
percent: '',
};

if (sampleMode) {
const sequenced = _.uniq(
_.flatMap(
geneSymbolArray,
symbol => sequencedSampleKeysByGene[symbol]
)
);
report.sequenced = sequenced.length;
report.alteredAndSequenced = _.intersection(
sequenced,
Array.from(alteredKeys)
).length;
} else {
const sequenced = _.uniq(
_.flatMap(
geneSymbolArray,
symbol => sequencedPatientKeysByGene[symbol]
)
);
report.sequenced = sequenced.length;
report.alteredAndSequenced = _.intersection(
sequenced,
Array.from(alteredKeys)
).length;
}

report.percent = percentAltered(
report.alteredAndSequenced,
report.sequenced
);

return report;
}

export function alterationInfoForOncoprintTrackData(
Expand All @@ -585,18 +611,26 @@ export function alterationInfoForOncoprintTrackData(
sequencedSampleKeysByGene: { [hugoGeneSymbol: string]: string[] },
sequencedPatientKeysByGene: { [hugoGeneSymbol: string]: string[] }
) {
const sequenced = getAlterationInfoSequenced(
const alteredKeys = data.trackData!.reduce((acc, next) => {
if (isAltered(next)) {
acc.add(next.uid);
}
return acc;
}, new Set<string>());
Comment on lines +614 to +619
Copy link
Member

Choose a reason for hiding this comment

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

Minor thing but I find it more readable when we do filter and map instead of reduce.

Suggested change
const alteredKeys = data.trackData!.reduce((acc, next) => {
if (isAltered(next)) {
acc.add(next.uid);
}
return acc;
}, new Set<string>());
const alteredKeys = new Set<string>(data.trackData!.filter(isAltered).map(d => d.uid));


const info = getAlterationInfo(
sampleMode,
data.oql,
sequencedSampleKeysByGene,
sequencedPatientKeysByGene
sequencedPatientKeysByGene,
alteredKeys
);
const altered = _.sumBy(data.trackData!, d => +isAltered(d));
const percent = percentAltered(altered, sequenced);

return {
sequenced,
altered,
percent,
sequenced: info.sequenced,
alteredAndSequenced: info.alteredAndSequenced,
altered: info.altered,
percent: info.percent,
};
}

Expand All @@ -609,24 +643,32 @@ export function alterationInfoForCaseAggregatedDataByOQLLine(
sequencedSampleKeysByGene: { [hugoGeneSymbol: string]: string[] },
sequencedPatientKeysByGene: { [hugoGeneSymbol: string]: string[] }
) {
const sequenced = getAlterationInfoSequenced(
// obtain a list of entity ids (samples or patients)
const alteredEntities = sampleMode
? _(data.cases.samples)
.entries()
.filter(e => !!e[1].length)
.map(e => e[0])
.value()
: _(data.cases.patients)
.entries()
.filter(e => !!e[1].length)
.map(e => e[0])
.value();
Comment on lines +647 to +657
Copy link
Member

Choose a reason for hiding this comment

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

This way we would avoid the duplicate .entries().filter(...).map(...).value() part

Suggested change
const alteredEntities = sampleMode
? _(data.cases.samples)
.entries()
.filter(e => !!e[1].length)
.map(e => e[0])
.value()
: _(data.cases.patients)
.entries()
.filter(e => !!e[1].length)
.map(e => e[0])
.value();
const alteredEntities = _(sampleMode ? data.cases.samples : data.cases.patients)
.entries()
.filter(e => !!e[1].length)
.map(e => e[0])
.value()


const info = getAlterationInfo(
sampleMode,
data.oql,
sequencedSampleKeysByGene,
sequencedPatientKeysByGene
sequencedPatientKeysByGene,
new Set(alteredEntities)
);
const altered = sampleMode
? Object.keys(data.cases.samples).filter(
k => !!data.cases.samples[k].length
).length
: Object.keys(data.cases.patients).filter(
k => !!data.cases.patients[k].length
).length;
const percent = percentAltered(altered, sequenced);

return {
sequenced,
altered,
percent,
sequenced: info.sequenced,
alteredAndSequenced: info.alteredAndSequenced,
altered: info.altered,
percent: info.percent,
};
}

Expand Down