b687dd9018670941ce30f8a4582d6597c5a974d8 lrnassar Tue Sep 1 14:57:43 2026 -0700 lrSv1kLin: fix 2bp insertion span, drop dead numConsolidated field, refresh lrSvAll merge. refs #38099 The Lin 1218 VCFs set INFO/END = POS+1 on insertions, and the converter took chromEnd from END, so every insertion was drawn 2bp wide with svLen 2 instead of the 1bp anchor base. That contradicted the track's own description page and the coordinate convention in the makeDoc, and it kept 107,980 Lin insertions from merging in lrSvAll. Insertions now clamp chromEnd to the anchor; deletions are unchanged and still verify span == |SVLEN| against the source VCFs. Dropped numConsolidated from the converter and the .as: the NumConsolidated INFO key is declared in the VCF header but never appears on a data line, so the column was 0 on all 1.2M rows and added a meaningless line to every detail page. Rebuilt lin1218 on hg38 and hs1 (item counts and variant names unchanged) and re-ran the merge: lrSvAll 2,963,093 -> 2,855,267 rows as the duplicate insertion rows collapse. Bumped seven filter.svLen/insLen maxima in lrSv.ra that were short of the data after the August deletion narrowing, three of them only visible on hs1. lrSvAll.html said the 1000 Genomes linear set was not in the merge, which is no longer true, and gave no warning that sourceCount double-counts because Lin1218 already absorbs HPRC, HGSVC3 and both 1KG ONT callsets. Corrected the merge key description and refreshed ten stale cells in the lrSv.html summary table. diff --git src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py index d58656e4dcb..60bd6827685 100644 --- src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py +++ src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py @@ -68,71 +68,81 @@ fields = line.rstrip("\n").split("\t") chrom = fields[0] pos = int(fields[1]) info = parseInfo(fields[7]) svType = normalizeSvType(info.get("SVTYPE", ".")) svLenRaw = toInt(info.get("SVLEN", "0")) end = toInt(info.get("END", str(pos))) chromStart = pos - 1 chromEnd = end # VCF POS is the anchor base (present in both REF and ALT); it is not # part of a deletion, so the deleted region starts one base to the # right. Drop the anchor from the left of DEL intervals so svLen == # |SVLEN| and coordinates match anchor-excluded callsets (e.g. - # HGSVC3). INS keeps the anchor-based position. + # HGSVC3). + # An insertion has no deleted span: its item is the single anchor + # base at the attach point, so span == 1. This VCF sets END = POS+1 + # on insertions, which would draw them 2 bp wide, so clamp chromEnd + # back to the anchor. That matches the other VCF-derived subtracks + # (CoLoRSdb, AoU, GA4K, CARD, gustafson, 1kgOnt, and deCODE for its + # single-base-REF records). if svType == "DEL": chromStart += 1 + elif svType in ("INS", "MEI"): + chromEnd = chromStart + 1 + # Only DEL and INS occur in either source VCF. A future INV/DUP/CPX + # would fall through to chromEnd = END with chromStart = pos-1, i.e. + # the same off-by-one just fixed for DEL, so revisit the anchor + # question per type before trusting such a record. if chromEnd <= chromStart: chromEnd = chromStart + 1 svLen = chromEnd - chromStart insLen = abs(svLenRaw) if svType in ("INS", "MEI") else 0 ac = toInt(info.get("AC", "0")) an = toInt(info.get("AN", "0")) af = toFloat(info.get("AF", "0")) afAfr = toFloat(info.get("AF_AFR", "0")) afAmr = toFloat(info.get("AF_AMR", "0")) afEas = toFloat(info.get("AF_EAS", "0")) afEur = toFloat(info.get("AF_EUR", "0")) afSas = toFloat(info.get("AF_SAS", "0")) ns = toInt(info.get("NS", "0")) - numConsolidated = toInt(info.get("NumConsolidated", "0")) color = SV_COLORS.get(svType, "100,100,100") featLen = insLen if svType in ("INS", "MEI") else svLen name = svName(svType, featLen, ac) row = [ chrom, str(chromStart), str(chromEnd), name, "0", ".", str(chromStart), str(chromEnd), color, svType, str(svLen), str(insLen), str(ac), str(an), f"{af:.6f}", f"{afAfr:.6f}", f"{afAmr:.6f}", f"{afEas:.6f}", f"{afEur:.6f}", f"{afSas:.6f}", str(ns), - str(numConsolidated), ] fOut.write("\t".join(row) + "\n") nWritten += 1 print(f"Wrote {nWritten} records to {outFile}", file=sys.stderr) if __name__ == "__main__": main()