def plot(self, placement, togjson, outdir, cfg): """ plot a plcement in the tree show all pplacer placements and the LCA and HCA node as well as the inferred lineage """ from ete3 import NodeStyle, TreeStyle from ete3 import CircleFace, TextFace, RectFace logging.debug("Plotting trees now") # with no X display this needs to be set os.environ["QT_QPA_PLATFORM"] = "offscreen" info = self.loadInfo(togjson) def defaultNodeStyle(): return NodeStyle() nodeStyles = defaultdict(defaultNodeStyle) no = 0 for LCAp, HPAp in zip(placement["LCA"], placement["HPA"]): plotpath = os.path.join(outdir, f"tree_{no}.png") # make shallow copy t = self.t LCA = LCAp["node"] HPA = HPAp["node"] # define basic tree style ts = TreeStyle() # hide leave names ts.show_leaf_name = False ts.root_opening_factor = 1 # circular tree ts.mode = "c" ts.rotation = 210 ts.arc_start = 0 # 0 degrees = 3 o'clock ts.arc_span = 350 highlightsize = 80 nodesize = 10 # define styles for special nodes # at the moment hard coded, but could be accesible for the user # LCA style LCAstyle = NodeStyle() LCAstyle["fgcolor"] = "#33a02c" LCAstyle["bgcolor"] = "#b2df8a" LCAstyle["size"] = highlightsize # HPA style HPAstyle = NodeStyle() HPAstyle["fgcolor"] = "#1f78b4" HPAstyle["bgcolor"] = "#a6cee3" HPAstyle["size"] = highlightsize # default node defaultStyle = NodeStyle() defaultStyle["fgcolor"] = "gray" defaultStyle["size"] = nodesize # add legend ts.legend_position = 1 ts.legend.add_face(CircleFace(40, LCAstyle["fgcolor"]), column=1) ts.legend.add_face(TextFace(f"LCA", fsize=50), column=2) ts.legend.add_face(CircleFace(40, HPAstyle["fgcolor"]), column=1) ts.legend.add_face(TextFace(f"HPA", fsize=50), column=2) i = 1 ts.legend.add_face(TextFace(f"p = {i}", fsize=50), column=1) while i > 0: temp_face = RectFace(60, 10, fgcolor=p_to_color(i), bgcolor=p_to_color(i)) temp_face.margin_top = -4 ts.legend.add_face(temp_face, column=1) i -= 0.01 ts.legend.add_face(TextFace(f"p = {cfg['minPlacementLikelyhood']}", fsize=50), column=1) # add highlights for each placed protein for n in t.traverse(): if n.name.startswith("PTHR"): # set color based on posterior prob: x = (info[n.name]["post_prob"] - cfg["minPlacementLikelyhood"]) / ( 1 - cfg["minPlacementLikelyhood"]) # orange to purple gradient from 0 to 1 posterior propability he = p_to_color(x) nodeStyles[he]["bgcolor"] = he # define back color of locations n.set_style(nodeStyles[he]) elif n.name == LCA: n.set_style(LCAstyle) elif n.name == HPA: n.set_style(HPAstyle) else: n.set_style(defaultStyle) # plot to disk _ = t.render(plotpath, w=320, units="mm", tree_style=ts) no = no + 1
def nodeLayoutFunc(node): taxid = int(node.name) if taxid in taxidsToKeep: taxGroupName = ncbiTaxa.get_taxid_translator( [taxid] )[taxid] # There has to be an easier way to look up names... row = None rangeRows = None print(len(ranges)) if (len(ranges) == 1): row = df[(df['ExplanatoryVar'] == var) & (df['TaxGroup'] == taxid) & (df['Range'] == ranges[0])] assert (len(row) == len(ranges)) elif len(ranges) > 1: row = df[(df['ExplanatoryVar'] == var) & (df['TaxGroup'] == taxid) & (df['Range'] == 0)] assert (len(row) == 1) rangeRows = df[(df['ExplanatoryVar'] == var) & (df['TaxGroup'] == taxid) & (df['Range'].isin(set(ranges)))] else: assert (False) overallPval = float(row['Pvalue'].values[0]) name = TextFace("%s" % taxGroupName, fsize=baseFontSize * 2.5) name.tight_text = True name.margin_left = 20 name.margin_right = 0 name.margin_top = 40 name.margin_bottom = 12 faces.add_face_to_node(name, node, column=0) #print(rangeRows) # For each range to be included in this plot, add a bar for rangeId in ranges: #print("rangeId = %s" % (rangeId)) rowForThisRange = None if len(ranges) == 1: rowForThisRange = row else: rowForThisRange = rangeRows[rangeRows['Range'] == rangeId] assert (len(rowForThisRange) == 1) # Extract p-value and "effect-size" (signed R^2) effectSize = float( rowForThisRange['EffectSize'].values[0]) pval = float(rowForThisRange['Pvalue'].values[0]) # Set bar-graph color and significance markers barColor = "" significanceMarker = "" if (pval < significanceLevel): significanceMarker = " %s" % unichr(0x2731) if effectSize < 0: barColor = "#1133ff" else: barColor = "#ff3311" else: # not significant if effectSize < 0: barColor = "#b0b0f0" else: barColor = "#ccb090" # Add the minus sign if needed signChar = "" if effectSize < 0: signChar = unichr( 0x2212 ) # minus sign (more legible than a hypen...) v = RectFace(width=abs(effectSize) * barScale, height=baseFontSize * 3.5, fgcolor=barColor, bgcolor=barColor, label={ "text": "%s%.2g %s" % (signChar, abs(effectSize), significanceMarker), "fontsize": baseFontSize * 1.8, "color": "black" }) #v.rotation = -90 v.margin_top = 1 v.margin_left = 30 v.margin_right = 8 v.margin_bottom = 12 faces.add_face_to_node(v, node, column=0) details = TextFace( "N=%d" % row['NumSpecies'], fsize=baseFontSize * 1.5) #, fsize=baseFontSize) #, fstyle="italic") details.background.color = "#dfdfdf" details.margin_left = 6 details.margin_right = 20 #details.margin_top=5 #details.margin_bottom=0 faces.add_face_to_node(details, node, column=1) nstyle = NodeStyle() nstyle["size"] = 0 node.set_style(nstyle)