mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 12:13:33 +08:00
193 lines
7.2 KiB
Python
193 lines
7.2 KiB
Python
# Dan Blankenberg
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
from optparse import OptionParser
|
|
|
|
import bx.align.maf
|
|
import galaxy_utils.sequence.vcf
|
|
from six import Iterator
|
|
|
|
UNKNOWN_NUCLEOTIDE = "*"
|
|
|
|
|
|
class PopulationVCFParser(Iterator):
|
|
def __init__(self, reader, name):
|
|
self.reader = iter(reader)
|
|
self.name = name
|
|
self.counter = 0
|
|
|
|
def __iter__(self):
|
|
for vc in self.reader:
|
|
rval = []
|
|
for i, allele in enumerate(vc.alt):
|
|
rval.append(("%s_%i.%i" % (self.name, i + 1, self.counter + 1), allele))
|
|
self.counter += 1
|
|
yield (vc, rval)
|
|
|
|
|
|
class SampleVCFParser(Iterator):
|
|
def __init__(self, reader):
|
|
self.reader = iter(reader)
|
|
self.counter = 0
|
|
|
|
def __iter__(self):
|
|
for vc in self.reader:
|
|
rval = []
|
|
alleles = [vc.ref] + vc.alt
|
|
|
|
if "GT" in vc.format:
|
|
gt_index = vc.format.index("GT")
|
|
for sample_name, sample_value in zip(vc.sample_names, vc.sample_values):
|
|
gt_indexes = []
|
|
for i in (
|
|
sample_value[gt_index].replace("|", "/").replace("\\", "/").split("/")
|
|
): # Do we need to consider phase here?
|
|
try:
|
|
gt_indexes.append(int(i))
|
|
except Exception:
|
|
gt_indexes.append(None)
|
|
for i, allele_i in enumerate(gt_indexes):
|
|
if allele_i is not None:
|
|
rval.append(("%s_%i.%i" % (sample_name, i + 1, self.counter + 1), alleles[allele_i]))
|
|
self.counter += 1
|
|
yield (vc, rval)
|
|
|
|
|
|
def main():
|
|
usage = "usage: %prog [options] output_file dbkey inputfile pop_name"
|
|
parser = OptionParser(usage=usage)
|
|
parser.add_option(
|
|
"-p",
|
|
"--population",
|
|
action="store_true",
|
|
dest="population",
|
|
default=False,
|
|
help="Create MAF on a per population basis",
|
|
)
|
|
parser.add_option(
|
|
"-s", "--sample", action="store_true", dest="sample", default=False, help="Create MAF on a per sample basis"
|
|
)
|
|
parser.add_option("-n", "--name", dest="name", default="Unknown Custom Track", help="Name for Custom Track")
|
|
parser.add_option(
|
|
"-g",
|
|
"--galaxy",
|
|
action="store_true",
|
|
dest="galaxy",
|
|
default=False,
|
|
help="Tool is being executed by Galaxy (adds extra error messaging).",
|
|
)
|
|
(options, args) = parser.parse_args()
|
|
|
|
if len(args) < 3:
|
|
if options.galaxy:
|
|
print(
|
|
"It appears that you forgot to specify an input VCF file, click 'Add new VCF...' to add at least input.\n",
|
|
file=sys.stderr,
|
|
)
|
|
parser.error("Need to specify an output file, a dbkey and at least one input file")
|
|
|
|
if not (options.population ^ options.sample):
|
|
parser.error("You must specify either a per population conversion or a per sample conversion, but not both")
|
|
|
|
out = open(args.pop(0), "w")
|
|
out.write('track name="%s" visibility=pack\n' % options.name.replace('"', "'"))
|
|
|
|
maf_writer = bx.align.maf.Writer(out)
|
|
|
|
dbkey = args.pop(0)
|
|
|
|
vcf_files = []
|
|
if options.population:
|
|
i = 0
|
|
while args:
|
|
filename = args.pop(0)
|
|
pop_name = args.pop(0).replace(" ", "_")
|
|
if not pop_name:
|
|
pop_name = "population_%i" % (i + 1)
|
|
vcf_files.append(PopulationVCFParser(galaxy_utils.sequence.vcf.Reader(open(filename)), pop_name))
|
|
i += 1
|
|
else:
|
|
while args:
|
|
filename = args.pop(0)
|
|
vcf_files.append(SampleVCFParser(galaxy_utils.sequence.vcf.Reader(open(filename))))
|
|
|
|
non_spec_skipped = 0
|
|
for vcf_file in vcf_files:
|
|
for vc, variants in vcf_file:
|
|
num_ins = 0
|
|
num_dels = 0
|
|
for _variant_name, variant_text in variants:
|
|
if "D" in variant_text:
|
|
num_dels = max(num_dels, int(variant_text[1:]))
|
|
elif "I" in variant_text:
|
|
num_ins = max(num_ins, len(variant_text) - 1)
|
|
|
|
alignment = bx.align.maf.Alignment()
|
|
ref_text = vc.ref + "-" * num_ins + UNKNOWN_NUCLEOTIDE * (num_dels - len(vc.ref))
|
|
start_pos = vc.pos - 1
|
|
if num_dels and start_pos:
|
|
ref_text = UNKNOWN_NUCLEOTIDE + ref_text
|
|
start_pos -= 1
|
|
alignment.add_component(
|
|
bx.align.maf.Component(
|
|
src="%s.%s%s" % (dbkey, ("chr" if not vc.chrom.startswith("chr") else ""), vc.chrom),
|
|
start=start_pos,
|
|
size=len(ref_text.replace("-", "")),
|
|
strand="+",
|
|
src_size=start_pos + len(ref_text),
|
|
text=ref_text,
|
|
)
|
|
)
|
|
for variant_name, variant_text in variants:
|
|
# FIXME:
|
|
# skip non-spec. compliant data, see: http://1000genomes.org/wiki/doku.php?id=1000_genomes:analysis:vcf3.3 for format spec
|
|
# this check is due to data having indels not represented in the published format spec,
|
|
# e.g. 1000 genomes pilot 1 indel data: ftp://ftp-trace.ncbi.nih.gov/1000genomes/ftp/pilot_data/release/2010_03/pilot1/indels/CEU.SRP000031.2010_03.indels.sites.vcf.gz
|
|
if variant_text and variant_text[0] in ["-", "+"]:
|
|
non_spec_skipped += 1
|
|
continue
|
|
|
|
# do we need a left padding unknown nucleotide (do we have deletions)?
|
|
if num_dels and start_pos:
|
|
var_text = UNKNOWN_NUCLEOTIDE
|
|
else:
|
|
var_text = ""
|
|
if "D" in variant_text:
|
|
cur_num_del = int(variant_text[1:])
|
|
pre_del = min(len(vc.ref), cur_num_del)
|
|
post_del = cur_num_del - pre_del
|
|
var_text = var_text + "-" * pre_del + "-" * num_ins + "-" * post_del
|
|
var_text = var_text + UNKNOWN_NUCLEOTIDE * (len(ref_text) - len(var_text))
|
|
elif "I" in variant_text:
|
|
cur_num_ins = len(variant_text) - 1
|
|
var_text = (
|
|
var_text
|
|
+ vc.ref
|
|
+ variant_text[1:]
|
|
+ "-" * (num_ins - cur_num_ins)
|
|
+ UNKNOWN_NUCLEOTIDE * max(0, (num_dels - 1))
|
|
)
|
|
else:
|
|
var_text = var_text + variant_text + "-" * num_ins + UNKNOWN_NUCLEOTIDE * (num_dels - len(vc.ref))
|
|
alignment.add_component(
|
|
bx.align.maf.Component(
|
|
src=variant_name,
|
|
start=0,
|
|
size=len(var_text.replace("-", "")),
|
|
strand="+",
|
|
src_size=len(var_text.replace("-", "")),
|
|
text=var_text,
|
|
)
|
|
)
|
|
maf_writer.write(alignment)
|
|
|
|
maf_writer.close()
|
|
|
|
if non_spec_skipped:
|
|
print("Skipped %i non-specification compliant indels." % non_spec_skipped)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|