Beispiel #1
0
def make_map_with_donuts(counts, outprefix, europe=False, debug=False):
    donut_coords = {
        'Australia': (369, 193),
        'Belgium': (195, 123),
        'Canada': (88, 91),
        'China': (330, 123),
        'Germany': (255, 120),
        'Italy': (285, 207),
        'Netherlands': (210, 105),
        'Pakistan': (288, 132),
        'Peru': (115, 180),
        'Russia': (314, 91),
        'Serbia': (372, 194),
        'Sierra Leone': (190, 150),
        'South Africa': (233, 201),
        'Spain': (110, 230),
        'Swaziland': (258, 210),
        'Thailand': (328, 143),
        'UK': (131, 96),
        'Uzbekistan': (282, 114),
        'Europe': (210, 110),
    }
    no_donuts_svg = f'{outprefix}.tmp.svg'
    final_svg = f'{outprefix}.svg'
    final_pdf = final_svg.replace('.svg', '.pdf')
    donut_files = make_donuts(counts, outprefix)
    make_map_no_donuts(no_donuts_svg, europe=europe)
    donut_size = 40 if europe else 32

    with open(no_donuts_svg) as f:
        svg_lines = [x.rstrip() for x in f]
    assert svg_lines[-1] == '</svg>'
    last_svg_line = svg_lines.pop()

    # line to point to Swaziland
    if not europe:
        svg_lines.append(
            '<line x1="280" y1="230" x2="262" y2="212" style="stroke:rgb(0,0,0);stroke-width:1" />'
        )

    for country in donut_files:
        x, y = donut_coords[country]
        filename = os.path.abspath(donut_files[country])
        svg_lines.append(
            f'<image x="{x}" y="{y}" width="{donut_size}" height="{donut_size}" xlink:href="file:{filename}"></image>'
        )

    svg_lines.append(last_svg_line)
    with open(final_svg, 'w') as f:
        print(*svg_lines, sep='\n', file=f)

    svg.svg2pdf(final_svg, final_pdf)

    if not debug:
        os.unlink(no_donuts_svg)
        os.unlink(final_svg)
        for filename in donut_files.values():
            os.unlink(filename)
Beispiel #2
0
def make_legend(outprefix, debug=False):
    svg_file = f'{outprefix}.svg'
    pdf_file = f'{outprefix}.pdf'
    s = r'''        <svg height="70pt" width="70pt">
        <text x="10" y="11">Dataset</text>
        <circle cx="11" cy="30" r="10" stroke="black" stroke-width="0.5" fill="''' + mykrobe_colour + r'''" />
        <text x="23" y="35">Training</text>
        <circle cx="11" cy="55" r="10" stroke="black" stroke-width="0.5" fill="''' + validate_colour + r'''" />
        <text x="23" y="60">Validation</text>
        <circle cx="11" cy="80" r="10" stroke="black" stroke-width="0.5" fill="''' + test_colour + r'''" />
        <text x="23" y="85">Test</text>
        </svg>'''

    with open(svg_file, 'w') as f:
        print(textwrap.dedent(s), file=f)

    svg.svg2pdf(svg_file, pdf_file)
    if not debug:
        os.unlink(svg_file)
def make_legend(tools, outfile, header=None):
    font_size = 14
    square_len = 20
    y_space = 3
    svg_lines = []
    y = 10
    square_left = 5
    square_right = square_left + square_len
    total_width = 200

    if header is not None:
        svg_lines.append(
            svg.svg_text(square_right,
                         y,
                         header,
                         font_size + 1,
                         position='start',
                         vertical_align='middle'))
        y += 15

    for tool in tools:
        svg_lines.append(
            svg.svg_rectangle(square_left, y, square_right, y + square_len,
                              common_data.tool_colours[tool], 'black'))
        svg_lines.append(
            svg.svg_text(square_right + 5,
                         y + 0.5 * square_len,
                         common_data.tool_names[tool],
                         font_size,
                         position='start',
                         vertical_align='middle'))
        y += square_len + y_space

    f = open(outfile, 'w')
    print(r'''<?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC " -//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg width="''' + str(total_width) + '" height="' + str(y) + '">',
          file=f)
    print(*svg_lines, sep='\n', file=f)
    print('</svg>', file=f)
    f.close()
    svg.svg2pdf(outfile, outfile.replace('.svg', '.pdf'))
Beispiel #4
0
def plot_one_tool(data, outfile, ignore=None, y_scale=0.8):
    assert outfile.endswith('.svg')
    to_ignore = {}
    if ignore is not None:
        data = copy.deepcopy(data)
        for k1, k2 in ignore:
            if k1 in data and k2 in data[k1]:
                if k1 not in to_ignore:
                    to_ignore[k1] = {}
                to_ignore[k1][k2] = data[k1][k2]
                del data[k1][k2]
                if len(data[k1]) == 1:
                    del data[k1]

    for k in data:
        try:
            del data[k][99]
        except:
            pass

    colours = [
     '#f0e8e3',
     '#e2d3c9',
     '#f5cfb6',
     '#FDD49E',
     '#FDBB84',
     '#FCA964',
     '#FC8D59',
     '#E34A33',
     '#d81529',
     '#CE1256',
     '#980043',
     '#400013'
    ]

    truth_nodes = {}
    called_nodes = {}
    edges = []

    for truth in data:
        if truth not in truth_nodes:
            truth_nodes[truth] = {}

        for called in data[truth]:
            if called not in called_nodes:
                called_nodes[called] = {}

            truth_nodes[truth][called] = data[truth][called]
            called_nodes[called][truth] = data[truth][called]
            edges.append((truth, called, data[truth][called]))

    plot_width = 1050

    drugs = OrderedDict([
        ('H', 'Isoniazid'),
        ('R', 'Rifampicin'),
        ('Z', 'Pyrazinamide'),
        ('E', 'Ethambutol'),
        ('Rfb', 'Rifabutin'),
        ('Rpt', 'Rifapentine'),
        ('Lfx', 'Levofloxacin'),
        ('Mfx', 'Moxifloxacin'),
        ('Gfx', 'Gatifloxacin'),
        ('S', 'Streptomycin'),
        ('Km', 'Kanamycin'),
        ('Am', 'Amikacin'),
        ('Cm', 'Capreomycin'),
        ('PAS', 'Para-aminosalicylate/Para-aminosalicylate-sodium'),
        ('Eto', 'Ethionamide'),
        ('Pto', 'Prothionamide'),
        ('Cs', 'Cycloserine'),
        ('Trd', 'Terizidone'),
        ('Cfz', 'Clofazimide'),
        ('Lzd', 'Linezolid'),
        ('hH', 'High dose Isoniazid'),
        ('hZ', 'High dose Pyrazinamide'),
        ('hE', 'High dose Ethambutol'),
        ('X', 'Amox-Clavulanate, Imipenem/Cilastatin, Meropenem, High dose Isoniazid (if possible)'),
    ])

    regimen_to_drug = {
        1: {'req': ('H', 'R', 'Z', 'E')},
        2: {'req': ('R', 'Z', 'E'), 'opt': ((1, 'Lfx', 'Mfx', 'Gfx'),)},
        3: {'req': ('R', 'Z', 'E')},
        4: {'req': ('R', 'E', (1, 'Lfx', 'Mfx', 'Gfx'))},
        5: {'req': ('R', 'Z', (1, 'Lfx', 'Mfx', 'Gfx'))},
        6: {'req': ('R', 'E', (1, 'Lfx', 'Mfx', 'Gfx'), (1, 'Km', 'Am', 'Cm'), (1, 'Eto', 'Pto', 'PAS', 'Cs', 'Trd'))},
        7: {'req': ('R', 'E', 'S', (1, 'Eto', 'Pto', 'PAS', 'Cs', 'Trd'))},
        8: {'req': ('H', 'R', 'E')},
        9: {'req': ('H', 'R', 'Z')},
        10: {'req': ('Z', (1, 'Lfx', 'Mfx', 'Gfx'), (1, 'Km', 'Am', 'Cm'), (2, 'Eto', 'Pto', 'Cs', 'Trd', 'Cfz', 'Lzd')), 'opt': ('hH', 'hE')},
        11: {'req': ((1, 'Gfx', 'Mfx'), 'Km', 'Pto', 'Cfz', 'hH', 'hE', 'hZ', )},
        12: {'req': ('Rfb', 'Rpt', 'E', 'Z', (1, 'Lfx', 'Mfx', 'Gfx'), (1, 'Km', 'Am', 'Cm', 'S'), 'Eto', 'Pto', 'PAS', 'Cs', 'Trd', 'hH', 'X')},
    }

    regimen_to_pheno = {
        1: {'H': 'S', 'R': 'S', 'Z': 'S', 'E': 'S'},
        2: {'H': 'R', 'R': 'S', 'Z': 'S', 'E': 'S'},
        3: {'H': 'R', 'R': 'S', 'Z': 'S', 'E': 'S', 'Mfx': 'R'},
        4: {'H': 'R', 'R': 'S', 'Z': 'R', 'E': 'S'},
        5: {'H': 'R', 'R': 'S', 'Z': 'S', 'E': 'R'},
        6: {'H': 'R', 'R': 'S', 'Z': 'R', 'E': 'R'},
        7: {'H': 'R', 'R': 'S', 'Z': 'R', 'E': 'R', 'Km': 'R', 'Am': 'R', 'Cm': 'R', 'S': 'S'},
        8: {'H': 'S', 'R': 'S', 'Z': 'R', 'E': 'S'},
        9: {'H': 'S', 'R': 'S', 'Z': 'S', 'E': 'R'},
        10: {'H': '(R)', 'R': 'R'},
        11: {'H': '(R)', 'R': 'R', 'Km': 'S', 'Mfx': 'S'},
        12: {'H': 'R', 'R': 'R', 'Mfx': 'R'},
    }

    drug_col_width = 26
    svg_lines = []
    x = 10
    headings_y = 20
    drug_to_x_centre = {}

    for drug in drugs:
        drug_to_x_centre[drug] = x + 0.5 * drug_col_width
        svg_lines.append(svg.svg_text(x + 0.5 * drug_col_width, headings_y, drug, 12, vertical_align='middle'))
        x += drug_col_width

    x += 80
    right_half_x_left  = x + 10
    node_to_edge_space = 50
    left_node_x = right_half_x_left + node_to_edge_space
    node_width = 20
    right_node_x = plot_width - node_to_edge_space - node_width
    y_start = 40
    node_y_gap = 30
    svg_node_lines = []
    truth_nodes_y_tops = {}
    truth_nodes_y_bottoms = {}
    called_nodes_y_tops = {}
    truth_node_to_y_centre = {}

    # Nodes on the left
    y = y_start
    svg_lines.append(svg.svg_text(left_node_x - 110, headings_y, 'Regimen', 11, position='middle', font_family='arial', vertical_align='middle'))
    svg_lines.append(svg.svg_text(left_node_x - 5, headings_y, 'Samples', 11, position='end', font_family='arial', vertical_align='middle'))

    for node in regimen_to_drug:
        if node-1 not in truth_nodes:
            truth_nodes[node-1] = {}
        if node-1 not in called_nodes:
            called_nodes[node-1] = {}

    for node, node_counts in sorted(truth_nodes.items()):
        truth_nodes_y_tops[node] = y
        if len(node_counts) > 0:
            total_samples = sum(node_counts.values())
        else:
            total_samples = 0
        node_y_bottom = y + y_scale * total_samples
        truth_node_to_y_centre[node] = 0.5 * (y + node_y_bottom)
        svg_node_lines.append(svg.svg_rectangle(left_node_x, y, left_node_x + node_width, node_y_bottom,
            colours[int(node)], colours[int(node)], border_width=1))
        #svg_lines.append(svg.svg_text(left_node_x - 110, 0.5 * (y + node_y_bottom),
        #    who_treatment.regimens[node+1].definition, 11, position='start', font_family='arial', vertical_align='middle'))
        svg_lines.append(svg.svg_text(left_node_x - 110, 0.5 * (y + node_y_bottom), str(node+1), 11, position='middle', font_family='arial', vertical_align='middle'))
        svg_lines.append(svg.svg_text(left_node_x - 5, 0.5 * (y + node_y_bottom),
            str(total_samples), 11, position='end', font_family='arial', vertical_align='middle'))

        if node in to_ignore:
            total_ignored = sum(to_ignore[node].values())
            svg_lines.append(svg.svg_text(left_node_x - 5, 0.5 * (y + node_y_bottom) + 12,
                f'(+{total_ignored})', 11, position='end', font_family='arial', vertical_align='middle'))

        y = node_y_bottom + node_y_gap
        truth_nodes_y_bottoms[node] = node_y_bottom

    y = y_start

    # Nodes on the right
    for node, node_counts in sorted(called_nodes.items()):
        called_nodes_y_tops[node] = y
        total_samples = sum(node_counts.values())
        node_y_bottom = y + y_scale * total_samples
        svg_node_lines.append(svg.svg_rectangle(right_node_x, y, right_node_x + node_width, node_y_bottom,
            colours[int(node)], colours[int(node)], border_width=1))
        svg_lines.append(svg.svg_text(right_node_x + node_width + 3, 0.5 * (y + node_y_bottom),
            str(total_samples), 11, position='start', font_family='arial', vertical_align='middle'))
        if node in to_ignore:
            total_ignored = sum(to_ignore[node].values())
            svg_lines.append(svg.svg_text(right_node_x + node_width + 3, 0.5 * (y + node_y_bottom) + 12,
                f'(+{total_ignored})', 11, position='start', font_family='arial', vertical_align='middle'))

        y = node_y_bottom + node_y_gap

    # Ribbons
    sample_total_counts = {'truth': {}, 'called': {}}

    for truth_node, called_node, samples in edges:
        if truth_node not in sample_total_counts['truth']:
            sample_total_counts['truth'][truth_node] = 0
        if called_node not in sample_total_counts['called']:
            sample_total_counts['called'][called_node] = 0

        y11 = truth_nodes_y_tops[truth_node] + y_scale * sample_total_counts['truth'][truth_node]
        sample_total_counts['truth'][truth_node] += samples
        y12 = truth_nodes_y_tops[truth_node] + y_scale * sample_total_counts['truth'][truth_node]
        y21 = called_nodes_y_tops[called_node] + y_scale * sample_total_counts['called'][called_node]
        sample_total_counts['called'][called_node] += samples
        y22 = called_nodes_y_tops[called_node] + y_scale * sample_total_counts['called'][called_node]
        svg_lines.append(svg.svg_ribbon(left_node_x + node_width, y11, y12, right_node_x, y21, y22,
            colours[int(truth_node)], colours[int(truth_node)], border_width=0.5, opacity=0.8))

    # Regimen circles and R/S
    svg_regimen_lines = []
    circle_radius = 0.1 * drug_col_width
    y_circle_R_S_offset = 7
    pheno_letters_lines = []

    for node, regime in regimen_to_drug.items():
        y_centre = truth_node_to_y_centre[node-1]
        y_circle_centre = y_centre + y_circle_R_S_offset
        y_R_or_S = y_centre - y_circle_R_S_offset

        for req_or_opt in regime:
            if req_or_opt not in regime:
                continue

            if req_or_opt == 'req':
                line_colour = 'black'
            else:
                line_colour = 'lightgrey'

            for drugs_tuple in regime[req_or_opt]:
                if type(drugs_tuple) is tuple:
                    x_min = float('Inf')
                    x_max = 0
                    to_append = []
                    number_of_lines = drugs_tuple[0]
                    if number_of_lines == 1:
                        lines_y = [y_circle_centre]
                        line_width = 1.5
                    else:
                        assert number_of_lines == 2
                        lines_y = [y_circle_centre - circle_radius / 2, y_circle_centre + circle_radius / 2]
                        line_width = 1

                    for drug in drugs_tuple[1:]:
                        to_append.append(svg.svg_circle(drug_to_x_centre[drug], y_circle_centre, 0.2 * drug_col_width, colours[node-1], line_colour, stroke_width=1))
                        x_min = min(x_min, drug_to_x_centre[drug])
                        x_max = max(x_max, drug_to_x_centre[drug])

                    for line_y in lines_y:
                        svg_regimen_lines.append(svg.svg_line(x_min, line_y, x_max, line_y, line_colour, line_width))
                    svg_regimen_lines.append(to_append)
                else:
                    svg_regimen_lines.append(svg.svg_circle(drug_to_x_centre[drugs_tuple], y_circle_centre, 0.2 * drug_col_width, colours[node-1], line_colour, stroke_width=1))

        for drug, pheno in regimen_to_pheno[node].items():
            pheno_letters_lines.append(svg.svg_text(drug_to_x_centre[drug], y_R_or_S, pheno, 10, vertical_align='middle'))

    # Make vertical lines to separate drugs
    y_top = min(truth_nodes_y_tops.values())
    y_top = 5
    y_bottom = max(truth_nodes_y_bottoms.values())
    y_bottom = y - 5
    for drug in drugs:
        x_pos = drug_to_x_centre[drug] - 0.5 * drug_col_width
        svg_lines.append(svg.svg_line(x_pos, y_top, x_pos, y_bottom, 'lightgrey', 1))
        if drug == 'X':
            x_pos = drug_to_x_centre[drug] + 0.5 * drug_col_width
            svg_lines.append(svg.svg_line(x_pos, y_top, x_pos, y_bottom, 'lightgrey', 1))

    # horizontal lines to separate regimens
    x_left = min(drug_to_x_centre.values()) - 0.5 * drug_col_width
    x_right = left_node_x
    y_top_line = truth_nodes_y_tops[0] - 10
    svg_lines.append(svg.svg_line(x_left, y_top, x_right, y_top, 'lightgrey', 1))
    svg_lines.append(svg.svg_line(x_left, y_top_line, x_right, y_top_line, 'lightgrey', 1))
    svg_lines.append(svg.svg_line(x_left, y_bottom, x_right, y_bottom, 'lightgrey', 1))
    for i in range(0, len(truth_nodes_y_bottoms) - 1, 1):
        y_pos = 0.5 * (truth_nodes_y_bottoms[i] + truth_nodes_y_tops[i+1])
        svg_lines.append(svg.svg_line(x_left, y_pos, x_right, y_pos, 'lightgrey', 1))

    f = open(outfile, 'w')
    print(r'''<?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC " -//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg width="''' + str(plot_width) + '" height="' + str(y) + '">', file=f)
    # plot nodes last so that borders are on top of ribbons and therefore still visible
    print(*svg_lines, svg_node_lines, svg_regimen_lines, pheno_letters_lines, sep='\n', file=f)
    print('</svg>', file=f)
    f.close()
    svg.svg2pdf(outfile, outfile.replace('.svg', '.pdf'))
def make_plot(stats_dict,
              tools,
              drugs,
              outfile,
              first_line=False,
              ten_k=False,
              how_to_scale='not at all',
              susc_gap=None,
              susc_xticks=None,
              res_xticks=None,
              plot_gap_size=30,
              extra_width=0):
    if susc_gap is not None:
        susc_gap_width = susc_gap[1] - susc_gap[0]
        stats_dict = copy.deepcopy(stats_dict)
        for drug in drugs:
            for tool in tools:
                if stats_dict[drug][tool]['TN'] > susc_gap[1]:
                    stats_dict[drug][tool][
                        'TN'] -= susc_gap_width - plot_gap_size

    max_count_res_keys = ['FN', 'TP', 'UNK_R', 'FAIL_R']
    max_count_susc_keys = ['FP', 'TN', 'UNK_S', 'FAIL_S']
    sample_count = {}

    for drug in drugs:
        res_counts = set()
        susc_counts = set()
        for tool in tools:
            if tool == '10k_predict':
                continue
            res_count = sum(
                [stats_dict[drug][tool][x] for x in max_count_res_keys])
            susc_count = sum(
                [stats_dict[drug][tool][x] for x in max_count_susc_keys])
            res_counts.add(res_count)
            susc_counts.add(susc_count)
        assert len(res_counts) == len(susc_counts) == 1

        sample_count[drug] = {
            'res': res_counts.pop(),
            'susc': susc_counts.pop()
        }

    max_count_res = max([sample_count[x]['res'] for x in sample_count])
    max_count_susc = max([sample_count[x]['susc'] for x in sample_count])
    centre_space = 20
    x_margin = 10

    if how_to_scale == 'not at all':
        total_width = 600
        total_bar_width = total_width - 2 * (centre_space + x_margin)
        susc_bar_width = total_bar_width * max_count_susc / (max_count_res +
                                                             max_count_susc)
        res_bar_width = total_bar_width * max_count_res / (max_count_res +
                                                           max_count_susc)
        x_centre = x_margin + susc_bar_width + centre_space
    elif how_to_scale == 'res susc independent':
        total_width = 1000
        total_bar_width = total_width - 2 * (centre_space + x_margin)
        susc_bar_width = 0.5 * total_bar_width
        res_bar_width = 0.5 * total_bar_width
        x_centre = total_width / 2
    elif how_to_scale == 'all to 100':
        total_width = 500
        total_bar_width = total_width - 2 * (centre_space + x_margin)
        susc_bar_width = 0.5 * total_bar_width - 40
        res_bar_width = 0.5 * total_bar_width - 40
        x_centre = total_width / 2

    bar_height = 13
    drug_spacer = 10
    x_susc_bar_left = x_centre - centre_space - susc_bar_width
    x_res_bar_right = x_centre + centre_space + res_bar_width

    y = 20

    svg_lines = []
    svg_lines.append(
        svg.svg_text(0.5 * (x_susc_bar_left + x_centre - centre_space), y,
                     'Susceptible samples', 20))
    svg_lines.append(
        svg.svg_text(0.5 * (x_centre + centre_space + x_res_bar_right), y,
                     'Resistant samples', 20))
    y = 45
    susc_tick_zeros = len(str(max_count_susc)) - 1
    susc_tick_gap = int('1' + '0' * susc_tick_zeros)
    susc_ticks = [
        -x for x in range(round(-max_count_susc, -susc_tick_zeros), 1,
                          susc_tick_gap) if x > -max_count_susc
    ]
    res_tick_zeros = len(str(max_count_res)) - 1
    res_tick_gap = int('1' + '0' * res_tick_zeros)
    res_ticks = list(
        range(0,
              round(max_count_res, -res_tick_zeros) + 1, res_tick_gap))

    if how_to_scale == 'all to 100':
        susc_ticks = [
            0, 0.25 * max_count_susc, 0.5 * max_count_susc,
            0.75 * max_count_susc, max_count_susc
        ]
        res_ticks = [
            0, 0.25 * max_count_res, 0.5 * max_count_res, 0.75 * max_count_res,
            max_count_res
        ]
        susc_ticks_labels = ['0', '25', '50', '75', '100']
        res_ticks_labels = ['0', '25', '50', '75', '100']
    else:
        if susc_xticks is not None and susc_gap is not None:
            susc_ticks_labels = [str(x) for x in susc_xticks]
            susc_ticks = []
            for i in susc_xticks:
                if i < susc_gap[0]:
                    susc_ticks.append(i)
                else:
                    susc_ticks.append(i - (susc_gap_width - plot_gap_size))
        else:
            susc_ticks_labels = [str(x) for x in susc_ticks]

        if res_xticks is not None:
            res_ticks = res_xticks
        res_ticks_labels = [str(x) for x in res_ticks]

    svg_lines.append(
        svg.svg_line(x_susc_bar_left, y, x_centre - centre_space, y, 'black',
                     1))
    svg_lines.append(
        svg.svg_line(x_centre + centre_space, y, x_res_bar_right, y, 'black',
                     1))
    y_tick = y

    y += 5

    for drug in drugs:
        y_drug_top = y
        for tool in tools:
            if drug not in common_data.first_line_drugs and tool == '10k_predict':
                continue
            stats = stats_dict[drug][tool]
            y_bottom = y + bar_height
            if how_to_scale == 'all to 100':
                if sample_count[drug]['res'] == 0:
                    x_fail = x_fn = x_right = x_centre + centre_space
                else:
                    x_fail = x_centre + centre_space + res_bar_width * (
                        stats['UNK_R'] +
                        stats['FAIL_R']) / sample_count[drug]['res']
                    x_fn = x_fail + res_bar_width * (stats['FN'] /
                                                     sample_count[drug]['res'])
                    x_right = x_centre + centre_space + res_bar_width
            else:
                x_fail = x_centre + centre_space + res_bar_width * (
                    (stats['UNK_R'] + stats['FAIL_R']) / max_count_res)
                x_fn = x_fail + res_bar_width * (stats['FN'] / max_count_res)
                x_right = x_centre + centre_space + res_bar_width * (
                    sample_count[drug]['res'] / max_count_res)
            svg_lines.append(
                svg.svg_rectangle(x_centre + centre_space, y, x_fail, y_bottom,
                                  common_data.other_colours['UNK_R'], 'black'))
            svg_lines.append(
                svg.svg_rectangle(x_fail, y, x_fn, y_bottom,
                                  common_data.other_colours['FN'], 'black'))
            svg_lines.append(
                svg.svg_rectangle(x_fn, y, x_right, y_bottom,
                                  common_data.tool_colours[tool], 'black'))

            if how_to_scale == 'all to 100':
                if sample_count[drug]['susc'] == 0:
                    x_fail = x_fn = x_right = x_centre - centre_space
                else:
                    x_fail = x_centre - centre_space - susc_bar_width * (
                        (stats['UNK_S'] + stats['FAIL_S']) /
                        sample_count[drug]['susc'])
                    x_fp = x_fail - susc_bar_width * stats[
                        'FP'] / sample_count[drug]['susc']
                    x_left = x_centre - centre_space - susc_bar_width
            else:
                x_fail = x_centre - centre_space - susc_bar_width * (
                    (stats['UNK_S'] + stats['FAIL_S']) / max_count_susc)
                x_fp = x_fail - susc_bar_width * (stats['FP'] / max_count_susc)
                x_left = x_centre - centre_space - susc_bar_width * (
                    sample_count[drug]['susc'] / max_count_susc)
            svg_lines.append(
                svg.svg_rectangle(x_fail, y, x_centre - centre_space, y_bottom,
                                  common_data.other_colours['UNK_S'], 'black'))
            svg_lines.append(
                svg.svg_rectangle(x_fp, y, x_fail, y_bottom,
                                  common_data.other_colours['FN'], 'black'))
            svg_lines.append(
                svg.svg_rectangle(x_left, y, x_fp, y_bottom,
                                  common_data.tool_colours[tool], 'black'))

            y += bar_height

        svg_lines.append(
            svg.svg_text(x_centre,
                         0.5 * (y_drug_top + y),
                         common_data.drug_abbreviations[drug],
                         15,
                         vertical_align='middle'))
        if how_to_scale == 'all to 100':
            svg_lines.append(
                svg.svg_text(x_susc_bar_left - 3,
                             0.5 * (y_drug_top + y),
                             str(sample_count[drug]['susc']),
                             15,
                             position='end',
                             vertical_align='middle'))
            svg_lines.append(
                svg.svg_text(x_res_bar_right + 3,
                             0.5 * (y_drug_top + y),
                             str(sample_count[drug]['res']),
                             15,
                             position='start',
                             vertical_align='middle'))

        y += drug_spacer

    grid_lines = []

    for x, label in zip(res_ticks, res_ticks_labels):
        x_pos = x_centre + centre_space + res_bar_width * x / max_count_res
        svg_lines.append(
            svg.svg_line(x_pos, y_tick - 4, x_pos, y_tick, 'black', 1))
        svg_lines.append(svg.svg_text(x_pos, y_tick - 7, label, 15))
        grid_lines.append(
            svg.svg_line(x_pos, y_tick, x_pos, y - drug_spacer, 'lightgray',
                         1))
    for x, label in zip(susc_ticks, susc_ticks_labels):
        x_pos = x_centre - centre_space - susc_bar_width * x / max_count_susc
        svg_lines.append(
            svg.svg_line(x_pos, y_tick - 4, x_pos, y_tick, 'black', 1))
        svg_lines.append(svg.svg_text(x_pos, y_tick - 7, label, 15))
        grid_lines.append(
            svg.svg_line(x_pos, y_tick, x_pos, y - drug_spacer, 'lightgray',
                         1))

    if susc_gap is not None:
        svg_lines.append(r'''<defs>
    <linearGradient id="gradwhiteleft" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="50%" style="stop-color:rgb(255,255,255);stop-opacity:-1" />
      <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1" />
    </linearGradient>
  </defs>''')
        svg_lines.append(r'''<defs>
    <linearGradient id="gradwhiteright" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="50%" style="stop-color:rgb(255,255,255);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:-1" />
    </linearGradient>
  </defs>''')

        x_left = x_centre - centre_space - susc_bar_width * (
            (susc_gap[0] + plot_gap_size) / max_count_susc)
        x_right = x_centre - centre_space - susc_bar_width * (susc_gap[0] /
                                                              max_count_susc)
        x_middle = 0.5 * (x_left + x_right)
        svg_lines.append(
            svg.svg_rectangle(x_left,
                              y_tick + 1,
                              x_middle,
                              y + 1,
                              'url(#gradwhiteleft)',
                              'white',
                              border_width=0))
        svg_lines.append(
            svg.svg_rectangle(x_middle,
                              y_tick + 1,
                              x_right,
                              y + 1,
                              'url(#gradwhiteright)',
                              'white',
                              border_width=0))
        svg_lines.append(
            svg.svg_line(x_left + 3, y_tick, x_right, y_tick, 'white', 1))
        svg_lines.append(
            svg.svg_line(x_right - 3, y_tick + 3, x_right + 3, y_tick - 3,
                         'black', 1))
        svg_lines.append(
            svg.svg_line(x_left, y_tick + 3, x_left + 6, y_tick - 3, 'black',
                         1))

    with open(outfile, 'w') as f:
        print(r'''<?xml version="1.0" standalone="no"?>
        <!DOCTYPE svg PUBLIC " -//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
        <svg width="''' + str(total_width + extra_width) + '" height="' +
              str(y) + '">',
              file=f)
        print(*grid_lines, sep='\n', file=f)
        print(*svg_lines, sep='\n', file=f)
        print('</svg>', file=f)

    svg.svg2pdf(outfile, outfile.replace('.svg', '.pdf'))