Skip to content

Commit ccde5ac

Browse files
committed
add draft of feature node class
1 parent 238f8ff commit ccde5ac

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

bin/stats/feature_nodes.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import numpy as np
2+
from dataclasses import dataclass, field
3+
from typing import Any, List, Generator
4+
5+
6+
@dataclass(slots=True)
7+
class FeatureNode:
8+
parents: List["FeatureNode"] = field(default_factory=list)
9+
children: List["FeatureNode"] = field(default_factory=list)
10+
identifier: str = ""
11+
region: str = ""
12+
start: np.int64 = -1
13+
end: np.int64 = -1
14+
data: dict[str, Any] = field(default_factory=dict[str, Any])
15+
16+
def add(self, child: "FeatureNode") -> None:
17+
"""Add a child node"""
18+
if self.region != child.region:
19+
raise Exception(
20+
f"Tried to add node \"{child.identifier}\" in genomic region \"{child.region}\" as a child of node \"{self.identifier}\" in genomic region \"{self.region}\""
21+
)
22+
23+
self.children.append(child)
24+
child.parents.append(self)
25+
self.start = child.start if child.start < self.start else self.start
26+
self.end = child.end if child.end > self.end else self.end
27+
28+
return None
29+
30+
def is_root(self) -> bool:
31+
"""Return true if the node is a root (has not parents)"""
32+
33+
return len(self.parents) == 0
34+
35+
def is_outer(self) -> bool:
36+
"""Return true is the node is outer ("leaf" or "terminal"), e.g. has no children"""
37+
38+
return len(self.children) == 0
39+
40+
def post_traverse(self) -> Generator["FeatureNode", None, None]:
41+
"""Create a generator that traverses the subtree of a node in post-order"""
42+
43+
for child in self.children:
44+
yield from child.post_traverse()
45+
46+
yield self
47+
48+
def subtree_size(self) -> int:
49+
"""Return the number of nodes in the subtree of a node"""
50+
s = 0
51+
for _ in self.post_traverse:
52+
s += 1
53+
54+
return s
55+
56+
def _newick(self) -> str:
57+
s = ''
58+
if not self.is_outer():
59+
s += '('
60+
first_child = True
61+
for child in self.children:
62+
s += first_child * ','
63+
s += child._newick()
64+
first_child = False
65+
66+
s += ')'
67+
68+
s += '\"' + self.identifier + '\"'
69+
70+
return s
71+
72+
def newick(self) -> str:
73+
"""Return the Newick representation of the stubtree of a node"""
74+
return self._newick() + ';'

0 commit comments

Comments
 (0)