Source code for rnftools.lavender.Bam

# -*- coding: utf-8 -*-

import rnftools.lavender
import rnftools.utils

import gzip
import os
import pysam
import re
import textwrap

import bs4

from svg42pdf import svg42pdf


###########
###########
### BAM ###
###########
###########
[docs]class Bam: """Class for a BAM file. Args: panel (rnftools.lavender.Panel): Panel containing this BAM file. bam_fn (str): BAM filename. name (str): Name for this report. keep_intermediate_files (bool): Keep files created in intermediate steps during evaluation. render_pdf_method (str): Method for svg42pdf to render PDF (None / 'any' / 'cairo' / 'reportlab' / 'inkscape' / 'imagemagick' / 'wkhtmltopdf'). compress_intermediate_files (bool): Compress files created in intermediate steps during evaluation. default_x_axis (str): Values on x-axis, e.g., "({m}+{w})/({M}+{m}+{w})". default_x_label (str): Label on x-axis. """ def __init__( self, panel, bam_fn, name, render_pdf_method, keep_intermediate_files, compress_intermediate_files, default_x_axis, default_x_label, ): self.panel = panel self.report = panel.get_report() self.name = name self.render_pdf_method = render_pdf_method self.keep_intermediate_files = keep_intermediate_files self.compress_intermediate_files = compress_intermediate_files self.default_x_axis = default_x_axis self.default_x_label = default_x_label self._bam_fn = bam_fn self._gp_fn = os.path.join(self.panel.get_panel_dir(), "gp", self.name + ".gp") self._html_fn = os.path.join(self.panel.get_panel_dir(), "html", self.name + ".html") if compress_intermediate_files: self._es_fn = os.path.join(self.panel.get_panel_dir(), "es", self.name + ".es.gz") self._et_fn = os.path.join(self.panel.get_panel_dir(), "et", self.name + ".et.gz") else: self._es_fn = os.path.join(self.panel.get_panel_dir(), "es", self.name + ".es") self._et_fn = os.path.join(self.panel.get_panel_dir(), "et", self.name + ".et") self._roc_fn = os.path.join(self.panel.get_panel_dir(), "roc", self.name + ".roc") self._svg_fn = os.path.join(self.panel.get_panel_dir(), "graphics", self.name + ".svg") self._pdf_fn = re.sub(r'\.svg$', r'.pdf', self._svg_fn) self.bam_id = len(rnftools.lavender.bams()) rnftools.lavender.add_bam(self)
[docs] def get_name(self): """Get name associated with the BAM.""" return self.name
[docs] def get_required_files(self): """Get names of all files required to complete the report.""" return [self._svg_fn, self._html_fn]
###################################### ######################################
[docs] def bam_fn(self): """Get name of the BAM file.""" return self._bam_fn
[docs] def gp_fn(self): """Get name of the GP file.""" return self._gp_fn
[docs] def html_fn(self): """Get name of the HTML report.""" return self._html_fn
[docs] def es_fn(self): """Get name of the es file.""" return self._es_fn
[docs] def et_fn(self): """Get name of the et file.""" return self._et_fn
[docs] def roc_fn(self): """Get name of the ROC file.""" return self._roc_fn
[docs] def svg_fn(self): """Get name of the SVG file.""" return self._svg_fn
[docs] def pdf_fn(self): """Get name of the SVG file.""" return self._pdf_fn
############################ ############################ ## ## ES ## ############################ ############################
[docs] @staticmethod def bam2es( bam_fn, es_fo, allowed_delta, ): """Convert BAM file to ES file. Args: bam_fn (str): File name of the BAM file. bam_fo (file): File object of the ES file. allowed_delta (int): Maximal allowed coordinates difference for correct reads. """ es_fo.write("# RN: read name" + os.linesep) es_fo.write("# Q: is mapped with quality" + os.linesep) es_fo.write("# Chr: chr id" + os.linesep) es_fo.write("# D: direction" + os.linesep) es_fo.write("# L: leftmost nucleotide" + os.linesep) es_fo.write("# R: rightmost nucleotide" + os.linesep) es_fo.write("# Cat: category of alignment assigned by LAVEnder" + os.linesep) es_fo.write("# M_i i-th segment is correctly mapped" + os.linesep) es_fo.write("# m segment should be unmapped but it is mapped" + os.linesep) es_fo.write("# w segment is mapped to a wrong location" + os.linesep) es_fo.write("# U segment is unmapped and should be unmapped" + os.linesep) es_fo.write("# u segment is unmapped and should be mapped" + os.linesep) es_fo.write("# Segs: number of segments" + os.linesep) es_fo.write("# " + os.linesep) es_fo.write("# RN\tQ\tChr\tD\tL\tR\tCat\tSegs" + os.linesep) with pysam.AlignmentFile(bam_fn, "rb") as sam: references_dict = {} for i in range(len(sam.references)): references_dict[sam.references[i]] = i + 1 for read in sam: rnf_read_tuple = rnftools.rnfformat.ReadTuple() rnf_read_tuple.destringize(read.query_name) left = read.reference_start + 1 right = read.reference_end chrom_id = references_dict[sam.references[read.reference_id]] nb_of_segments = len(rnf_read_tuple.segments) if rnf_read_tuple.segments[0].genome_id == 1: should_be_mapped = True else: should_be_mapped = False # read is unmapped if read.is_unmapped: # read should be mapped if should_be_mapped: category = "u" # read should be unmapped else: category = "U" # read is mapped else: # read should be mapped if should_be_mapped: exists_corresponding_segment = False for j in range(len(rnf_read_tuple.segments)): segment = rnf_read_tuple.segments[j] if ( (segment.left == 0 or abs(segment.left - left) <= allowed_delta) and (segment.right == 0 or abs(segment.right - right) <= allowed_delta) and (segment.left != 0 or segment.right == 0) and (chrom_id == 0 or chrom_id == segment.chr_id) ): exists_corresponding_segment = True segment = str(j + 1) break # read was mapped to correct location if exists_corresponding_segment: # exists ok location? category = "M_" + segment # read was mapped to incorrect location else: category = "w" # read should be unmapped else: category = "m" es_fo.write( "\t".join( map( str, [ # read name read.query_name, # aligned? "unmapped" if read.is_unmapped else "mapped_" + str(read.mapping_quality), # reference id chrom_id, # direction "R" if read.is_reverse else "F", # left left, # right right, # assigned category category, # count of segments nb_of_segments ] ) ) + os.linesep )
[docs] def create_es(self): """Create an ES (intermediate) file for this BAM file. This is the function which asses if an alignment is correct """ with (gzip.open(self._es_fn, "tw+") if self.compress_intermediate_files else open(self._es_fn, "w+")) as es_fo: self.bam2es( bam_fn=self._bam_fn, es_fo=es_fo, allowed_delta=self.report.allowed_delta, )
############################ ############################ ## ## ET ## ############################ ############################ @staticmethod def _vector_of_categories(srs, read_tuple_name, parts): """Create vector of categories (voc[q] ... assigned category for given quality level) srs ... single read statistics ... for every q ... dictionary read_tuple_name ... read name parts ... number of segments """ # default value vec = ["x" for i in range(rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1)] assert len(srs) <= rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1, srs should_be_mapped = bool(srs[0]["m"] + srs[0]["U"] == 0) for q in range(len(srs)): ##### # M # - all parts correctly aligned ##### if len(srs[q]["M"] ) == parts and srs[q]["w"] == 0 and srs[q]["m"] == 0 and srs[q]["U"] == 0 and srs[q]["u"] == 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "M" ##### # w # - at least one segment is incorrectly aligned ##### if srs[q]["w"] > 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "w" ##### # m # - at least one segment was aligned but should not be aligned ##### if srs[q]["w"] == 0 and srs[q]["m"] > 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "m" ##### # U # - all segments should be unaligned but are unaligned ##### if srs[q]["U"] > 0 and srs[q]["u"] == 0 and srs[q]["m"] == 0 and srs[q]["w"] == 0 and len(srs[q]["M"]) == 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "U" ##### # u # - at least one segment was unaligned but should be aligned ##### if srs[q]["w"] == 0 and srs[q]["u"] > 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "u" ##### # t # - at least one segment was thresholded ##### if len(srs[q]["M"]) != parts and srs[q]["w"] == 0 and srs[q]["m"] == 0 and srs[q]["U"] == 0 and srs[q][ "u"] == 0 and srs[q]["t"] > 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "t" ##### # T # - at least one segment was thresholded ##### if len(srs[q]["M"]) != parts and srs[q]["w"] == 0 and srs[q]["m"] == 0 and srs[q]["U"] == 0 and srs[q][ "u"] == 0 and srs[q]["T"] > 0: assert vec[q] == "x", str((q, srs[q])) vec[q] = "T" ##### # P # - multimapped, M + w + m > parts ##### # only this one can rewrite some older assignment if len(srs[q]["M"]) + srs[q]["w"] + srs[q]["m"] > parts and srs[q]["U"] == 0 and srs[q]["u"] == 0: # assert vec[q]=="x",str((q,srs[q])) vec[q] = "P" return vec @staticmethod def _et_line(readname, vector_of_categories): i = 0 intervals = [] for j in range(len(vector_of_categories)): if vector_of_categories[i] != vector_of_categories[j]: intervals.append("{}:{}-{}".format(vector_of_categories[i], i, j - 1)) i = j intervals.append("{}:{}-{}".format(vector_of_categories[i], i, len(vector_of_categories) - 1)) return "{}\t{}".format(readname, ",".join(intervals))
[docs] @staticmethod def es2et( es_fo, et_fo, ): """Convert ES to ET. Args: es_fo (file): File object for the ES file. et_fo (file): File object for the ET file. """ et_fo.write("# Mapping information for read tuples" + os.linesep) et_fo.write("#" + os.linesep) et_fo.write("# RN: read name" + os.linesep) et_fo.write("# I: intervals with asigned categories" + os.linesep) et_fo.write("#" + os.linesep) et_fo.write("# RN I" + os.linesep) last_rname = "" for line in es_fo: line = line.strip() if line == "" or line[0] == "#": continue else: (rname, mapped, ref, direction, left, right, category, nb_of_segments) = line.split("\t") nb_of_segments = int(nb_of_segments) # print(rname,last_rname,mapped) # new read if rname != last_rname: # update if last_rname != "": voc = Bam._vector_of_categories(single_reads_statistics, rname, nb_of_segments) et_fo.write(Bam._et_line(readname=rname, vector_of_categories=voc)) et_fo.write(os.linesep) # nulling single_reads_statistics = [ { "U": 0, "u": 0, "M": [], "m": 0, "w": 0, "T": 0, "t": 0, } for i in range(rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1) ] last_rname = rname #################### # Unmapped segment # #################### ##### # U # ##### if category == "U": for q in range(len(single_reads_statistics)): single_reads_statistics[q]["U"] += 1 ##### # u # ##### elif category == "u": for q in range(len(single_reads_statistics)): single_reads_statistics[q]["u"] += 1 ################## # Mapped segment # ################## else: mapping_quality = int(mapped.replace("mapped_", "")) assert 0 <= mapping_quality and mapping_quality <= rnftools.lavender.MAXIMAL_MAPPING_QUALITY, mapping_quality ##### # m # ##### if category == "m": for q in range(mapping_quality + 1): single_reads_statistics[q]["m"] += 1 for q in range(mapping_quality + 1, rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1): single_reads_statistics[q]["T"] += 1 ##### # w # ##### elif category == "w": for q in range(mapping_quality + 1): single_reads_statistics[q]["w"] += 1 for q in range(mapping_quality + 1, rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1): single_reads_statistics[q]["t"] += 1 ##### # M # ##### else: assert category[0] == "M", category segment_id = int(category.replace("M_", "")) for q in range(mapping_quality + 1): single_reads_statistics[q]["M"].append(segment_id) for q in range(mapping_quality + 1, rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1): single_reads_statistics[q]["t"] += 1 # last read voc = Bam._vector_of_categories(single_reads_statistics, rname, nb_of_segments) et_fo.write(Bam._et_line(readname=rname, vector_of_categories=voc)) et_fo.write(os.linesep)
[docs] def create_et(self): """Create a et file for this BAM file (mapping information about read tuples). raises: ValueError """ with (gzip.open(self._es_fn, "tr") if self.compress_intermediate_files else open(self._es_fn, "r")) as es_fo: with (gzip.open(self._et_fn, "tw+") if self.compress_intermediate_files else open(self._et_fn, "w+")) as et_fo: self.es2et( es_fo=es_fo, et_fo=et_fo, )
############################ ############################ ## ## ROC ## ############################ ############################
[docs] @staticmethod def et2roc(et_fo, roc_fo): """ET to ROC conversion. Args: et_fo (file): File object for the ET file. roc_fo (file): File object for the ROC file. raises: ValueError """ stats_dicts = [ { "q": q, "M": 0, "w": 0, "m": 0, "P": 0, "U": 0, "u": 0, "T": 0, "t": 0, "x": 0 } for q in range(rnftools.lavender.MAXIMAL_MAPPING_QUALITY + 1) ] for line in et_fo: line = line.strip() if line != "" and line[0] != "#": (read_tuple_name, tab, info_categories) = line.partition("\t") intervals = info_categories.split(",") for interval in intervals: category = interval[0] (left, colon, right) = interval[2:].partition("-") for q in range(int(left), int(right) + 1): stats_dicts[q][category] += 1 roc_fo.write("# Numbers of reads in several categories in dependence" + os.linesep) roc_fo.write("# on the applied threshold on mapping quality q" + os.linesep) roc_fo.write("# " + os.linesep) roc_fo.write("# Categories:" + os.linesep) roc_fo.write("# M: Mapped correctly." + os.linesep) roc_fo.write("# w: Mapped to a wrong position." + os.linesep) roc_fo.write("# m: Mapped but should be unmapped." + os.linesep) roc_fo.write("# P: Multimapped." + os.linesep) roc_fo.write("# U: Unmapped and should be unmapped." + os.linesep) roc_fo.write("# u: Unmapped but should be mapped." + os.linesep) roc_fo.write("# T: Thresholded correctly." + os.linesep) roc_fo.write("# t: Thresholded incorrectly." + os.linesep) roc_fo.write("# x: Unknown." + os.linesep) roc_fo.write("#" + os.linesep) roc_fo.write("# q\tM\tw\tm\tP\tU\tu\tT\tt\tx\tall" + os.linesep) l_numbers = [] for line in stats_dicts: numbers = [ line["M"], line["w"], line["m"], line["P"], line["U"], line["u"], line["T"], line["t"], line["x"] ] if numbers != l_numbers: roc_fo.write("\t".join([str(line["q"])] + list(map(str, numbers)) + [str(sum(numbers))]) + os.linesep) l_numbers = numbers
[docs] def create_roc(self): """Create a ROC file for this BAM file. raises: ValueError """ with (gzip.open(self._et_fn, "tr") if self.compress_intermediate_files else open(self._et_fn, "r")) as et_fo: with open(self._roc_fn, "w+") as roc_fo: self.et2roc( et_fo=et_fo, roc_fo=roc_fo, )
############################ ############################ ## ## GRAPHICS ## ############################ ############################
[docs] def create_gp(self): """Create a GnuPlot file for this BAM file.""" categories_order = [ ("{U}", "#ee82ee", 'Unmapped correctly'), ("{u}", "#ff0000", 'Unmapped incorrectly'), ("{T}", "#00ff00", 'Thresholded correctly'), ("{t}", "#008800", 'Thresholded incorrectly'), ("{P}", "#ffff00", 'Multimapped'), ("{w}+{x}", "#7f7f7f", 'Mapped, should be unmapped'), ("{m}", "#000000", 'Mapped to wrong position'), ("{M}", "#0000ff", 'Mapped correctly'), ] plot_lines = [ '"{roc_fn}" using (( ({x}) )):({y}) lt rgb "{color}" with filledcurve x1 title "{title}", \\'.format( roc_fn=self._roc_fn, x=rnftools.lavender._format_xxx(self.default_x_axis), y=rnftools.lavender._format_xxx( '({sum})*100/{{all}}'.format(sum="+".join([c[0] for c in categories_order[i:]])) ), color=categories_order[i][1], title=categories_order[i][2], ) for i in range(len(categories_order)) ] plot = os.linesep.join((["plot \\"] + plot_lines + [""])) with open(self._gp_fn, "w+") as gp: gp_content = """ set title "{{/:Bold=16 {title}}}" set x2lab "{x_lab}" set log x set log x2 set format x "10^{{%L}}" set format x2 "10^{{%L}}" set xran [{xran}] set x2ran [{xran}] set x2tics unset xtics set ylab "Part of all reads (%)" set format y "%g %%" set yran [{yran}] set y2ran [{yran}] set pointsize 1.5 set grid ytics lc rgb "#777777" lw 1 lt 0 front set grid x2tics lc rgb "#777777" lw 1 lt 0 front set datafile separator "\\t" set palette negative set termin svg size {svg_size} enhanced set out "{svg_fn}" set key spacing 0.8 opaque width -5 """.format( svg_fn=self._svg_fn, xran="{:.10f}:{:.10f}".format(self.report.default_x_run[0], self.report.default_x_run[1]), yran="{:.10f}:{:.10f}".format(self.report.default_y_run[0], self.report.default_y_run[1]), svg_size="{},{}".format(self.report.default_svg_size_px[0], self.report.default_svg_size_px[1]), title=os.path.basename(self._bam_fn)[:-4], x_lab=self.default_x_label, ) gp_content = textwrap.dedent(gp_content) + "\n" + plot # gp_lines=gp_content.split("\n") # gp_lines=[x.strip() for x in gp_lines] # gp_content=gp_lines.join("\n") gp.write(gp_content)
[docs] def create_graphics(self): """Create images related to this BAM file using GnuPlot.""" rnftools.utils.shell('"{}" "{}"'.format("gnuplot", self._gp_fn)) if self.render_pdf_method is not None: svg_fn = self._svg_fn pdf_fn = self._pdf_fn svg42pdf(svg_fn, pdf_fn, method=self.render_pdf_method)
############################ ############################ ## ## HTML ## ############################ ############################
[docs] def create_html(self): """Create a HTML page for this BAM file.""" roc_dicts = [] with open(self._roc_fn, "r") as roc: for line in roc: line = line.strip() if line != "" and line[0] != "#": (q, M, w, m, P, U, u, T, t, x, a) = map(int, line.split("\t")) roc_dict = { "q": q, "M": M, "w": w, "m": m, "P": P, "U": U, "u": u, "T": T, "t": t, "x": x, "a": a, "mapped": M + w + m + P, "unmapped": U + u + T + t + x, } roc_dicts.append(roc_dict) tbody = os.linesep.join( [ """ <tr> <td> {quality} </td> <td> {mapped} </td> <td><small> {mapped_proc:.2f} </small></td> <td> {M} </td> <td><small> {M_proc:.2f} </small></td> <td> {w} </td> <td><small> {w_proc:.2f} </small></td> <td> {m} </td> <td><small> {m_proc:.2f} </small></td> <td> {P} </td> <td><small> {P_proc:.2f} </small></td> <td> {unmapped} </td> <td><small> {unmapped_proc:.2f} </small></td> <td> {U} </td> <td><small> {U_proc:.2f} </small></td> <td> {u} </td> <td><small> {u_proc:.2f} </small></td> <td> {T} </td> <td><small> {T_proc:.2f} </small></td> <td> {t} </td> <td><small> {t_proc:.2f} </small></td> <td> {x} </td> <td><small> {x_proc:.2f} </small></td> <td> {sum} </td> <td> {prec_proc:.3f} </td> </tr> """.format( quality=roc_dict["q"], mapped=roc_dict["mapped"], mapped_proc=100.0 * (roc_dict["mapped"]) / roc_dict["a"], M=roc_dict["M"], M_proc=100.0 * (roc_dict["M"]) / (roc_dict["mapped"]) if (roc_dict["mapped"]) != 0 else 0, w=roc_dict["w"], w_proc=100.0 * (roc_dict["w"]) / (roc_dict["mapped"]) if (roc_dict["mapped"]) != 0 else 0, m=roc_dict["m"], m_proc=100.0 * (roc_dict["m"]) / (roc_dict["mapped"]) if (roc_dict["mapped"]) != 0 else 0, P=roc_dict["P"], P_proc=100.0 * (roc_dict["P"]) / (roc_dict["mapped"]) if (roc_dict["mapped"]) != 0 else 0, unmapped=roc_dict["unmapped"], unmapped_proc=100.0 * (roc_dict["unmapped"]) / roc_dict["a"], U=roc_dict["U"], U_proc=100.0 * (roc_dict["U"]) / (roc_dict["unmapped"]) if (roc_dict["unmapped"]) != 0 else 0, u=roc_dict["u"], u_proc=100.0 * (roc_dict["u"]) / (roc_dict["unmapped"]) if (roc_dict["unmapped"]) != 0 else 0, T=roc_dict["T"], T_proc=100.0 * (roc_dict["T"]) / (roc_dict["unmapped"]) if (roc_dict["unmapped"]) != 0 else 0, t=roc_dict["t"], t_proc=100.0 * (roc_dict["t"]) / (roc_dict["unmapped"]) if (roc_dict["unmapped"]) != 0 else 0, x=roc_dict["x"], x_proc=100.0 * (roc_dict["x"]) / (roc_dict["unmapped"]) if (roc_dict["unmapped"]) != 0 else 0, sum=roc_dict["a"], prec_proc=100.0 * (roc_dict["M"]) / (roc_dict["mapped"]) if (roc_dict["mapped"]) != 0 else 0, ) for roc_dict in roc_dicts ] ) with open(self._html_fn, "w+") as html: program_info = ["No information available (PG header is essing)."] for x in rnftools.utils.shell( '"{samtools}" view -H "{bam}"'.format( samtools="samtools", bam=self._bam_fn, ), iterable=True ): x = x.strip() if x[:3] == "@PG": pg_header = x[4:].strip() parts = pg_header.split("\t") program_info = ["<table>"] for part in parts: (lvalue, _, rvalue) = part.partition(":") program_info.append( '<tr><td style="font-weight:bold">{}:</td><td style="text-align:left">{}</td></tr>'.format( lvalue, rvalue ) ) program_info.append("</table>") eval_info = ["<table>"] for (lvalue, rvalue) in [ ("BAM file", self._bam_fn), ("Allowed delta", self.report.allowed_delta), ]: eval_info.append( '<tr><td style="font-weight:bold">{}:</td><td style="text-align:left">{}</td></tr>'.format( lvalue, rvalue ) ) eval_info.append("</table>") css_src = textwrap.dedent( """ table {border-collapse:collapse;} td {border: solid #aaaaff 1px;padding:4px;text-align:right;} colgroup, thead {border: solid black 2px;padding 2px;} .link_to_top {font-size:10pt;} .desc {color:#aaa;} .formats {text-align:left;margin-bottom:20px;} """ ) html_src = """<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>{name}</title> <style type="text/css"> {css} </style> </head> <body> <h1 id="top"> {name} <span class="link_to_top"> [<a href="{homepage}">Back to main report</a>] </span> </h1> <p> <a href="#info_eval">Information about evaluation</a> - <a href="#info_prog">Information about program</a> - <a href="#roctable">ROC table</a> - <a href="#graphs">Graphs</a> </p> <h2 id="info_eval"> Information about evaluation {headline_links} </h2> {eval_info} <h2 id="info_prog"> Information about program {headline_links} </h2> {program_info} <h2 id="roctable"> ROC table {headline_links} </h2> <p style="font-size:80%"> <strong>M</strong>: mapped correctly <span class="desc"> (all segments of tuple are mapped once and correctly), </span> <strong>w</strong>: mapped to wrong position <span class="desc"> (at least one segment was mapped to a wrong position), </span> <strong>m</strong>: mapped but should be unmapped <span class="desc"> (at least one segment was mapped but the read should not be mapped), </span> <strong>P</strong>: multimapped <span class="desc"> (read should be mapped but it at least one segment was mapped several times and all segments were mapped correctly at least once), </span> <strong>U</strong>: unmapped correctly <span class="desc"> (all segments of the read were correctly marked as unmapped), </span> <strong>u</strong>: unmapped but should be mapped <span class="desc"> (at least one segment was mapped but entire read should be unmapped), </span> <strong>T</strong>: thresholded correctly <span class="desc"> (read shoud not be mapped), </span> <strong>t</strong>: thresholded incorrectly <span class="desc"> (read should be mapped), </span> <strong>x</strong>: unknown <span class="desc"> (read is probably not reported by mapper) </span> </p> <table> <colgroup span="1" style=""> <colgroup span="2" style="background-color:#ddd"> <colgroup span="8" style=""> <colgroup span="2" style="background-color:#ddd"> <colgroup span="10" style=""> <colgroup span="1" style="background-color:#ddd"> <colgroup span="1" style=""> <thead style="font-weight:bold;background-color:#ddddff"> <tr style="font-weight:bold;background-color:#ddddff"> <td>q</td> <td>mapped</td> <td>%</td> <td>M</td> <td>%</td> <td>w</td> <td>%</td> <td>m</td> <td>%</td> <td>P</td> <td>%</td> <td>unmapped</td> <td>%</td> <td>U</td> <td>%</td> <td>u</td> <td>%</td> <td>T</td> <td>%</td> <td>t</td> <td>%</td> <td>x</td> <td>%</td> <td>sum</td> <td>prec. (%)</td> </tr> </thead> <tbody> {tbody} </tbody> </table> <h2 id="graphs"> Graphs {headline_links} </h2> <div class="formats"> <img src="{svg}" /> <br /> <a href="{svg}">SVG version</a> | <a href="{roc}" type="text/csv">ROC file</a> | <a href="{gp}" type="text/plain">GP file</a> </div> </body> </html> """.format( name=self.name, tbody=tbody, css=css_src, svg=os.path.relpath(self._svg_fn, os.path.dirname(self._html_fn)), roc=os.path.relpath(self._roc_fn, os.path.dirname(self._html_fn)), gp=os.path.relpath(self._gp_fn, os.path.dirname(self._html_fn)), eval_info=os.linesep.join(eval_info), program_info=os.linesep.join(program_info), homepage=os.path.relpath(self.report.html_fn(), os.path.dirname(self._html_fn)), headline_links=''' <span class="link_to_top"> [<a href="#top">Top of this page</a>] [<a href="{homepage}">Back to main report</a>] </span> '''.format(homepage=os.path.relpath(self.report.html_fn(), os.path.dirname(self._html_fn)), ) ) tidy_html_src = bs4.BeautifulSoup(html_src).prettify() html.write(tidy_html_src)