I am answering question 1. However, as I am unfamiliar with python, I am providing a javascript solution only to show the basic logic. To try the code at the end, you need to install node.js and run it with node this-script.js
.
Given that this is a very standard analysis
I tend to believe this is a very rare analysis. I have only seen it done twice. Converting CIGAR+MD to padded alignment is complicated in coding, as you have to keep sequence, cigar and MD in sync all the time. The code below generates two strings in one go. It may be logically easier to generate the reference and the query strings separately.
In general, MD is ill defined. It is too difficult to work with. If possible, avoid this tag. You can do a lot of things with the combination of CIGAR and NM.
function md2pad(CIGAR, MD, SEQ) {
var m, re_cigar = /(\d+)([MIDSHNX=])/g, re_MD = /(\d+)|(\^[A-Za-z]+)|([A-Za-z])/g;
var cigar = [];
while ((m = re_cigar.exec(CIGAR)) != null) // parse CIGAR into an array
if (m[2] != 'H') // do nothing for hard clipping, as it has no effect
cigar.push([parseInt(m[1]), m[2]]);
var k = 0, sx = '', sy = ''; // k: k-th cigar operator; x: ref; y: query
// cx/cy: start coordinate of cigar[k] on ref/query; mx/my: start of current MD op
var cx = 0, cy = 0, mx = 0, my = 0;
while ((m = re_MD.exec(MD)) != null) {
if (m[2] != null) { // deletion from the reference
var len = m[2].length - 1; // length of deletion
sx += m[2].substr(1), sy += Array(len+1).join("-");
mx += len, cx += len, ++k; // consume a deletion D from ref
} else { // copy or mismatch
var ml = m[1] != null? parseInt(m[1]) : 1; // length of this MD operation
while (k < cigar.length && cigar[k][1] != 'D') {
var cl = cigar[k][0], op = cigar[k][1];
if (op == 'M' || op == 'X' || op == '=') {
if (my + ml < cy + cl) { // an MD ends in the middle of cigar M
if (ml > 0) {
if (m[3] != null) sx += m[3], sy += SEQ[my];
else sx += SEQ.substr(my, ml), sy += SEQ.substr(my, ml);
}
mx += ml, my += ml, ml = 0;
break;
} else {
var dl = cy + cl - my;
sx += SEQ.substr(my, dl), sy += SEQ.substr(my, dl);
cx += cl, cy += cl, ++k; // consume an M operator
mx += dl, my += dl, ml -= dl;
}
} else if (op == 'I') {
sy += SEQ.substr(cy, cl), sx += Array(cl+1).join("-");
cy += cl, my += cl, ++k; // consume an insertion I to ref
} else if (op == 'S') {
cy += cl, my += cl, ++k; // consume a soft-clipping S
} else throw Error("inconsistent MD"); // not working with N
}
if (ml != 0) throw Error("inconsistent MD");
}
}
if (cx != mx || cy != my) throw Error("inconsistent MD");
return [sx, sy];
}
var cigar = '16025H3M2I55M1D108M306H';
var MD = '7T15C24T9^C0G2T6T16T3A1A7G21T17G6C1T6T5A4';
var seq = 'GATCACAGGCCTATCACCCTATTAATCACTCACGGGAGCTCTCCATGCATCTGGTATTTTTTCGGGGGGGGATGCACGCGATAGCATCGCGGGCCGCTGGAACCGGAGCACCCTATGTCGCAGGATCTGTCTTTGATTCCTACCTCATGCCATTATTAATCGCGCCTA';
var s = md2pad(cigar, MD, seq);
console.log(s[0]);
console.log(s[1]);
echo 78M2I10M5D30M|perl -ne 's/(\d+)([ID])/$h{$2}+=$1/eg; print "$h{I}\t$h{D}\n"'
. Someone will probably post python answers. $\endgroup$