Coverage for /home/deng/Projects/ete4/hackathon/ete4/ete4/smartview/renderer/layouts/context_layouts.py: 12%
81 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-21 09:19 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-21 09:19 +0100
1from collections import Counter, defaultdict
2from ..treelayout import TreeLayout
3from ..faces import ArrowFace
6__all__ = [ "LayoutGenomicContext" ]
9class LayoutGenomicContext(TreeLayout):
10 def __init__(self, name="Genomic context", nside=2,
11 conservation_threshold=0, width=70, height=15, collapse_size=1,
12 gene_name="name", tooltip_props=[],
13 stroke_color="gray", stroke_width="1.5px",
14 anchor_stroke_color="black", anchor_stroke_width="3px",
15 non_conserved_color="#d0d0d0", collapse_conservation=0.2,
16 collapse_by_conservation=True):
18 super().__init__(name, aligned_faces=True)
20 self.nside = nside
21 self.conservation_threshold = conservation_threshold
22 self.gene_name = gene_name
23 self.tooltip_props = tooltip_props
25 self.width = width
26 self.height = height
28 self.collapse_size = collapse_size
30 self.anchor_stroke_color = anchor_stroke_color
31 self.anchor_stroke_width = anchor_stroke_width
32 self.stroke_color = stroke_color
33 self.stroke_width = stroke_width
35 self.non_conserved_color = non_conserved_color
37 self.collapse_conservation = collapse_conservation
38 self.collapse_by_conservation = collapse_by_conservation
40 def set_tree_style(self, tree, style):
41 super().set_tree_style(tree, style)
42 style.collapse_size = self.collapse_size
44 def set_node_style(self, node):
45 context = self.get_context(node)
46 if context:
47 for idx, gene in enumerate(context):
48 name = gene.get("name")
49 color = gene.get("color", self.non_conserved_color)
50 conservation = gene.get("conservation_score")
51 if conservation is not None\
52 and float(conservation) < self.conservation_threshold:
53 color = self.non_conserved_color
54 strand = gene.get("strand", "+")
55 cluster = gene.get("cluster")
56 orientation = "left" if strand == "-" else "right"
57 text = gene.get(self.gene_name, "")
58 if idx == self.nside:
59 stroke_color = self.anchor_stroke_color
60 stroke_width = self.anchor_stroke_width
61 else:
62 stroke_color = self.stroke_color
63 stroke_width = self.stroke_width
64 arrow = ArrowFace(self.width, self.height,
65 orientation=orientation, color=color,
66 stroke_color=stroke_color, stroke_width=stroke_width,
67 tooltip=self.get_tooltip(gene),
68 text=text,
69 padding_x=2, padding_y=2)
70 node.add_face(arrow, position="aligned", column=idx,
71 collapsed_only=(not node.is_leaf))
73 def get_tooltip(self, gene):
74 if self.tooltip_props is None:
75 return ""
77 if self.tooltip_props == []:
78 key_props = gene.keys()
79 else:
80 key_props = self.tooltip_props
82 props = {}
83 for k,v in gene.items():
84 if k in key_props and v and not k in ("strand", "color")\
85 and not k.startswith("_"):
86 if k == "hyperlink":
87 k = "Go to"
88 label, url = v
89 v = f'<a href="{url}" target="_blank">{label}</a>'
90 props[k] = v
92 return "<br>".join(f'{k}: {v}' for k,v in props.items())
94 def get_context(self, node):
95 if node.is_leaf:
96 return node.props.get("_context")
98 if not self.collapse_by_conservation:
99 first_leaf = next(node.iter_leaves())
100 return first_leaf.props.get("_context")
102 # Compute conserved context by color
103 color_context = defaultdict(list)
104 color2genes = {}
106 for l in node:
107 lcontext = l.props.get("_context")
108 for pos, gene in enumerate(lcontext):
109 color = gene["color"]
110 color_context[pos].append(color)
111 color2genes[color] = gene
113 ntips = len(node)
114 context = []
115 for pos, colors in sorted(color_context.items()):
116 color, n = Counter(colors).most_common(1)[0]
117 if n / ntips >= self.collapse_conservation\
118 and color != self.non_conserved_color:
119 context.append({
120 **color2genes[color],
121 "vertical_conservation": n / ntips })
122 else:
123 context.append({ "color": self.non_conserved_color })
125 return context