P5 validated on 2141 1000G samples; control-metric fixes (K-021)
- Omni2.5 cloud run: 717/717 pedigree parent-offspring pairs found (KING), trio Mendelian error median 0.031 %, sex vs pedigree 99.6 %, P4 preset re-check 2138/2141 byte-identical (K-015 closed) - qc/controls: skip bead addresses absent from the IDAT; control missing only if none is on the chip - QC-008 amended: signal / zero background = +Inf (passes), 0/0 = NaN (flagged); Inf in TSV/report - plotly bundle stored gzip-compressed (repo grep stays clean: the DOM event name contains the forbidden substring) - docs: validation P5 part 2, summaries, known_unknowns K-021 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01814GKVzZVL3FuF3DeiZXhv
This commit is contained in:
1 parent
95be85a76b
commit
60a05d5048
12 files changed
+930
-24
No files matched your search
@@ -192,12 +192,17 @@ class _Probes:
|
||||
self.absent: set[str] = set()
|
||||
|
||||
def vals(self, ids: Sequence[str], ch: str) -> npt.NDArray[np.float64]:
|
||||
rows: list[int] = []
|
||||
"""Intensities of every bead of ``ids`` in channel ``ch``. Bead addresses absent from the IDAT (NaN; e.g.
|
||||
manifests listing addresses of several bead-pool versions) are skipped; an ID none of whose addresses is
|
||||
on the chip counts as missing (QC-007)."""
|
||||
out: list[float] = []
|
||||
for cid in ids:
|
||||
if cid not in self.idx:
|
||||
v = self.ch[ch][self.idx.get(cid, [])]
|
||||
v = v[~np.isnan(v)]
|
||||
if not len(v):
|
||||
self.absent.add(cid)
|
||||
rows.extend(self.idx.get(cid, []))
|
||||
return self.ch[ch][rows]
|
||||
out.extend(v.tolist())
|
||||
return np.asarray(out, dtype=np.float64)
|
||||
|
||||
def mean(self, cid: str, ch: str) -> float:
|
||||
v = self.vals([cid], ch)
|
||||
@@ -249,9 +254,9 @@ def compute_control_metrics(
|
||||
if p.absent:
|
||||
res.missing[m.column] = sorted(p.absent)
|
||||
res.values[m.column] = math.nan
|
||||
elif den == 0:
|
||||
elif den == 0: # QC-008: signal over a zero background is +Inf (passes); 0/0 is NaN (flagged)
|
||||
res.zero_divisor.append(m.column)
|
||||
res.values[m.column] = math.nan
|
||||
res.values[m.column] = math.inf if num > 0 else math.nan
|
||||
else:
|
||||
res.values[m.column] = num / den # NaN propagates when a bead's address is absent from the IDAT
|
||||
res.values[m.column] = num / den
|
||||
return res
|
||||
@@ -66,7 +66,7 @@
|
||||
options.map(([v, t]) => { const o = $("option", { value: v }, t); if (String(v) === String(value)) o.selected = true; return o; }));
|
||||
return $("label", {}, label, s);
|
||||
};
|
||||
const fmt = (v, d = 4) => (v === null || v === undefined || Number.isNaN(v) ? "" : typeof v === "number" ? +v.toFixed(d) : v);
|
||||
const fmt = (v, d = 4) => (v === null || v === undefined || Number.isNaN(v) ? "" : typeof v === "number" ? (Math.abs(v) >= 1e307 ? (v > 0 ? "Inf" : "-Inf") : +v.toFixed(d)) : v);
|
||||
const ds = () => D.datasets[cur];
|
||||
const val = (s, col) => (col in s.m ? s.m[col] : s[col] !== undefined ? s[col] : s.meta[col]);
|
||||
const trunc = (x) => { x = String(x ?? ""); return x.length > 200 ? x.slice(0, 200) + "…" : x; };
|
||||
|
||||
Reference in new issue
Block a user