def create_tree(tree, tree_root_key, filepath, patient, phylogeny, drivers=None): """ Takes a networkx tree and creates an ete3 tree and creates a PDF, SVG or PNG according to the given filename at the given path :param tree: inferred cancer phylogeny encoded as networkx tree :param tree_root_key: key of root in networkx tree :param filepath: path to output file (excluding file extension) :param patient: data structure around patient :param phylogeny: inferred cancer phylogeny :param drivers: defaultdict with mutation IDs and instance of driver class to be highlighted on edges :return path to the generated ete3 PNG file """ # generate ete3 tree rt = Tree() # generate root rt.name = 'Germline {}'.format(patient.name) _generate_ete_tree(tree, tree_root_key, rt, 0, patient, phylogeny, gene_names=patient.gene_names, drivers=drivers) ts = TreeStyle() ts.layout_fn = ete_layout ts.show_leaf_name = False ts.show_branch_length = False ts.show_branch_support = False ts.show_scale = False ts.extra_branch_line_color = 'Black' # ts.complete_branch_lines_when_necessary = False # Find the maximal number of variants present in a single sample to determine the optimal ETE3 scale level max_muts = max(no_pres_vars for no_pres_vars in patient.no_present_vars.values()) def round_to_1(x): """ Round to one significant digit :param x: :return: """ return round(x, -int(math.floor(math.log10(abs(x))))) # XX pixels per branch length unit # if max_muts > 20000: # ts.scale = 0.02 # elif max_muts > 10000: # ts.scale = 0.05 # elif max_muts > 5000: # ts.scale = 0.1 # elif max_muts > 2000: # ts.scale = 0.2 # elif max_muts > 1000: # ts.scale = 0.5 # elif max_muts > 500: # ts.scale = 1 # elif max_muts > 200: # ts.scale = 2 # elif max_muts > 100: # ts.scale = 5 # elif max_muts > 50: # ts.scale = 5 # else: # ts.scale = 10 ts.scale = round_to_1(600 / max_muts) ts.branch_vertical_margin = 15 # XX pixels between adjacent branches # rt.show(tree_style=ts) ete_tree_plot = filepath + '.png' rt.render(ete_tree_plot, w=183, units="mm", tree_style=ts, dpi=300) rt.render(filepath + '.pdf', w=183, units="mm", tree_style=ts, dpi=300) return ete_tree_plot
i += 1 sp_t.write(format=1, features=["D", "S"], outfile=args.o + ".nhx", format_root_node=True) if os.environ.has_key("DISPLAY"): from ete3 import TreeStyle, TextFace # Basic tree style tree_style = TreeStyle() tree_style.show_leaf_name = False tree_style.show_branch_length = False tree_style.show_scale = False tree_style.extra_branch_line_type = 0 # 0=solid, 1=dashed, 2=dotted tree_style.extra_branch_line_color = "black" i = 0 for n in sp_t.traverse(): n.dist = 5 Dstrinq = " D: " + str(Res_dict[str(i)]["D"]) Sstrinq = " S: " + str(Res_dict[str(i)]["S"]) n.add_face(TextFace(Dstrinq, fgcolor="#1C01AC"), column=0, position='branch-bottom') n.add_face(TextFace(Sstrinq, fgcolor="#800080"), column=0, position='branch-bottom') if n.is_leaf(): n.add_face(TextFace( " " + n.name,
def main(): parser = argparse.ArgumentParser() parser.add_argument("-t", action="store", dest="treef", help="The tree file") parser.add_argument("-s", action="store", dest="statf", help="The statistics file") parser.add_argument("-c", action="store", default=0, type=float, dest="scaling", help="If this parameter is set " "to more than 0, " "the size of the pie charts " "correlate with the total " "number of events at a node " "(and are scaled by the factor " "given as a float).") parser.add_argument( "-e", action="store", type=str, default="all", dest="event", help= "If an event type is specified, just this event type is visualized. Per default all event types are shown on the tree.\n" "all\n" "fusions\n" "fissions\n" "termLosses\n" "termEmergences\n" "singleDomainLosses\n" "singleDomainEmergences") parser.add_argument( "-p", action="store", dest="treeshape", default="r", choices=["c", "r"], help="shape of the tree, circle (c) or tree format (r)") parser.add_argument("-o", action="store", dest="outputname") parser.add_argument( "-y", action="store", type=str, dest="NodeIDtreeName", default=None, help="Name for output file that shows a tree with all node IDs.") parser.add_argument( "-l", dest="short_legend", help= "Writes the full legend for all events in two levels for short trees", action="store_true") params = parser.parse_args() if (params.event not in ("all", "fusions", "fissions", "termLosses", "termEmergences", "singleDomainLosses", "singleDomainEmergences")): print( "Error: Please specify a valid event type. For a list of possible options use the --help parameter." ) sys.exit(1) if params.NodeIDtreeName != None: id_tree = Tree(params.treef, format=0) coun = 0 for node in id_tree.traverse('preorder'): node.add_features(ID=coun) coun += 1 # Create empty TreeStyle ts = TreeStyle() # Set custom layout function ts.layout_fn = layout_idtree # Draw tree ts.mode = params.treeshape ts.complete_branch_lines_when_necessary = True ts.extra_branch_line_type = 0 ts.extra_branch_line_color = "black" # ts.optimal_scale_level ="full" ts.branch_vertical_margin = 40 ts.scale = 100 # We will add node names manually ts.show_leaf_name = False ts.draw_guiding_lines = True if (params.NodeIDtreeName.endswith(".pdf")): pathout = params.NodeIDtreeName else: pathout = params.NodeIDtreeName + ".pdf" id_tree.render(pathout, dpi=1200, tree_style=ts) pl.close() else: tree = Tree(params.treef, format=0) # Read statistics file node_stat_dict = {} with open(params.statf, "r") as sf: for line in sf: # Stop the loop at the second part of statistics file if line.startswith( "# Number of events per domain." ) or line.startswith( "# Events per domain arrangement for last common ancestor" ): break if line[0] not in ('#', '\n'): vecline = line.strip().split() id = vecline.pop(0) stats = [int(i) for i in vecline] node_stat_dict[int(id)] = stats # determine max. number of events per node for scaling fus_max = 0 fis_max = 0 termLoss_max = 0 termGain_max = 0 singLoss_max = 0 singGain_max = 0 tot_max = 0 # Assign rearrangement events to leaves c = 0 for node in tree.traverse('preorder'): node.add_features(fusion=node_stat_dict[c][0]) if (node_stat_dict[c][0] > fus_max): fus_max = node_stat_dict[c][0] node.add_features(fission=node_stat_dict[c][1]) if (node_stat_dict[c][1] > fis_max): fis_max = node_stat_dict[c][1] node.add_features(termLoss=node_stat_dict[c][2]) if (node_stat_dict[c][2] > termLoss_max): termLoss_max = node_stat_dict[c][2] node.add_features(termGain=node_stat_dict[c][3]) if (node_stat_dict[c][3] > termGain_max): termGain_max = node_stat_dict[c][3] node.add_features(singLoss=node_stat_dict[c][4]) if (node_stat_dict[c][4] > singLoss_max): singLoss_max = node_stat_dict[c][4] node.add_features(singGain=node_stat_dict[c][5]) if (node_stat_dict[c][5] > singGain_max): singGain_max = node_stat_dict[c][5] if (sum(node_stat_dict[c]) > tot_max): tot_max = sum(node_stat_dict[c]) c += 1 global scal scal = params.scaling global eve event_options = { "all": 0, "fusions": 1, "fissions": 2, "termLosses": 3, "termEmergences": 4, "singleDomainLosses": 5, "singleDomainEmergences": 6 } eve = event_options[params.event] # Create empty TreeStyle ts = TreeStyle() # Set custom layout function ts.layout_fn = layout_gen_events # Draw tree ts.mode = params.treeshape ts.complete_branch_lines_when_necessary = True ts.extra_branch_line_type = 0 ts.extra_branch_line_color = "black" #ts.optimal_scale_level ="full" ts.branch_vertical_margin = 40 ts.scale = 100 # We will add node names manually ts.show_leaf_name = False # legend creation if (params.event == "all"): ts.legend.add_face(CircleFace(10, "DimGray"), column=0) ts.legend.add_face(TextFace(" Fusion ", fsize=16, fgcolor='DimGray'), column=1) ts.legend.add_face(CircleFace(10, "DeepPink"), column=2) ts.legend.add_face(TextFace(' Fission ', fsize=16, fgcolor='DeepPink'), column=3) ts.legend.add_face(CircleFace(10, "YellowGreen"), column=4) ts.legend.add_face(TextFace(' Terminal Loss ', fsize=16, fgcolor='YellowGreen'), column=5) if params.short_legend: ts.legend.add_face(CircleFace(10, "DarkBlue"), column=0) ts.legend.add_face(TextFace(' Terminal Emergence ', fsize=16, fgcolor='DarkBlue'), column=1) ts.legend.add_face(CircleFace(10, "Chocolate"), column=2) ts.legend.add_face(TextFace(' Single Domain Loss ', fsize=16, fgcolor='Chocolate'), column=3) ts.legend.add_face(CircleFace(10, "DeepSkyBlue"), column=4) ts.legend.add_face(TextFace(' Single Domain Emergence ', fsize=16, fgcolor='DeepSkyBlue'), column=5) else: ts.legend.add_face(CircleFace(10, "DarkBlue"), column=6) ts.legend.add_face(TextFace(' Terminal Emergence ', fsize=16, fgcolor='DarkBlue'), column=7) ts.legend.add_face(CircleFace(10, "Chocolate"), column=8) ts.legend.add_face(TextFace(' Single Domain Loss ', fsize=16, fgcolor='Chocolate'), column=9) ts.legend.add_face(CircleFace(10, "DeepSkyBlue"), column=10) ts.legend.add_face(TextFace(' Single Domain Emergence ', fsize=16, fgcolor='DeepSkyBlue'), column=11) elif (params.event == "fusions"): ts.legend.add_face(CircleFace(10, "DimGray"), column=0) ts.legend.add_face(TextFace(" Fusion ", fsize=16, fgcolor='DimGray'), column=1) elif (params.event == "fissions"): ts.legend.add_face(CircleFace(10, "DeepPink"), column=0) ts.legend.add_face(TextFace(' Fission ', fsize=16, fgcolor='DeepPink'), column=1) elif (params.event == "termLosses"): ts.legend.add_face(CircleFace(10, "YellowGreen"), column=0) ts.legend.add_face(TextFace(' Terminal Loss ', fsize=16, fgcolor='YellowGreen'), column=1) elif (params.event == "termEmergences"): ts.legend.add_face(CircleFace(10, "DarkBlue"), column=0) ts.legend.add_face(TextFace(' Terminal Emergence ', fsize=16, fgcolor='DarkBlue'), column=1) elif (params.event == "singleDomainLosses"): ts.legend.add_face(CircleFace(10, "Chocolate"), column=0) ts.legend.add_face(TextFace(' Single Domain Loss ', fsize=16, fgcolor='Chocolate'), column=1) elif (params.event == "singleDomainEmergences"): ts.legend.add_face(CircleFace(10, "DeepSkyBlue"), column=0) ts.legend.add_face(TextFace(' Single Domain Emergence ', fsize=16, fgcolor='DeepSkyBlue'), column=1) ts.legend_position = 1 ts.draw_guiding_lines = True if (params.outputname.endswith(".pdf")): pathout = params.outputname else: pathout = params.outputname + ".pdf" tree.render(pathout, dpi=1200, tree_style=ts) pl.close() sys.exit(0)
def analyze_tree(tree, outfile, anno, monoout): """ Main function to analyze the tree :param tree: :param outfile: :param anno: :param monoout: :return: tree file as pdf file """ m = pd.read_csv(group_mapping, sep="\t", header=None, names=['taxon', 'group']) taxon_mapping = dict(zip(m['taxon'], m['group'])) unique_groups = list(set(m['group'])) group_dict = {} for u in unique_groups: group_dict[u] = [] for k, v in taxon_mapping.iteritems(): if taxon_mapping[k] in group_dict: group_dict[taxon_mapping[k]].append(k) adf = pd.read_csv(anno, sep="\t", header=None, names=['gene', 'anno']) anno_dict = dict(zip(adf['gene'], adf['anno'])) title = "unknown gene" if tree.startswith("chloNOG"): gene = '.'.join(tree.split(".")[:3]) if gene in anno_dict: title = gene + " (" + anno_dict[gene] + ")" elif tree.startswith("OC_"): gene = tree.split(".")[0] if gene in anno_dict: title = gene + " (" + anno_dict[gene] + ")" t = Tree(tree) outgroup_to_use = determine_outgroup(t, group_dict) print title if outgroup_to_use[2] == 0: print "None of the outgroups chosen are found in the tree. Rooted with mid-point rooting." r = t.get_midpoint_outgroup() t.set_outgroup(r) elif outgroup_to_use[2] == 1: t.set_outgroup(outgroup_to_use[1][0]) elif outgroup_to_use[2] >= 2: ## do mid-point rooting first to get around the problem of some clades that can't be re-rooted right away r = t.get_midpoint_outgroup() t.set_outgroup(r) x = t.check_monophyly(values=outgroup_to_use[1], target_attr="name") if x[0] == True: print outgroup_to_use[0], "members are monophyletic" ## now, re-root with actual outgroup lca = t.get_common_ancestor(outgroup_to_use[1]) t.set_outgroup(lca) else: ## use mid-point rooting if members are not mono (already mid-point rooted in the outer scope) print outgroup_to_use[ 0], "members are NOT monophyletic. Can't use as an outgroup. Rooted with mid-point rooting" check_monophyly(t, group_dict, monoout) t.ladderize(direction=1) ts = TreeStyle() ns = NodeStyle() ts.show_branch_support = True ts.extra_branch_line_color = "DarkGrey" ts.show_leaf_name = False ts.layout_fn = tree_layout #ts.branch_vertical_margin = 0 ns['shape'] = "square" ns['size'] = 0 ts.title.add_face(TextFace(title, fsize=8), column=0) for n in t.traverse(): n.set_style(ns) t.render(outfile, w=1500, units="px", tree_style=ts) print " "