def generatePrecisionRecallPlot(self, page, test_objects): import json csv = "mapper,precision,recall\n" columns = [["Precision"], ["Recall"]] groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) columns[0].append(round(result.precision, 4)) columns[1].append(round(result.recall, 4)) csv += "%s,%.4f,%.4f\n" % (test.getMapper().getTitle(), result.precision, result.recall) csv_filename = self.writeCSV("precision_recall", csv) page.addSection( "Precision and Recall", """<div id="plot_pr"></div>%s""" % util.makeExportDropdown("plot_pr", csv_filename)) page.addScript(""" var chart = c3.generate({ bindto: '#plot_pr', data: { columns: %s, type: 'bar', }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "Value", position: "outer-middle"} } }, legend: { position: "bottom" }, tooltip: { grouped: false } });""" % (json.dumps(columns), json.dumps(groups)))
def generatePrecisionRecallPlot(self, page, test_objects): import json csv = "mapper,precision,recall\n" columns = [["Precision"], ["Recall"]] groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) columns[0].append(round(result.precision, 4)) columns[1].append(round(result.recall, 4)) csv += "%s,%.4f,%.4f\n" % (test.getMapper().getTitle(),result.precision,result.recall) csv_filename = self.writeCSV("precision_recall",csv) page.addSection("Precision and Recall", """<div id="plot_pr"></div>%s""" % util.makeExportDropdown("plot_pr",csv_filename) ) page.addScript(""" var chart = c3.generate({ bindto: '#plot_pr', data: { columns: %s, type: 'bar', }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "Value", position: "outer-middle"} } }, legend: { position: "bottom" }, tooltip: { grouped: false } });""" % (json.dumps(columns), json.dumps(groups)))
def generateMappingStatisticsPlot(self, page, test_objects): import json csv = "mapper,mapq_threshold,mapped_percent,not_mapped_percent\n" columns_mapqs = [] for i in range(256): columns_mapqs.append([["Mapped"], ["Not Mapped"]]) groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue mapqs = sorted(result.mapq_cumulated) for curr in mapqs: correct,wrong,not_mapped=0,0,0 if result.total != 0: correct=result.mapq_cumulated[curr]["correct"] / float(result.total) not_mapped=(result.total - ( result.mapq_cumulated[curr]["correct"] + result.mapq_cumulated[curr]["wrong"])) / float(result.total) columns_mapqs[curr][0].append(correct) columns_mapqs[curr][1].append(not_mapped) csv += "%s,%d,%.4f,%.4f\n" % (test.getMapper().getTitle(), curr, correct, not_mapped) groups.append(test.getMapper().getTitle()) csv_filename = self.writeCSV("mapping_statistics",csv) page.addSection("Mapping Statistics", """MAPQ threshold <input type="number" onchange="javascript:updateMapstatsChart(this.value);" value="0" min="0" max="255" size="5"> <div id="plot_mapstats"></div>%s""" % util.makeExportDropdown("plot_mapstats",csv_filename), None, "This plot shows the fractions of correctly, wrongly and not mapped reads for each mapper and the selected mapping quality cutoff. Reads that have been filtered using the mapping quality cutoff are shown as unmapped. The interactive legend can be used to, for example, display only the number of wrongly and not mapped reads.") page.addScript(""" var column_mapqs=%s; function updateMapstatsChart(cutoff) { mapstats_chart.load({columns: column_mapqs[cutoff], type:'bar',groups: [['Not Mapped','Mapped']],order: null}); } var mapstats_chart = c3.generate({ bindto: '#plot_mapstats', size: { height: 500 }, data: { columns: %s, type: 'bar', groups: [['Not Mapped','Mapped']], order: null, colors: { "Mapped": d3.rgb('#2CA02C'), "Not Mapped": d3.rgb('#1F77B4') } }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "Percentage of reads", position: "outer-middle" }, tick: { format: d3.format(".3%%") }, padding: { top:15 } } }, legend: { position: "bottom" }, tooltip: { grouped: false } }); """ % (json.dumps(columns_mapqs), json.dumps(columns_mapqs[0]), json.dumps(groups)))
def generateMappingQualityOverview(self, page, test_objects): import json csv_rating = "mapper,mapq_threshold,filtered_wrong_per_filtered_correct\n" csv_distribution = "mapper,mapq_value,read_count\n" data = [] data_dist = [] x_values_dist = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column = [test.getMapper().getTitle()] column_dist = [test.getMapper().getTitle()] x_values_dist = [] results = test.getRunResults() if results == None or test.getErrorCount(): continue mapqs = sorted(results.mapq_cumulated) for curr in mapqs: lost_correct = (results.correct - results.mapq_cumulated[curr]["correct"]) lost_wrong = (results.wrong - results.mapq_cumulated[curr]["wrong"]) if curr < 30 or (curr < 100 and curr%10==0) or curr%20==0 or curr==255: try: column.append(round(lost_wrong / float(lost_correct), 4)) except ZeroDivisionError: column.append(0) if curr <= 255: column_dist.append( results.total-results.mapq_cumulated[curr]["correct"]-results.mapq_cumulated[curr]["wrong"] ) else: column_dist.append(0) x_values_dist.append(curr) csv_rating += "%s,%d,%.4f\n" % (test.getMapper().getTitle(),curr,column[-1]) csv_distribution += "%s,%d,%d\n" % (test.getMapper().getTitle(),curr,column_dist[-1]) data.append(column) data_dist.append(column_dist) data.append(["x"]+x_values_dist) data_dist.append(["x"]+x_values_dist) csv_filename_distribution = self.writeCSV("mapq_distribution",csv_distribution) page.addSection("Mapping Quality", """<div id="plot_mapq_dist"></div>%s""" % (util.makeExportDropdown("plot_mapq_dist",csv_filename_distribution)), None, "This plot shows the total number of mapped reads for each mapping quality threshold (all reads with a mapping quality value smaller or equal to the threshold).") page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_dist', size: { height: 500 }, data: { x: 'x', columns: %s, type: 'step' }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Number of Reads Mapped", position: "outer-middle"} } }, legend: { show:true }, point: { show: false } });""" % (json.dumps(data_dist)) )
def generateTestList(self, tests): html = "" html += "<table id=\"test_list\" class=\"table table-striped\">" html += "<thead>" html += "<tr>" html += "<th>State</th>" html += "<th>Mapper</th>" html += "<th>Parameters</th>" # html += "<th>Warnings</th>" # html += "<th>Errors</th>" html += "<th>Mapped</th>" html += "<th>Not Mapped</th>" html += "<th>Throughput (reads/s)</th>" html += "</tr>" html += "</thead>" csv = "mapper,additional_parameters,mapped_percent,not_mapped_percent,throughput_reads_per_sec\n" for test in tests: warnings = 0 errors = 0 show_test = True if self.mate.run_only != False and not test.getName() in self.mate.run_only: show_test = False break if not test.getWasRun() or not self.mate.isTestIncluded(test): show_test = False break warnings += test.getWarningCount() errors += test.getErrorCount() if not show_test: continue tclass = "info" icon = "glyphicon-ok" if warnings > 0: tclass = "warning" icon = "glyphicon-exclamation-sign" if errors > 0: tclass = "danger" icon = "glyphicon-remove" html += "<tr>" html += "<td class=\"col-md-1 " + tclass + "\" align=\"center\"><span class=\"glyphicon " + icon + "\"></span></td>" html += "<td><a href=\"" + test.getFullName() + ".html\">" + test.getMapper().getTitle() + "</a></td>" html += "<td>%s</td>" % test.getMapper().param_string total=test.getRunResults().total if total != 0: correct=float(100 * test.getRunResults().correct)/total wrong=float(100 * test.getRunResults().wrong)/total not_mapped=float(100 * (test.getRunResults().not_mapped + test.getRunResults().not_found))/total else: correct=0 wrong=0 not_mapped=0 maptime=test.getRunResults().maptime if maptime != 0: throughput=total/float(test.getRunResults().maptime) else: throughput=-1 if errors == 0: html += "<td>%.3f%%</td>" % correct html += "<td>%.3f%%</td>" % not_mapped html += "<td>%d</td>" % throughput csv+="%s,%s,%.4f,%.4f,%d\n" % (test.getMapper().getTitle(),test.getMapper().param_string,correct,not_mapped,throughput) else: html += "<td></td>" html += "<td></td>" html += "<td></td>" csv+="%s,%s,-,-,-\n" % (test.getMapper().getTitle(),test.getMapper().param_string) html += "</tr>" csv_filename = self.writeCSV("overview",csv) html += "</table>" html += util.makeExportDropdown("",csv_filename) return html
def generateMappingStatisticsPlot(self, page, test_objects): import json csv = "mapper,mapq_threshold,mapped_percent,not_mapped_percent\n" columns_mapqs = [] for i in range(256): columns_mapqs.append([["Mapped"], ["Not Mapped"]]) groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue mapqs = sorted(result.mapq_cumulated) for curr in mapqs: correct, wrong, not_mapped = 0, 0, 0 if result.total != 0: correct = result.mapq_cumulated[curr]["correct"] / float( result.total) not_mapped = (result.total - (result.mapq_cumulated[curr]["correct"] + result.mapq_cumulated[curr]["wrong"])) / float( result.total) columns_mapqs[curr][0].append(correct) columns_mapqs[curr][1].append(not_mapped) csv += "%s,%d,%.4f,%.4f\n" % (test.getMapper().getTitle(), curr, correct, not_mapped) groups.append(test.getMapper().getTitle()) csv_filename = self.writeCSV("mapping_statistics", csv) page.addSection( "Mapping Statistics", """MAPQ threshold <input type="number" onchange="javascript:updateMapstatsChart(this.value);" value="0" min="0" max="255" size="5"> <div id="plot_mapstats"></div>%s""" % util.makeExportDropdown("plot_mapstats", csv_filename), None, "This plot shows the fractions of correctly, wrongly and not mapped reads for each mapper and the selected mapping quality cutoff. Reads that have been filtered using the mapping quality cutoff are shown as unmapped. The interactive legend can be used to, for example, display only the number of wrongly and not mapped reads." ) page.addScript(""" var column_mapqs=%s; function updateMapstatsChart(cutoff) { mapstats_chart.load({columns: column_mapqs[cutoff], type:'bar',groups: [['Not Mapped','Mapped']],order: null}); } var mapstats_chart = c3.generate({ bindto: '#plot_mapstats', size: { height: 500 }, data: { columns: %s, type: 'bar', groups: [['Not Mapped','Mapped']], order: null, colors: { "Mapped": d3.rgb('#2CA02C'), "Not Mapped": d3.rgb('#1F77B4') } }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "Percentage of reads", position: "outer-middle" }, tick: { format: d3.format(".3%%") }, padding: { top:15 } } }, legend: { position: "bottom" }, tooltip: { grouped: false } }); """ % (json.dumps(columns_mapqs), json.dumps( columns_mapqs[0]), json.dumps(groups)))
def generateResourcePlot(self, page, test_objects, measure): import json if measure == "runtime": columns = [["Runtime/mio. reads"]] title = "Runtime [min/mio. reads]" section_title = "Runtime" elif measure == "memory": columns = [["Memory Usage"]] title = "Memory Usage [MB]" section_title = "Memory Usage" elif measure == "corrects": columns = [["Correct/s"]] title = "Correctly Mapped [reads/s]" section_title = "Correctly Mapped/s" groups = [] csv = "mapper,%s\n" % measure for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) try: if measure == "runtime": columns[0].append( round((result.maptime / 60.0) * (1000000.0 / result.total), 3)) elif measure == "memory": columns[0].append(round(result.memory / (1000 * 1000))) elif measure == "corrects": columns[0].append(round((result.correct) / result.maptime, 3)) except ZeroDivisionError: columns[0].append(0) csv += "%s,%f\n" % (test.getMapper().getTitle(), columns[0][-1]) csv_filename = self.writeCSV(measure, csv) page.addSection( section_title, """<div id="plot_resource_%s"></div>%s""" % (measure, util.makeExportDropdown("plot_resource_%s" % measure, csv_filename))) page.addScript(""" var chart = c3.generate({ bindto: '#plot_resource_%s', data: { columns: %s, type: 'bar', }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "%s", position: "outer-middle" }, tick: { format: d3.format("10.3f") } } }, legend: { position: "inset", show:false, inset: { anchor: 'top-right' } } });""" % (measure, json.dumps(columns), json.dumps(groups), title))
def generateOverallScatterPlot(self, page, test_objects): import json csv = "parameter,mapped_percent,throughput_reads_per_sec\n" # csv = "correct_percent,reads_per_sec\n" columns = [] xs = {} labels = {} max_throughput = 0 for test in test_objects: if test.getRunResults() != None: throughput = -1 if test.getRunResults().maptime != 0: throughput = test.getRunResults().correct / test.getRunResults( ).maptime max_throughput = max(throughput, max_throughput) for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue mapper_name = test.getMapper().getTitle() + " " + test.getMapper( ).param_string correct, throughput = 0, 0 if result.total != 0: correct = result.correct / float(result.total) if result.maptime != 0: throughput = round((result.total / result.maptime), 0) columns.append([mapper_name + "_x", throughput, 0]) columns.append([mapper_name, correct]) xs[mapper_name] = mapper_name + "_x" labels[int( result.total / result.maptime )] = test.getMapper().getTitle() + " " + test.getMapper().param_string csv += "%s,%s,%f,%f\n" % (test.getMapper().getName(), test.getMapper().param_string, round(correct, 3), throughput) csv_filename = self.writeCSV("overview_scatter", csv) page.addSection( "Results Overview", generateTestList(self, test_objects) + """<p style="margin-top:15px;">The figure below visualizes above results by directly comparing mapped percentage and throughput.</p><div id="plot_os"></div>%s""" % util.makeExportDropdown("plot_os", csv_filename), None, """Mappers were evaluated for the given test <a href="#section2">data set</a>. The table below shows the used parameters, mapping statistics and throughput for each mapper. Detailed results for a mapper can be displayed by clicking on its name in the table or the navigation on top.""" ) show_legend = len(columns) < 30 page.addScript(""" var params=%s; var chart = c3.generate({ bindto: '#plot_os', size: { height: 500 }, data: { xs: %s, columns: %s, type: 'scatter' }, grid: { y: { show: true }, x: { show: true } }, axis: { x: { label: { text: "Throughput [Reads/s]", position: "outer-middle"}, tick: {fit: false, format: d3.format("d")}, padding: { left: 500 } }, y: { label: { text: "Mapped [%%]", position: "outer-middle"}, tick: { format: d3.format(".3%%") } } }, point: { r: 5 }, legend: { position: "bottom", show:%s, inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { format: { //title: function (d) { return ""; } }, grouped: false } });""" % (json.dumps(labels), json.dumps(xs), json.dumps(columns), str(show_legend).lower()))
def generatePrecisionRecallScatterPlot(self, page, test_objects): import json csv = "mapper,precision,recall\n" columns = [] xs = {} groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) columns.append([test.getMapper().getTitle()+"_x",round(result.recall, 4), 0]) columns.append([test.getMapper().getTitle(),round(result.precision, 4)]) xs[test.getMapper().getTitle()] = test.getMapper().getTitle() + "_x" csv += "%s,%.4f,%.4f\n" % (test.getMapper().getTitle(),result.precision,result.recall) csv_filename = self.writeCSV("precision_recall",csv) page.addSection("Precision and Recall", """<div id="plot_pr"></div>%s""" % util.makeExportDropdown("plot_pr",csv_filename) ) page.addScript(""" var chart = c3.generate({ bindto: '#plot_pr', size: { height: 500 }, data: { xs: %s, columns: %s, type: 'scatter', }, grid: { x: { show: true }, y: { show: true } }, axis: { x: { label: { text: "Recall", position: "outer-middle" }, tick: { fit: false, format: d3.format("10.3f") } }, y: { label: { text: "Precision", position: "outer-middle"}, tick: { format: d3.format("10.3f") } } }, point: { r: 5 }, legend: { position: "bottom", inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { format: { //title: function (d) { return ""; } }, grouped: false } });""" % (json.dumps(xs), json.dumps(columns)))
def generateROCPlot(self, page, test_objects): import json import math xs = {} columns = [] labels=[] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue xs[test.getMapper().getTitle()] = test.getMapper().getTitle() + "_x" column_x=[test.getMapper().getTitle()+"_x"] column_data = [test.getMapper().getTitle()] for mapq in range(255): correctfrac=result.mapq_cumulated[mapq]["correct"] wrongfrac=result.mapq_cumulated[mapq]["wrong"] if wrongfrac==0 or correctfrac==0: continue correctfrac=math.log(correctfrac,10) wrongfrac=math.log(wrongfrac,10) column_x.append(wrongfrac) column_data.append(correctfrac) columns.append(column_x) columns.append(column_data) page.addSection("MAPQ ROC", """ <div id="mapq_roc"></div>%s""" % (util.makeExportDropdown("mapq_roc","")), None, "") page.addScript(""" var roc_plot = c3.generate({ bindto: '#mapq_roc', size: { height: 500 }, data: { xs: %s, columns: %s }, grid: { x: { show: true }, y: { show: true } }, axis: { x: { label: { text: "log10(Wrongly Mapped)", position: "outer-middle" }, tick: { fit: false, format: d3.format("10.3f") } }, y: { label: { text: "log10(Correctly Mapped)", position: "outer-middle"}, tick: { format: d3.format("10.3f") } } }, point: { r: 5 }, legend: { position: "bottom", inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { grouped: false }}); """ % (json.dumps(xs), json.dumps(columns)))
def generateMappingQualityOverview(self, page, test_objects): import json csv_rating = "mapper,mapq_threshold,filtered_wrong_per_filtered_correct\n" csv_distribution = "mapper,mapq_value,read_count\n" data = [] data_dist = [] x_values_dist = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column = [test.getMapper().getTitle()] column_dist = [test.getMapper().getTitle()] results = test.getRunResults() if results == None or test.getErrorCount(): continue x_values_dist = [] mapqs = sorted(results.mapq_cumulated) for curr in mapqs: lost_correct = (results.correct - results.mapq_cumulated[curr]["correct"]) lost_wrong = (results.wrong - results.mapq_cumulated[curr]["wrong"]) if curr < 30 or (curr < 100 and curr % 10 == 0) or curr % 20 == 0 or curr == 255: try: column.append(round(lost_wrong / float(lost_correct), 4)) except ZeroDivisionError: column.append(0) if curr <= 255: column_dist.append( results.mapq_cumulated[curr]["correct"] + results.mapq_cumulated[curr]["wrong"]) else: column_dist.append(0) x_values_dist.append(curr) csv_rating += "%s,%d,%.4f\n" % (test.getMapper().getTitle(), curr, column[-1]) csv_distribution += "%s,%d,%d\n" % (test.getMapper().getTitle(), curr, column_dist[-1]) data.append(column) data_dist.append(column_dist) data.append(["x"] + x_values_dist) data_dist.append(["x"] + x_values_dist) csv_filename_rating = self.writeCSV("mapq_rating", csv_rating) csv_filename_distribution = self.writeCSV("mapq_distribution", csv_distribution) page.addSection( "Mapping Quality", """<div id="plot_mapq_rating"></div>%s<p> </p><div id="plot_mapq_dist"></div>%s""" % (util.makeExportDropdown("plot_mapq_rating", csv_filename_rating), util.makeExportDropdown("plot_mapq_dist", csv_filename_distribution)), None, "This section represents an overview of the distribution of mapping quality values for all mappers. A detailed evaluation of mapping qualities for specific mappers can be found on the respective mapper results page (accessible using the navigation on top). The first plot rates each mapping quality threshold (0-255) by comparing the numbers of wrongly and correctly mapped reads that would be removed due to falling under the threshold. The second plot shows the total number of mapped reads for each mapping quality threshold (all reads with a mapping quality value smaller or equal to the threshold)." ) page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_rating', size: { height: 500 }, data: { x: 'x', columns: %s }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Filtered wrong reads / Filtered correct reads", position: "outer-middle"} } }, legend: { show:true }, point: { show: false }, subchart: { show:true } });""" % json.dumps(data)) page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_dist', size: { height: 500 }, data: { x: 'x', columns: %s, type: 'step' }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Number of Reads Mapped", position: "outer-middle"} } }, legend: { show:true }, point: { show: false } });""" % (json.dumps(data_dist)))
def generatePrecisionRecallScatterPlot(self, page, test_objects): import json csv = "mapper,precision,recall\n" columns = [] xs = {} groups = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) columns.append( [test.getMapper().getTitle() + "_x", round(result.recall, 4), 0]) columns.append( [test.getMapper().getTitle(), round(result.precision, 4)]) xs[test.getMapper().getTitle()] = test.getMapper().getTitle() + "_x" csv += "%s,%.4f,%.4f\n" % (test.getMapper().getTitle(), result.precision, result.recall) csv_filename = self.writeCSV("precision_recall", csv) page.addSection( "Precision and Recall", """<div id="plot_pr"></div>%s""" % util.makeExportDropdown("plot_pr", csv_filename)) page.addScript(""" var chart = c3.generate({ bindto: '#plot_pr', size: { height: 500 }, data: { xs: %s, columns: %s, type: 'scatter', }, grid: { x: { show: true }, y: { show: true } }, axis: { x: { label: { text: "Recall", position: "outer-middle" }, tick: { fit: false, format: d3.format("10.3f") } }, y: { label: { text: "Precision", position: "outer-middle"}, tick: { format: d3.format("10.3f") } } }, point: { r: 5 }, legend: { position: "bottom", inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { format: { //title: function (d) { return ""; } }, grouped: false } });""" % (json.dumps(xs), json.dumps(columns)))
def generateROCPlot(self, page, test_objects): import json import math xs = {} columns = [] labels = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue xs[test.getMapper().getTitle()] = test.getMapper().getTitle() + "_x" column_x = [test.getMapper().getTitle() + "_x"] column_data = [test.getMapper().getTitle()] for mapq in range(255): correctfrac = result.mapq_cumulated[mapq]["correct"] wrongfrac = result.mapq_cumulated[mapq]["wrong"] if wrongfrac == 0 or correctfrac == 0: continue correctfrac = math.log(correctfrac, 10) wrongfrac = math.log(wrongfrac, 10) column_x.append(wrongfrac) column_data.append(correctfrac) columns.append(column_x) columns.append(column_data) page.addSection( "MAPQ ROC", """ <div id="mapq_roc"></div>%s""" % (util.makeExportDropdown("mapq_roc", "")), None, "") page.addScript(""" var roc_plot = c3.generate({ bindto: '#mapq_roc', size: { height: 500 }, data: { xs: %s, columns: %s }, grid: { x: { show: true }, y: { show: true } }, axis: { x: { label: { text: "log10(Wrongly Mapped)", position: "outer-middle" }, tick: { fit: false, format: d3.format("10.3f") } }, y: { label: { text: "log10(Correctly Mapped)", position: "outer-middle"}, tick: { format: d3.format("10.3f") } } }, point: { r: 5 }, legend: { position: "bottom", inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { grouped: false }}); """ % (json.dumps(xs), json.dumps(columns)))
def generateOverviewPlot(self, page, measure): format_func = "function(y){return y;}" min = 0 title_section = "" if measure == "correct": title = "Correctly Mapped Reads [%]" title_section = "Correctly Mapped Reads" format_func = "function(y){return Math.round(100000*y)/100000 + \"%\";}" elif measure == "corrects": title = "Correctly Mapped Reads [Reads/s]" title_section = "Correct/s" format_func = "function(y){return Math.round(1000*y)/1000;}" elif measure == "precision": title = "Precision" format_func = "function(y){return Math.round(10000*y)/10000;}" # min = 0.75 elif measure == "recall": title = "Recall" format_func = "function(y){return Math.round(10000*y)/10000;}" # min = 0.75 elif measure == "fmeasure": title = "F-Measure" format_func = "function(y){return Math.round(10000*y)/10000;}" # min = 0.75 if title_section == "": title_section = title import json data = [] titles = [] for mapper in sorted(self.mate.getMappers()): column = [] for test_name in self.mate.getTestNameList(): test = self.mate.getTestByMapperName(test_name, mapper) if test == None or test._("base") != "tests_base/base_mapping": continue if len(column) == 0: column.append(test.getMapper().getTitle()) if test.getRunResults() == None or test.getErrorCount(): column.append(0) continue if measure == "correct": try: value = test.getRunResults().correct / float( test.getRunResults().total) value = value * 100 except ZeroDivisionError: value = 0 elif measure == "corrects": try: value = test.getRunResults().correct / float( test.getRunResults().maptime) except ZeroDivisionError: value = 0 elif measure == "precision": value = test.getRunResults().precision elif measure == "recall": value = test.getRunResults().recall elif measure == "fmeasure": value = test.getRunResults().fmeasure value = round(value, 4) column.append(value) if len(column): data.append(column) if min != 0: min_str = ",min: %f" % min else: min_str = "" titles = [] for name in self.mate.getTestNameList(): tests = self.mate.getTestsByName(name) if len(tests) != 0 and tests[0]._( "base") == "tests_base/base_mapping": titles.append(tests[0].getTitle()) # titles=["0%","75%","90%","95%","99%"] page.addSection( "Results: %s" % title_section, """<div id="plot_%s"></div>%s""" % (measure, util.makeExportDropdown("plot_%s" % measure, ""))) page.addScript(""" var chart_%s = c3.generate({ bindto: '#plot_%s', size: { height: 400 }, data: { columns: %s, type: "bar" }, grid: { y: { show: true } }, axis: { x: { type: "category", categories: %s, label: { text: "Data Set", position: "outer-middle" } }, y: { label: { text: "%s", position: "outer-middle" }, tick: { format: %s } %s } }, legend: { position: "right" } });""" % (measure, measure, json.dumps(data), json.dumps(titles), title, format_func, min_str))
def generateMappingQualityOverview(self, page, test_objects): import json csv_rating = "mapper,mapq_threshold,filtered_wrong_per_filtered_correct\n" csv_distribution = "mapper,mapq_value,read_count\n" data = [] data_dist = [] x_values_dist = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column = [test.getMapper().getTitle()] column_dist = [test.getMapper().getTitle()] results = test.getRunResults() if results == None or test.getErrorCount(): continue x_values_dist = [] mapqs = sorted(results.mapq_cumulated) for curr in mapqs: lost_correct = (results.correct - results.mapq_cumulated[curr]["correct"]) lost_wrong = (results.wrong - results.mapq_cumulated[curr]["wrong"]) if curr < 30 or (curr < 100 and curr%10==0) or curr%20==0 or curr==255: try: column.append(round(lost_wrong / float(lost_correct), 4)) except ZeroDivisionError: column.append(0) if curr <= 255: column_dist.append(results.mapq_cumulated[curr]["correct"]+results.mapq_cumulated[curr]["wrong"] ) else: column_dist.append(0) x_values_dist.append(curr) csv_rating += "%s,%d,%.4f\n" % (test.getMapper().getTitle(),curr,column[-1]) csv_distribution += "%s,%d,%d\n" % (test.getMapper().getTitle(),curr,column_dist[-1]) data.append(column) data_dist.append(column_dist) data.append(["x"]+x_values_dist) data_dist.append(["x"]+x_values_dist) csv_filename_rating = self.writeCSV("mapq_rating",csv_rating) csv_filename_distribution = self.writeCSV("mapq_distribution",csv_distribution) page.addSection("Mapping Quality", """<div id="plot_mapq_rating"></div>%s<p> </p><div id="plot_mapq_dist"></div>%s""" % (util.makeExportDropdown("plot_mapq_rating",csv_filename_rating), util.makeExportDropdown("plot_mapq_dist",csv_filename_distribution)), None, "This section represents an overview of the distribution of mapping quality values for all mappers. A detailed evaluation of mapping qualities for specific mappers can be found on the respective mapper results page (accessible using the navigation on top). The first plot rates each mapping quality threshold (0-255) by comparing the numbers of wrongly and correctly mapped reads that would be removed due to falling under the threshold. The second plot shows the total number of mapped reads for each mapping quality threshold (all reads with a mapping quality value smaller or equal to the threshold).") page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_rating', size: { height: 500 }, data: { x: 'x', columns: %s }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Filtered wrong reads / Filtered correct reads", position: "outer-middle"} } }, legend: { show:true }, point: { show: false }, subchart: { show:true } });""" % json.dumps(data)) page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_dist', size: { height: 500 }, data: { x: 'x', columns: %s, type: 'step' }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Number of Reads Mapped", position: "outer-middle"} } }, legend: { show:true }, point: { show: false } });""" % (json.dumps(data_dist)) )
def generateMappingQualityPlot(self, page): results = self.getRunResults() columns = [["Mapped"]] csv = "mapq_threshold,mapped_reads\n" correct = [] wrong = [] mc = max(1, float(results.total)) mw = max(1, float(results.total)) wrong_scale = float(results.correct / mc) / float((1 + results.wrong) / mw) mapqs = sorted(results.mapq_cumulated) for curr in mapqs: columns[0].append(results.mapq_cumulated[curr]["correct"] / float(mc)) csv += "%d,%d\n" % (curr,results.mapq_cumulated[curr]["correct"]) csv_filename = self.writeCSV(self.getMapper().getName() + "_mapqs",csv) page.addSection("Mapping Quality Thresholds", """<div id="plot_mapq_tresh"></div>%s""" % util.makeExportDropdown("plot_mapq_thresh",csv_filename), None, "The plot below shows the percentages of mapped reads for all mapping quality thresholds for this mapper. The values at threshold 0 therefore correspond to the unfiltered results.") page.addScript(""" var cols=%s; var a=0; var b=0; var last_a=0; var last_b=0; function updateRange() { if(last_a==a && last_b==b) { setTimeout(updateRange,500); return; } an=Math.floor(parseInt(a)); bn=Math.floor(parseInt(b)); var mn = 0; var mx = 0; for(i=an;i<bn;i++) { mx=Math.max(mx,cols[0][i+1]); mn=Math.min(mn,cols[1][i+1]); } r={} r.max=mx; r.min=mn; chart.axis.range(r); last_a=a; last_b=b; setTimeout(updateRange,500); } setTimeout(updateRange,500); var chart = c3.generate({ bindto: '#plot_mapq_tresh', data: { columns: %s, type: "area" }, grid: { y: { show: true } }, axis: { x: { label: "Mapping Quality Threshold" }, y: { label: { text: "Read Count", position: "outer-middle" }, tick: { format: function(v) { if(v>=0){return Math.min(100,(Math.round(100000*v)/1000))+"%%";}else{ return Math.min(100,(Math.round(100000*(-v/%f))/1000))+"%%";} } } } }, legend: { position: "right" }, subchart: { show: true, onbrush: function (d) { a = d[0]; b = d[1]; } } });""" % (json.dumps(columns), json.dumps(columns), wrong_scale))
def generateEditDistancePlot(self, page, test_objects): import json data_dist = [] max_edit_dist = 0 reads_by_edit_distance = {} for test in test_objects: results = test.getRunResults() if len(results.reads_by_edit_distance)!=0: reads_by_edit_distance = results.reads_by_edit_distance max_edit_dist = max([key for key in reads_by_edit_distance]) x=[] for key in reads_by_edit_distance: if reads_by_edit_distance[key] > 100: x.append(key) for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column_dist = [test.getMapper().getTitle()] results = test.getRunResults() if results == None or test.getErrorCount(): continue reads_by_edit_distance = results.reads_by_edit_distance mapped_by_edit_distance = results.mapped_by_edit_distance for i in range(max_edit_dist+1): mapped = mapped_by_edit_distance[i] if i in mapped_by_edit_distance else 0 reads = reads_by_edit_distance[i] if i in reads_by_edit_distance else 0 if reads > 100: column_dist.append((reads-mapped)/float(reads)) else: pass data_dist.append(column_dist) print(column_dist) data_dist.append(["x"]+x) page.addSection("Edit Distance", """This plot shows the effect of the number of mismatches introduced in reads on mapping rates.<br><br><div id="plot_edit_dist"></div>%s""" % util.makeExportDropdown("plot_edit_dist",""), None, "") page.addScript(""" var chart = c3.generate({ bindto: '#plot_edit_dist', size: { height: 500 }, data: { x: 'x', columns: %s }, grid: { y: { show: true } }, axis: { x: { label: {text: "Edit distance in read", position: "outer-middle"} }, y: { label: {text: "Not mapped reads [%%]", position: "outer-middle"}, tick: { format: d3.format(".3%%") } } }, legend: { show:true }, point: { show: false } });""" % json.dumps(data_dist))
def generateTestList(self, tests): html = "" html += "<table id=\"test_list\" class=\"table table-striped\">" html += "<thead>" html += "<tr>" html += "<th>State</th>" html += "<th>Mapper</th>" html += "<th>Parameters</th>" # html += "<th>Warnings</th>" # html += "<th>Errors</th>" html += "<th>Mapped</th>" html += "<th>Not Mapped</th>" html += "<th>Throughput (reads/s)</th>" html += "</tr>" html += "</thead>" csv = "mapper,additional_parameters,mapped_percent,not_mapped_percent,throughput_reads_per_sec\n" for test in tests: warnings = 0 errors = 0 show_test = True if self.mate.run_only != False and not test.getName( ) in self.mate.run_only: show_test = False break if not test.getWasRun() or not self.mate.isTestIncluded(test): show_test = False break warnings += test.getWarningCount() errors += test.getErrorCount() if not show_test: continue tclass = "info" icon = "glyphicon-ok" if warnings > 0: tclass = "warning" icon = "glyphicon-exclamation-sign" if errors > 0: tclass = "danger" icon = "glyphicon-remove" html += "<tr>" html += "<td class=\"col-md-1 " + tclass + "\" align=\"center\"><span class=\"glyphicon " + icon + "\"></span></td>" html += "<td><a href=\"" + test.getFullName( ) + ".html\">" + test.getMapper().getTitle() + "</a></td>" html += "<td>%s</td>" % test.getMapper().param_string total = test.getRunResults().total if total != 0: correct = float(100 * test.getRunResults().correct) / total wrong = float(100 * test.getRunResults().wrong) / total not_mapped = float(100 * (test.getRunResults().not_mapped + test.getRunResults().not_found)) / total else: correct = 0 wrong = 0 not_mapped = 0 maptime = test.getRunResults().maptime if maptime != 0: throughput = total / float(test.getRunResults().maptime) else: throughput = -1 if errors == 0: html += "<td>%.3f%%</td>" % correct html += "<td>%.3f%%</td>" % not_mapped html += "<td>%d</td>" % throughput csv += "%s,%s,%.4f,%.4f,%d\n" % (test.getMapper().getTitle(), test.getMapper().param_string, correct, not_mapped, throughput) else: html += "<td></td>" html += "<td></td>" html += "<td></td>" csv += "%s,%s,-,-,-\n" % (test.getMapper().getTitle(), test.getMapper().param_string) html += "</tr>" csv_filename = self.writeCSV("overview", csv) html += "</table>" html += util.makeExportDropdown("", csv_filename) return html
def generateOverallScatterPlot(self, page, test_objects): import json csv = "parameter,mapped_percent,throughput_reads_per_sec\n" # csv = "correct_percent,reads_per_sec\n" columns = [] xs = {} labels = {} max_throughput = 0 for test in test_objects: if test.getRunResults() != None: throughput=-1 if test.getRunResults().maptime != 0: throughput=test.getRunResults().correct / test.getRunResults().maptime max_throughput = max(throughput, max_throughput) for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue mapper_name = test.getMapper().getTitle() + " " + test.getMapper().param_string correct,throughput=0,0 if result.total != 0: correct=result.correct / float(result.total) if result.maptime != 0: throughput=round((result.total / result.maptime), 0) columns.append([mapper_name + "_x", throughput, 0]) columns.append([mapper_name, correct]) xs[mapper_name] = mapper_name + "_x" labels[int(result.total / result.maptime)] = test.getMapper().getTitle() + " " + test.getMapper().param_string csv += "%s,%s,%f,%f\n" % (test.getMapper().getName(),test.getMapper().param_string,round(correct,3),throughput) csv_filename = self.writeCSV("overview_scatter",csv) page.addSection("Results Overview", generateTestList(self,test_objects) + """<p style="margin-top:15px;">The figure below visualizes above results by directly comparing mapped percentage and throughput.</p><div id="plot_os"></div>%s""" % util.makeExportDropdown("plot_os",csv_filename), None, """Mappers were evaluated for the given test <a href="#section2">data set</a>. The table below shows the used parameters, mapping statistics and throughput for each mapper. Detailed results for a mapper can be displayed by clicking on its name in the table or the navigation on top.""") show_legend = len(columns) < 30 page.addScript(""" var params=%s; var chart = c3.generate({ bindto: '#plot_os', size: { height: 500 }, data: { xs: %s, columns: %s, type: 'scatter' }, grid: { y: { show: true }, x: { show: true } }, axis: { x: { label: { text: "Throughput [Reads/s]", position: "outer-middle"}, tick: {fit: false, format: d3.format("d")}, padding: { left: 500 } }, y: { label: { text: "Mapped [%%]", position: "outer-middle"}, tick: { format: d3.format(".3%%") } } }, point: { r: 5 }, legend: { position: "bottom", show:%s, inset: { anchor: 'top-left', x: 20, y: 10, step: 2 } }, tooltip: { format: { //title: function (d) { return ""; } }, grouped: false } });""" % (json.dumps(labels), json.dumps(xs), json.dumps(columns), str(show_legend).lower()))
def generateMappingQualityOverview(self, page, test_objects): import json csv_rating = "mapper,mapq_threshold,filtered_wrong_per_filtered_correct\n" csv_distribution = "mapper,mapq_value,read_count\n" data = [] data_dist = [] x_values_dist = [] for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column = [test.getMapper().getTitle()] column_dist = [test.getMapper().getTitle()] x_values_dist = [] results = test.getRunResults() if results == None or test.getErrorCount(): continue mapqs = sorted(results.mapq_cumulated) for curr in mapqs: lost_correct = (results.correct - results.mapq_cumulated[curr]["correct"]) lost_wrong = (results.wrong - results.mapq_cumulated[curr]["wrong"]) if curr < 30 or (curr < 100 and curr % 10 == 0) or curr % 20 == 0 or curr == 255: try: column.append(round(lost_wrong / float(lost_correct), 4)) except ZeroDivisionError: column.append(0) if curr <= 255: column_dist.append( results.total - results.mapq_cumulated[curr]["correct"] - results.mapq_cumulated[curr]["wrong"]) else: column_dist.append(0) x_values_dist.append(curr) csv_rating += "%s,%d,%.4f\n" % (test.getMapper().getTitle(), curr, column[-1]) csv_distribution += "%s,%d,%d\n" % (test.getMapper().getTitle(), curr, column_dist[-1]) data.append(column) data_dist.append(column_dist) data.append(["x"] + x_values_dist) data_dist.append(["x"] + x_values_dist) csv_filename_distribution = self.writeCSV("mapq_distribution", csv_distribution) page.addSection( "Mapping Quality", """<div id="plot_mapq_dist"></div>%s""" % (util.makeExportDropdown("plot_mapq_dist", csv_filename_distribution)), None, "This plot shows the total number of mapped reads for each mapping quality threshold (all reads with a mapping quality value smaller or equal to the threshold)." ) page.addScript(""" var chart = c3.generate({ bindto: '#plot_mapq_dist', size: { height: 500 }, data: { x: 'x', columns: %s, type: 'step' }, grid: { y: { show: true } }, axis: { x: { label: {text: "Mapping Quality Threshold", position: "outer-middle"}, min: -5 }, y: { label: {text: "Number of Reads Mapped", position: "outer-middle"} } }, legend: { show:true }, point: { show: false } });""" % (json.dumps(data_dist)))
def generateResourcePlot(self, page, test_objects, measure): import json if measure == "runtime": columns = [["Runtime/mio. reads"]] title = "Runtime [min/mio. reads]" section_title = "Runtime" elif measure == "memory": columns = [["Memory Usage"]] title = "Memory Usage [MB]" section_title = "Memory Usage" elif measure == "corrects": columns = [["Correct/s"]] title = "Correctly Mapped [reads/s]" section_title = "Correctly Mapped/s" groups = [] csv = "mapper,%s\n" % measure for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): result = test.getRunResults() if result == None or test.getErrorCount(): continue groups.append(test.getMapper().getTitle()) try: if measure == "runtime": columns[0].append(round( (result.maptime / 60.0) * (1000000.0/result.total), 3)) elif measure == "memory": columns[0].append(round(result.memory / (1000 * 1000))) elif measure == "corrects": columns[0].append(round((result.correct) / result.maptime, 3)) except ZeroDivisionError: columns[0].append(0) csv += "%s,%f\n" % (test.getMapper().getTitle(),columns[0][-1]) csv_filename = self.writeCSV(measure,csv) page.addSection(section_title, """<div id="plot_resource_%s"></div>%s""" % (measure,util.makeExportDropdown("plot_resource_%s"%measure,csv_filename))) page.addScript(""" var chart = c3.generate({ bindto: '#plot_resource_%s', data: { columns: %s, type: 'bar', }, grid: { y: { show: true } }, axis: { x: { label: { text: "Mapper", position: "outer-middle" }, type: "category", categories: %s }, y: { label: { text: "%s", position: "outer-middle" }, tick: { format: d3.format("10.3f") } } }, legend: { position: "inset", show:false, inset: { anchor: 'top-right' } } });""" % (measure, json.dumps(columns), json.dumps(groups), title))
def generateMappingQualityPlot(self, page): results = self.getRunResults() columns = [["Correct"], ["Wrong"]] csv = "mapq_threshold,correct,wrong\n" correct = [] wrong = [] mc = max(1, float(results.total)) mw = max(1, float(results.total)) wrong_scale = float(results.correct / mc) / float((1 + results.wrong) / mw) mapqs = sorted(results.mapq_cumulated) for curr in mapqs: columns[0].append(results.mapq_cumulated[curr]["correct"] / float(mc)) columns[1].append( (-results.mapq_cumulated[curr]["wrong"] / float(mw)) * wrong_scale) csv += "%d,%d,%d\n" % (curr, results.mapq_cumulated[curr]["correct"], results.mapq_cumulated[curr]["wrong"]) csv_filename = self.writeCSV(self.getMapper().getName() + "_mapqs", csv) page.addSection( "Mapping Quality Thresholds", """<div id="plot_mapq_thresh"></div>%s""" % util.makeExportDropdown("plot_mapq_thresh", csv_filename), None, "The plot below shows the percentages of correctly and wrongly mapped reads for all mapping quality thresholds for this mapper. The values at threshold 0 therefore correspond to the unfiltered results." ) page.addScript(""" var cols=%s; var a=0; var b=0; var last_a=0; var last_b=0; function updateRange() { if(last_a==a && last_b==b) { setTimeout(updateRange,500); return; } an=Math.floor(parseInt(a)); bn=Math.floor(parseInt(b)); var mn = 0; var mx = 0; for(i=an;i<bn;i++) { mx=Math.max(mx,cols[0][i+1]); mn=Math.min(mn,cols[1][i+1]); } r={} r.max=mx; r.min=mn; chart.axis.range(r); last_a=a; last_b=b; setTimeout(updateRange,500); } setTimeout(updateRange,500); var chart = c3.generate({ bindto: '#plot_mapq_thresh', data: { columns: %s, type: "area" }, grid: { y: { show: true } }, axis: { x: { label: "Mapping Quality Threshold" }, y: { label: { text: "Read Count", position: "outer-middle" }, tick: { format: function(v) { if(v>=0){return Math.min(100,(Math.round(100000*v)/1000))+"%%";}else{ return Math.min(100,(Math.round(100000*(-v/%f))/1000))+"%%";} } } } }, legend: { position: "right" }, subchart: { show: true, onbrush: function (d) { a = d[0]; b = d[1]; } } });""" % (json.dumps(columns), json.dumps(columns), wrong_scale))
def generateEditDistancePlot(self, page, test_objects): import json data_dist = [] max_edit_dist = 0 reads_by_edit_distance = {} for test in test_objects: results = test.getRunResults() if len(results.reads_by_edit_distance) != 0: reads_by_edit_distance = results.reads_by_edit_distance max_edit_dist = max([key for key in reads_by_edit_distance]) x = [] for key in reads_by_edit_distance: if reads_by_edit_distance[key] > 100: x.append(key) for test in sorted(test_objects, key=lambda k: k.getMapper().getTitle()): column_dist = [test.getMapper().getTitle()] results = test.getRunResults() if results == None or test.getErrorCount(): continue reads_by_edit_distance = results.reads_by_edit_distance mapped_by_edit_distance = results.mapped_by_edit_distance for i in range(max_edit_dist + 1): mapped = mapped_by_edit_distance[ i] if i in mapped_by_edit_distance else 0 reads = reads_by_edit_distance[ i] if i in reads_by_edit_distance else 0 if reads > 100: column_dist.append((reads - mapped) / float(reads)) else: pass data_dist.append(column_dist) print(column_dist) data_dist.append(["x"] + x) page.addSection( "Edit Distance", """This plot shows the effect of the number of mismatches introduced in reads on mapping rates.<br><br><div id="plot_edit_dist"></div>%s""" % util.makeExportDropdown("plot_edit_dist", ""), None, "") page.addScript(""" var chart = c3.generate({ bindto: '#plot_edit_dist', size: { height: 500 }, data: { x: 'x', columns: %s }, grid: { y: { show: true } }, axis: { x: { label: {text: "Edit distance in read", position: "outer-middle"} }, y: { label: {text: "Not mapped reads [%%]", position: "outer-middle"}, tick: { format: d3.format(".3%%") } } }, legend: { show:true }, point: { show: false } });""" % json.dumps(data_dist))