Skip to content

Commit e43caf9

Browse files
committed
MAPQ undefined being 255 handling
1 parent 4f0107b commit e43caf9

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,60 @@ import { toArray } from 'rxjs/operators'
33

44
import Adapter from './PAFAdapter.ts'
55
import MyConfigSchema from './configSchema.ts'
6+
import { getWeightedMeans } from './util.ts'
7+
8+
import type { PAFRecord } from './util.ts'
9+
10+
function makeRecord(
11+
qname: string,
12+
tname: string,
13+
mappingQual: number | undefined,
14+
blockLen: number,
15+
): PAFRecord {
16+
return {
17+
qname,
18+
qstart: 0,
19+
qend: blockLen,
20+
tname,
21+
tstart: 0,
22+
tend: blockLen,
23+
strand: 1,
24+
extra: { mappingQual, blockLen },
25+
}
26+
}
27+
28+
test('getWeightedMeans treats MAPQ 255 as missing, not as quality 255', () => {
29+
const records = [
30+
makeRecord('q1', 't1', 255, 1000),
31+
makeRecord('q1', 't1', 60, 1000),
32+
]
33+
getWeightedMeans(records)
34+
// If 255 were used as-is the mean would be (255+60)/2 = 157.5, so min=max=157.5, meanScore=0.5.
35+
// With the fix, 255 → 1, so mean = (1+60)/2 = 30.5, still min=max=30.5, meanScore=0.5.
36+
// The key assertion is that the record with MAPQ 255 doesn't skew the result toward 255.
37+
expect(records[0]!.extra.meanScore).toBe(0.5)
38+
expect(records[1]!.extra.meanScore).toBe(0.5)
39+
})
40+
41+
test('getWeightedMeans returns 0.5 when all pairs have identical quality', () => {
42+
const records = [
43+
makeRecord('q1', 't1', 60, 1000),
44+
makeRecord('q2', 't2', 60, 500),
45+
]
46+
getWeightedMeans(records)
47+
expect(records[0]!.extra.meanScore).toBe(0.5)
48+
expect(records[1]!.extra.meanScore).toBe(0.5)
49+
})
50+
51+
test('getWeightedMeans normalizes scores correctly across different quality pairs', () => {
52+
const records = [
53+
makeRecord('q1', 't1', 60, 1000),
54+
makeRecord('q2', 't2', 20, 1000),
55+
]
56+
getWeightedMeans(records)
57+
expect(records[0]!.extra.meanScore).toBe(1)
58+
expect(records[1]!.extra.meanScore).toBe(0)
59+
})
660

761
function makeAdapter() {
862
return new Adapter(

plugins/comparative-adapters/src/PAFAdapter/util.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export function getWeightedMeans(ret: PAFRecord[]) {
5959
const scoreMap: Record<string, { valueSum: number; weightSum: number }> = {}
6060
for (const entry of ret) {
6161
const key = `${entry.qname}-${entry.tname}`
62-
const qual = entry.extra.mappingQual ?? 1
62+
// MAPQ 255 is the PAF spec sentinel for "not computed" — treat as missing,
63+
// not as a high-quality signal (which would massively upweight such records).
64+
const mapq = entry.extra.mappingQual
65+
const qual = mapq !== undefined && mapq !== 255 ? mapq : 1
6366
const len = entry.extra.blockLen ?? 1
6467
const existing = scoreMap[key]
6568
if (existing) {
@@ -86,7 +89,9 @@ export function getWeightedMeans(ret: PAFRecord[]) {
8689
for (const entry of ret) {
8790
const key = `${entry.qname}-${entry.tname}`
8891
const score = meanScoreMap[key]!
89-
entry.extra.meanScore = range > 0 ? (score - min) / range : 1
92+
// When all pairs have identical quality (range === 0), use 0.5 rather than
93+
// 1 so the encoding reads as "neutral" instead of "maximum quality."
94+
entry.extra.meanScore = range > 0 ? (score - min) / range : 0.5
9095
}
9196

9297
return ret

0 commit comments

Comments
 (0)