Mercurial > repos > mheinzl > fsd_regions
view fsd_regions.py @ 14:6879295d3f11 draft default tip
planemo upload for repository https://github.com/monikaheinzl/duplexanalysis_galaxy/tree/master/tools/fsd_regions commit b8a2f7b7615b2bcd3b602027af31f4e677da94f6-dirty
author | mheinzl |
---|---|
date | Tue, 08 Jan 2019 09:50:01 -0500 |
parents | 63432e6f6a61 |
children |
line wrap: on
line source
#!/usr/bin/env python # Family size distribution of tags which were aligned to the reference genome # # Author: Monika Heinzl & Gundula Povysil, Johannes-Kepler University Linz (Austria) # Contact: monika.heinzl@edumail.at # # Takes at least one TABULAR file with tags before the alignment to the SSCS, # a BAM file with tags of reads that overlap the regions of the reference genome and # an optional BED file with chromosome, start and stop position of the regions as input. # The program produces a plot which shows the distribution of family sizes of the tags from the input files and # a tabular file with the data of the plot. # USAGE: python FSD_regions.py --inputFile filenameSSCS --inputName1 filenameSSCS # --bamFile DCSbamFile --rangesFile BEDfile --output_tabular outptufile_name_tabular # --output_pdf outputfile_name_pdf import argparse import collections import re import sys import matplotlib.pyplot as plt import numpy as np import pysam from matplotlib.backends.backend_pdf import PdfPages plt.switch_backend('agg') def readFileReferenceFree(file, delim): with open(file, 'r') as dest_f: data_array = np.genfromtxt(dest_f, skip_header=0, delimiter=delim, comments='#', dtype='string') return(data_array) def make_argparser(): parser = argparse.ArgumentParser(description='Family Size Distribution of tags which were aligned to regions of the reference genome') parser.add_argument('--inputFile', help='Tabular File with three columns: ab or ba, tag and family size.') parser.add_argument('--inputName1') parser.add_argument('--bamFile', help='BAM file with aligned reads.') parser.add_argument('--rangesFile', default=None, help='BED file with chromosome, start and stop positions.') parser.add_argument('--output_pdf', default="data.pdf", type=str, help='Name of the pdf and tabular file.') parser.add_argument('--output_tabular', default="data.tabular", type=str, help='Name of the pdf and tabular file.') return parser def compare_read_families_refGenome(argv): parser = make_argparser() args = parser.parse_args(argv[1:]) firstFile = args.inputFile name1 = args.inputName1 name1 = name1.split(".tabular")[0] bamFile = args.bamFile rangesFile = args.rangesFile title_file = args.output_pdf title_file2 = args.output_tabular sep = "\t" with open(title_file2, "w") as output_file, PdfPages(title_file) as pdf: data_array = readFileReferenceFree(firstFile, "\t") pysam.index(bamFile) bam = pysam.AlignmentFile(bamFile, "rb") qname_dict = collections.OrderedDict() if rangesFile != str(None): with open(rangesFile, 'r') as regs: range_array = np.genfromtxt(regs, skip_header=0, delimiter='\t', comments='#', dtype='string') if range_array.ndim == 0: print("Error: file has 0 lines") exit(2) if range_array.ndim == 1: chrList = range_array[0] start_posList = range_array[1].astype(int) stop_posList = range_array[2].astype(int) chrList = [chrList.tolist()] start_posList = [start_posList.tolist()] stop_posList = [stop_posList.tolist()] else: chrList = range_array[:, 0] start_posList = range_array[:, 1].astype(int) stop_posList = range_array[:, 2].astype(int) if len(start_posList) != len(stop_posList): print("start_positions and end_positions do not have the same length") exit(3) chrList = np.array(chrList) start_posList = np.array(start_posList).astype(int) stop_posList = np.array(stop_posList).astype(int) for chr, start_pos, stop_pos in zip(chrList, start_posList, stop_posList): chr_start_stop = "{}_{}_{}".format(chr, start_pos, stop_pos) qname_dict[chr_start_stop] = [] for read in bam.fetch(chr.tobytes(), start_pos, stop_pos): if not read.is_unmapped: if re.search('_', read.query_name): tags = re.split('_', read.query_name)[0] else: tags = read.query_name qname_dict[chr_start_stop].append(tags) else: for read in bam.fetch(): if not read.is_unmapped: if re.search(r'_', read.query_name): tags = re.split('_', read.query_name)[0] else: tags = read.query_name if read.reference_name not in qname_dict.keys(): qname_dict[read.reference_name] = [tags] else: qname_dict[read.reference_name].append(tags) seq = np.array(data_array[:, 1]) tags = np.array(data_array[:, 2]) quant = np.array(data_array[:, 0]).astype(int) group = np.array(qname_dict.keys()) all_ab = seq[np.where(tags == "ab")[0]] all_ba = seq[np.where(tags == "ba")[0]] quant_ab = quant[np.where(tags == "ab")[0]] quant_ba = quant[np.where(tags == "ba")[0]] seqDic_ab = dict(zip(all_ab, quant_ab)) seqDic_ba = dict(zip(all_ba, quant_ba)) lst_ab = [] lst_ba = [] quantAfterRegion = [] for i in group: lst_ab_r = [] lst_ba_r = [] seq_mut = qname_dict[i] if rangesFile == str(None): seq_mut, seqMut_index = np.unique(np.array(seq_mut), return_index=True) for r in seq_mut: if re.search('\.', r): # BAM file with SSCS tags splitted_tag = re.split('\.', r)[0] direction = re.split('\.', r)[1] if direction == "ab": count_ab = seqDic_ab.get(splitted_tag) lst_ab_r.append(count_ab) lst_ab.append(count_ab) else: count_ba = seqDic_ba.get(splitted_tag) lst_ba_r.append(count_ba) lst_ba.append(count_ba) else: # BAM file with DCS tags count_ab = seqDic_ab.get(r) lst_ab_r.append(count_ab) lst_ab.append(count_ab) count_ba = seqDic_ba.get(r) lst_ba_r.append(count_ba) lst_ba.append(count_ba) dataAB = np.array(lst_ab_r) dataBA = np.array(lst_ba_r) bigFamilies = np.where(dataAB > 20)[0] dataAB[bigFamilies] = 22 bigFamilies = np.where(dataBA > 20)[0] dataBA[bigFamilies] = 22 quantAll = np.concatenate((dataAB, dataBA)) quantAfterRegion.append(quantAll) quant_ab = np.array(lst_ab) quant_ba = np.array(lst_ba) length_regions = len(np.concatenate(quantAfterRegion)) maximumX = np.amax(np.concatenate(quantAfterRegion)) minimumX = np.amin(np.concatenate(quantAfterRegion)) # PLOT plt.rc('figure', figsize=(11.69, 8.27)) # A4 format plt.rcParams['axes.facecolor'] = "E0E0E0" # grey background color plt.rcParams['xtick.labelsize'] = 14 plt.rcParams['ytick.labelsize'] = 14 plt.rcParams['patch.edgecolor'] = "black" fig = plt.figure() plt.subplots_adjust(bottom=0.3) colors = ["#6E6E6E", "#0431B4", "#5FB404", "#B40431", "#F4FA58", "#DF7401", "#81DAF5"] col = [] for i in range(0, len(group)): col.append(colors[i]) counts = plt.hist(quantAfterRegion, bins=range(minimumX, maximumX + 1), stacked=False, label=group, align="left", alpha=1, color=col, edgecolor="black", linewidth=1) ticks = np.arange(minimumX - 1, maximumX, 1) ticks1 = map(str, ticks) ticks1[len(ticks1) - 1] = ">20" plt.xticks(np.array(ticks), ticks1) count = np.bincount(map(int, quant_ab)) # original counts legend = "max. family size:\nabsolute frequency:" \ "\nrelative frequency:\n\ntotal nr. of reads:\n(before SSCS building)" plt.text(0.15, 0.085, legend, size=11, transform=plt.gcf().transFigure) legend = "AB\n{}\n{}\n{:.5f}\n\n{:,}".format(max(map(int, quant_ab)), count[len(count) - 1], float(count[len(count) - 1]) / sum(count), sum(np.array(data_array[:, 0]).astype(int))) plt.text(0.35, 0.105, legend, size=11, transform=plt.gcf().transFigure) count2 = np.bincount(map(int, quant_ba)) # original counts legend = "BA\n{}\n{}\n{:.5f}" \ .format(max(map(int, quant_ba)), count2[len(count2) - 1], float(count2[len(count2) - 1]) / sum(count2)) plt.text(0.45, 0.1475, legend, size=11, transform=plt.gcf().transFigure) plt.text(0.53, 0.2125, "total nr. of tags:", size=11, transform=plt.gcf().transFigure) if re.search('\.', r): # BAM file with SSCS tags plt.text(0.85, 0.2125, "{:,}".format(length_regions), size=11, transform=plt.gcf().transFigure) else: plt.text(0.85, 0.2125, "{:,} ({:,})".format(length_regions, length_regions / 2), size=11, transform=plt.gcf().transFigure) legend4 = "* In the plot, both family sizes of the ab and ba strands were used." \ "\nWhereas the total numbers indicate only the single count of the tags per region.\n" plt.text(0.1, 0.01, legend4, size=11, transform=plt.gcf().transFigure) space = 0 for i, count in zip(group, quantAfterRegion): plt.text(0.53, 0.15 - space, "{}:\n".format(i), size=11, transform=plt.gcf().transFigure) if re.search('\.', r): # BAM file with SSCS tags plt.text(0.85, 0.15 - space, "{:,}\n".format(len(count)), size=11, transform=plt.gcf().transFigure) else: plt.text(0.85, 0.15 - space, "{:,}\n".format(len(count) / 2), size=11, transform=plt.gcf().transFigure) space = space + 0.02 plt.legend(loc='upper right', fontsize=14, bbox_to_anchor=(0.9, 1), frameon=True) plt.xlabel("Family size", fontsize=14) plt.ylabel("Absolute Frequency", fontsize=14) plt.grid(b=True, which="major", color="#424242", linestyle=":") plt.margins(0.01, None) pdf.savefig(fig, bbox_inch="tight") plt.close() output_file.write("Dataset:{}{}\n".format(sep, name1)) output_file.write("{}AB{}BA\n".format(sep, sep)) output_file.write("max. family size:{}{}{}{}\n".format(sep, max(map(int, quant_ab)), sep, max(map(int, quant_ba)))) output_file.write("absolute frequency:{}{}{}{}\n".format(sep, count[len(count) - 1], sep, count2[len(count2) - 1])) output_file.write("relative frequency:{}{:.3f}{}{:.3f}\n\n".format(sep, float(count[len(count) - 1]) / sum(count), sep, float(count2[len(count2) - 1]) / sum(count2))) output_file.write("total nr. of reads{}{}\n".format(sep, sum(np.array(data_array[:, 0]).astype(int)))) if re.search('\.', r): # BAM file with SSCS tags output_file.write("total nr. of tags{}{}\n".format(sep, length_regions)) else: output_file.write("total nr. of tags{}{} ({})\n".format(sep, length_regions, length_regions / 2)) output_file.write("\n\nValues from family size distribution\n") output_file.write("{}".format(sep)) for i in group: output_file.write("{}{}".format(i, sep)) output_file.write("\n") j = 0 for fs in counts[1][0:len(counts[1]) - 1]: if fs == 21: fs = ">20" else: fs = "={}".format(fs) output_file.write("FS{}{}".format(fs, sep)) if len(group) == 1: output_file.write("{}{}".format(int(counts[0][j]), sep)) else: for n in range(len(group)): output_file.write("{}{}".format(int(counts[0][n][j]), sep)) output_file.write("\n") j += 1 output_file.write("sum{}".format(sep)) if len(group) == 1: output_file.write("{}{}".format(int(sum(counts[0])), sep)) else: for i in counts[0]: output_file.write("{}{}".format(int(sum(i)), sep)) output_file.write("\n") if re.search('\.', r): # BAM file with SSCS tags output_file.write("\n") else: output_file.write("\n\nIn the plot, both family sizes of the ab and ba strands were used." "\nWhereas the total numbers indicate only the single count of the tags per region.\n") output_file.write("Region{}total nr. of tags per region\n".format(sep)) for i, count in zip(group, quantAfterRegion): if re.search('\.', r): # BAM file with SSCS tags output_file.write("{}{}{}\n".format(i, sep, len(count))) else: output_file.write("{}{}{}\n".format(i, sep, len(count) / 2)) print("Files successfully created!") if __name__ == '__main__': sys.exit(compare_read_families_refGenome(sys.argv))