iplotx 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iplotx/cascades.py +223 -0
- iplotx/edge/__init__.py +180 -420
- iplotx/edge/arrow.py +20 -20
- iplotx/edge/geometry.py +448 -0
- iplotx/edge/ports.py +7 -2
- iplotx/groups.py +24 -14
- iplotx/ingest/__init__.py +12 -4
- iplotx/ingest/heuristics.py +1 -3
- iplotx/ingest/providers/network/igraph.py +4 -2
- iplotx/ingest/providers/network/networkx.py +4 -2
- iplotx/ingest/providers/tree/biopython.py +21 -79
- iplotx/ingest/providers/tree/cogent3.py +17 -88
- iplotx/ingest/providers/tree/ete4.py +19 -87
- iplotx/ingest/providers/tree/skbio.py +17 -88
- iplotx/ingest/typing.py +225 -22
- iplotx/label.py +103 -21
- iplotx/layout.py +57 -36
- iplotx/network.py +9 -8
- iplotx/plotting.py +6 -3
- iplotx/style.py +36 -10
- iplotx/tree.py +237 -29
- iplotx/typing.py +19 -0
- iplotx/version.py +1 -1
- iplotx/vertex.py +122 -35
- {iplotx-0.2.0.dist-info → iplotx-0.3.0.dist-info}/METADATA +16 -3
- iplotx-0.3.0.dist-info/RECORD +32 -0
- iplotx-0.2.0.dist-info/RECORD +0 -30
- {iplotx-0.2.0.dist-info → iplotx-0.3.0.dist-info}/WHEEL +0 -0
iplotx/ingest/heuristics.py
CHANGED
|
@@ -15,7 +15,6 @@ from ..layout import compute_tree_layout
|
|
|
15
15
|
from ..typing import (
|
|
16
16
|
GraphType,
|
|
17
17
|
GroupingType,
|
|
18
|
-
TreeType,
|
|
19
18
|
LayoutType,
|
|
20
19
|
)
|
|
21
20
|
|
|
@@ -91,7 +90,6 @@ def normalise_layout(layout, network=None):
|
|
|
91
90
|
|
|
92
91
|
def normalise_tree_layout(
|
|
93
92
|
layout: str | Any,
|
|
94
|
-
tree: Optional[TreeType] = None,
|
|
95
93
|
**kwargs,
|
|
96
94
|
) -> pd.DataFrame:
|
|
97
95
|
"""Normalise tree layout from a variety of inputs.
|
|
@@ -108,7 +106,7 @@ def normalise_tree_layout(
|
|
|
108
106
|
the layout internally. This might change in the future.
|
|
109
107
|
"""
|
|
110
108
|
if isinstance(layout, str):
|
|
111
|
-
layout = compute_tree_layout(
|
|
109
|
+
layout = compute_tree_layout(layout, **kwargs)
|
|
112
110
|
else:
|
|
113
111
|
raise NotImplementedError(
|
|
114
112
|
"Only internally computed tree layout currently accepted."
|
|
@@ -83,14 +83,16 @@ class IGraphDataProvider(NetworkDataProvider):
|
|
|
83
83
|
}
|
|
84
84
|
return network_data
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
@staticmethod
|
|
87
|
+
def check_dependencies() -> bool:
|
|
87
88
|
try:
|
|
88
89
|
import igraph
|
|
89
90
|
except ImportError:
|
|
90
91
|
return False
|
|
91
92
|
return True
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
@staticmethod
|
|
95
|
+
def graph_type():
|
|
94
96
|
import igraph as ig
|
|
95
97
|
|
|
96
98
|
return ig.Graph
|
|
@@ -120,14 +120,16 @@ class NetworkXDataProvider(NetworkDataProvider):
|
|
|
120
120
|
}
|
|
121
121
|
return network_data
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
@staticmethod
|
|
124
|
+
def check_dependencies() -> bool:
|
|
124
125
|
try:
|
|
125
126
|
import networkx
|
|
126
127
|
except ImportError:
|
|
127
128
|
return False
|
|
128
129
|
return True
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
@staticmethod
|
|
132
|
+
def graph_type():
|
|
131
133
|
from networkx import Graph
|
|
132
134
|
|
|
133
135
|
return Graph
|
|
@@ -1,105 +1,47 @@
|
|
|
1
1
|
from typing import (
|
|
2
|
+
Any,
|
|
2
3
|
Optional,
|
|
3
4
|
Sequence,
|
|
4
5
|
)
|
|
5
|
-
from
|
|
6
|
-
from operator import attrgetter
|
|
7
|
-
import numpy as np
|
|
8
|
-
import pandas as pd
|
|
6
|
+
from functools import partialmethod
|
|
9
7
|
|
|
10
|
-
from ....typing import (
|
|
11
|
-
TreeType,
|
|
12
|
-
LayoutType,
|
|
13
|
-
)
|
|
14
8
|
from ...typing import (
|
|
15
9
|
TreeDataProvider,
|
|
16
|
-
TreeData,
|
|
17
|
-
)
|
|
18
|
-
from ...heuristics import (
|
|
19
|
-
normalise_tree_layout,
|
|
20
10
|
)
|
|
21
11
|
|
|
22
12
|
|
|
23
13
|
class BiopythonDataProvider(TreeDataProvider):
|
|
24
|
-
def
|
|
25
|
-
self
|
|
26
|
-
tree: TreeType,
|
|
27
|
-
layout: str | LayoutType,
|
|
28
|
-
orientation: str = "horizontal",
|
|
29
|
-
directed: bool | str = False,
|
|
30
|
-
vertex_labels: Optional[
|
|
31
|
-
Sequence[str] | dict[Hashable, str] | pd.Series | bool
|
|
32
|
-
] = None,
|
|
33
|
-
edge_labels: Optional[Sequence[str] | dict] = None,
|
|
34
|
-
) -> TreeData:
|
|
35
|
-
"""Create tree data object for iplotx from BioPython.Phylo.Tree classes."""
|
|
14
|
+
def is_rooted(self) -> bool:
|
|
15
|
+
return self.tree.rooted
|
|
36
16
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"rooted": tree.rooted,
|
|
41
|
-
"directed": directed,
|
|
42
|
-
"ndim": 2,
|
|
43
|
-
"layout_name": layout,
|
|
44
|
-
}
|
|
17
|
+
def _traverse(self, order: str) -> Any:
|
|
18
|
+
"""Traverse the tree."""
|
|
19
|
+
return self.tree.find_clades(order=order)
|
|
45
20
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
layout,
|
|
49
|
-
tree=tree,
|
|
50
|
-
orientation=orientation,
|
|
51
|
-
root_fun=attrgetter("root"),
|
|
52
|
-
preorder_fun=lambda tree: tree.find_clades(order="preorder"),
|
|
53
|
-
postorder_fun=lambda tree: tree.find_clades(order="postorder"),
|
|
54
|
-
children_fun=attrgetter("clades"),
|
|
55
|
-
branch_length_fun=attrgetter("branch_length"),
|
|
56
|
-
)
|
|
57
|
-
if layout in ("radial",):
|
|
58
|
-
tree_data["layout_coordinate_system"] = "polar"
|
|
59
|
-
else:
|
|
60
|
-
tree_data["layout_coordinate_system"] = "cartesian"
|
|
21
|
+
preorder = partialmethod(_traverse, order="preorder")
|
|
22
|
+
postorder = partialmethod(_traverse, order="postorder")
|
|
61
23
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for node in tree.find_clades(order="preorder"):
|
|
65
|
-
for child in node.clades:
|
|
66
|
-
if directed == "parent":
|
|
67
|
-
edge_data["_ipx_source"].append(child)
|
|
68
|
-
edge_data["_ipx_target"].append(node)
|
|
69
|
-
else:
|
|
70
|
-
edge_data["_ipx_source"].append(node)
|
|
71
|
-
edge_data["_ipx_target"].append(child)
|
|
72
|
-
edge_df = pd.DataFrame(edge_data)
|
|
73
|
-
tree_data["edge_df"] = edge_df
|
|
24
|
+
def get_leaves(self) -> Sequence[Any]:
|
|
25
|
+
return self.tree.get_terminals()
|
|
74
26
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if np.isscalar(vertex_labels) and vertex_labels:
|
|
79
|
-
tree_data["vertex_df"]["label"] = [
|
|
80
|
-
x.name for x in tree_data["vertices"].index
|
|
81
|
-
]
|
|
82
|
-
elif not np.isscalar(vertex_labels):
|
|
83
|
-
# If a dict-like object is passed, it can be incomplete (e.g. only the leaves):
|
|
84
|
-
# we fill the rest with empty strings which are not going to show up in the plot.
|
|
85
|
-
if isinstance(vertex_labels, pd.Series):
|
|
86
|
-
vertex_labels = dict(vertex_labels)
|
|
87
|
-
if isinstance(vertex_labels, dict):
|
|
88
|
-
for vertex in tree_data["vertex_df"].index:
|
|
89
|
-
if vertex not in vertex_labels:
|
|
90
|
-
vertex_labels[vertex] = ""
|
|
91
|
-
tree_data["vertex_df"]["label"] = pd.Series(vertex_labels)
|
|
27
|
+
@staticmethod
|
|
28
|
+
def get_children(node: Any) -> Sequence[Any]:
|
|
29
|
+
return node.clades
|
|
92
30
|
|
|
93
|
-
|
|
31
|
+
@staticmethod
|
|
32
|
+
def get_branch_length(node: Any) -> Optional[float]:
|
|
33
|
+
return node.branch_length
|
|
94
34
|
|
|
95
|
-
|
|
35
|
+
@staticmethod
|
|
36
|
+
def check_dependencies() -> bool:
|
|
96
37
|
try:
|
|
97
38
|
from Bio import Phylo
|
|
98
39
|
except ImportError:
|
|
99
40
|
return False
|
|
100
41
|
return True
|
|
101
42
|
|
|
102
|
-
|
|
43
|
+
@staticmethod
|
|
44
|
+
def tree_type():
|
|
103
45
|
from Bio import Phylo
|
|
104
46
|
|
|
105
47
|
return Phylo.BaseTree.Tree
|
|
@@ -1,112 +1,41 @@
|
|
|
1
1
|
from typing import (
|
|
2
|
+
Any,
|
|
2
3
|
Optional,
|
|
3
4
|
Sequence,
|
|
4
5
|
)
|
|
5
|
-
from collections.abc import Hashable
|
|
6
|
-
from operator import attrgetter
|
|
7
|
-
import numpy as np
|
|
8
|
-
import pandas as pd
|
|
9
|
-
|
|
10
|
-
from ....typing import (
|
|
11
|
-
TreeType,
|
|
12
|
-
LayoutType,
|
|
13
|
-
)
|
|
14
6
|
from ...typing import (
|
|
15
7
|
TreeDataProvider,
|
|
16
|
-
TreeData,
|
|
17
|
-
)
|
|
18
|
-
from ...heuristics import (
|
|
19
|
-
normalise_tree_layout,
|
|
20
8
|
)
|
|
21
9
|
|
|
22
10
|
|
|
23
11
|
class Cogent3DataProvider(TreeDataProvider):
|
|
24
|
-
def
|
|
25
|
-
self
|
|
26
|
-
tree: TreeType,
|
|
27
|
-
layout: str | LayoutType,
|
|
28
|
-
orientation: str = "horizontal",
|
|
29
|
-
directed: bool | str = False,
|
|
30
|
-
vertex_labels: Optional[
|
|
31
|
-
Sequence[str] | dict[Hashable, str] | pd.Series | bool
|
|
32
|
-
] = None,
|
|
33
|
-
edge_labels: Optional[Sequence[str] | dict] = None,
|
|
34
|
-
) -> TreeData:
|
|
35
|
-
"""Create tree data object for iplotx from cogent3.core.tree.PhyloNode classes."""
|
|
36
|
-
|
|
37
|
-
root_fun = lambda tree: tree.root()
|
|
38
|
-
preorder_fun = lambda tree: tree.preorder()
|
|
39
|
-
postorder_fun = lambda tree: tree.postorder()
|
|
40
|
-
children_fun = attrgetter("children")
|
|
41
|
-
branch_length_fun = attrgetter("length")
|
|
42
|
-
leaves_fun = lambda tree: tree.tips()
|
|
43
|
-
|
|
44
|
-
tree_data = {
|
|
45
|
-
"root": root_fun(tree),
|
|
46
|
-
"leaves": leaves_fun(tree),
|
|
47
|
-
"rooted": True,
|
|
48
|
-
"directed": directed,
|
|
49
|
-
"ndim": 2,
|
|
50
|
-
"layout_name": layout,
|
|
51
|
-
}
|
|
12
|
+
def preorder(self) -> Sequence[Any]:
|
|
13
|
+
return self.tree.preorder()
|
|
52
14
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layout,
|
|
56
|
-
tree=tree,
|
|
57
|
-
orientation=orientation,
|
|
58
|
-
root_fun=root_fun,
|
|
59
|
-
preorder_fun=preorder_fun,
|
|
60
|
-
postorder_fun=postorder_fun,
|
|
61
|
-
children_fun=children_fun,
|
|
62
|
-
branch_length_fun=branch_length_fun,
|
|
63
|
-
)
|
|
64
|
-
if layout in ("radial",):
|
|
65
|
-
tree_data["layout_coordinate_system"] = "polar"
|
|
66
|
-
else:
|
|
67
|
-
tree_data["layout_coordinate_system"] = "cartesian"
|
|
15
|
+
def postorder(self) -> Sequence[Any]:
|
|
16
|
+
return self.tree.postorder()
|
|
68
17
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for node in preorder_fun(tree):
|
|
72
|
-
for child in node.children:
|
|
73
|
-
if directed == "parent":
|
|
74
|
-
edge_data["_ipx_source"].append(child)
|
|
75
|
-
edge_data["_ipx_target"].append(node)
|
|
76
|
-
else:
|
|
77
|
-
edge_data["_ipx_source"].append(node)
|
|
78
|
-
edge_data["_ipx_target"].append(child)
|
|
79
|
-
edge_df = pd.DataFrame(edge_data)
|
|
80
|
-
tree_data["edge_df"] = edge_df
|
|
18
|
+
def get_leaves(self) -> Sequence[Any]:
|
|
19
|
+
return self.tree.tips()
|
|
81
20
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if np.isscalar(vertex_labels) and vertex_labels:
|
|
86
|
-
tree_data["vertex_df"]["label"] = [
|
|
87
|
-
x.name for x in tree_data["vertices"].index
|
|
88
|
-
]
|
|
89
|
-
elif not np.isscalar(vertex_labels):
|
|
90
|
-
# If a dict-like object is passed, it can be incomplete (e.g. only the leaves):
|
|
91
|
-
# we fill the rest with empty strings which are not going to show up in the plot.
|
|
92
|
-
if isinstance(vertex_labels, pd.Series):
|
|
93
|
-
vertex_labels = dict(vertex_labels)
|
|
94
|
-
if isinstance(vertex_labels, dict):
|
|
95
|
-
for vertex in tree_data["vertex_df"].index:
|
|
96
|
-
if vertex not in vertex_labels:
|
|
97
|
-
vertex_labels[vertex] = ""
|
|
98
|
-
tree_data["vertex_df"]["label"] = pd.Series(vertex_labels)
|
|
21
|
+
@staticmethod
|
|
22
|
+
def get_children(node: Any) -> Sequence[Any]:
|
|
23
|
+
return node.children
|
|
99
24
|
|
|
100
|
-
|
|
25
|
+
@staticmethod
|
|
26
|
+
def get_branch_length(node: Any) -> Optional[float]:
|
|
27
|
+
return node.length
|
|
101
28
|
|
|
102
|
-
|
|
29
|
+
@staticmethod
|
|
30
|
+
def check_dependencies() -> bool:
|
|
103
31
|
try:
|
|
104
32
|
import cogent3
|
|
105
33
|
except ImportError:
|
|
106
34
|
return False
|
|
107
35
|
return True
|
|
108
36
|
|
|
109
|
-
|
|
37
|
+
@staticmethod
|
|
38
|
+
def tree_type():
|
|
110
39
|
from cogent3.core.tree import PhyloNode
|
|
111
40
|
|
|
112
41
|
return PhyloNode
|
|
@@ -1,112 +1,44 @@
|
|
|
1
1
|
from typing import (
|
|
2
|
+
Any,
|
|
2
3
|
Optional,
|
|
3
4
|
Sequence,
|
|
4
5
|
)
|
|
5
|
-
from
|
|
6
|
-
from operator import attrgetter
|
|
7
|
-
import numpy as np
|
|
8
|
-
import pandas as pd
|
|
6
|
+
from functools import partialmethod
|
|
9
7
|
|
|
10
|
-
from ....typing import (
|
|
11
|
-
TreeType,
|
|
12
|
-
LayoutType,
|
|
13
|
-
)
|
|
14
8
|
from ...typing import (
|
|
15
9
|
TreeDataProvider,
|
|
16
|
-
TreeData,
|
|
17
|
-
)
|
|
18
|
-
from ...heuristics import (
|
|
19
|
-
normalise_tree_layout,
|
|
20
10
|
)
|
|
21
11
|
|
|
22
12
|
|
|
23
13
|
class Ete4DataProvider(TreeDataProvider):
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
tree
|
|
27
|
-
layout: str | LayoutType,
|
|
28
|
-
orientation: str = "horizontal",
|
|
29
|
-
directed: bool | str = False,
|
|
30
|
-
vertex_labels: Optional[
|
|
31
|
-
Sequence[str] | dict[Hashable, str] | pd.Series | bool
|
|
32
|
-
] = None,
|
|
33
|
-
edge_labels: Optional[Sequence[str] | dict] = None,
|
|
34
|
-
) -> TreeData:
|
|
35
|
-
"""Create tree data object for iplotx from ete4.core.tre.Tree classes."""
|
|
36
|
-
|
|
37
|
-
root_fun = attrgetter("root")
|
|
38
|
-
preorder_fun = lambda tree: tree.traverse("preorder")
|
|
39
|
-
postorder_fun = lambda tree: tree.traverse("postorder")
|
|
40
|
-
children_fun = attrgetter("children")
|
|
41
|
-
branch_length_fun = lambda node: node.dist if node.dist is not None else 1.0
|
|
42
|
-
leaves_fun = lambda tree: tree.leaves()
|
|
43
|
-
|
|
44
|
-
tree_data = {
|
|
45
|
-
"root": tree.root,
|
|
46
|
-
"leaves": leaves_fun(tree),
|
|
47
|
-
"rooted": True,
|
|
48
|
-
"directed": directed,
|
|
49
|
-
"ndim": 2,
|
|
50
|
-
"layout_name": layout,
|
|
51
|
-
}
|
|
14
|
+
def _traverse(self, order: str) -> Any:
|
|
15
|
+
"""Traverse the tree."""
|
|
16
|
+
return self.tree.traverse(order)
|
|
52
17
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layout,
|
|
56
|
-
tree=tree,
|
|
57
|
-
orientation=orientation,
|
|
58
|
-
root_fun=root_fun,
|
|
59
|
-
preorder_fun=preorder_fun,
|
|
60
|
-
postorder_fun=postorder_fun,
|
|
61
|
-
children_fun=children_fun,
|
|
62
|
-
branch_length_fun=branch_length_fun,
|
|
63
|
-
)
|
|
64
|
-
if layout in ("radial",):
|
|
65
|
-
tree_data["layout_coordinate_system"] = "polar"
|
|
66
|
-
else:
|
|
67
|
-
tree_data["layout_coordinate_system"] = "cartesian"
|
|
18
|
+
preorder = partialmethod(_traverse, order="preorder")
|
|
19
|
+
postorder = partialmethod(_traverse, order="postorder")
|
|
68
20
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for node in preorder_fun(tree):
|
|
72
|
-
for child in children_fun(node):
|
|
73
|
-
if directed == "parent":
|
|
74
|
-
edge_data["_ipx_source"].append(child)
|
|
75
|
-
edge_data["_ipx_target"].append(node)
|
|
76
|
-
else:
|
|
77
|
-
edge_data["_ipx_source"].append(node)
|
|
78
|
-
edge_data["_ipx_target"].append(child)
|
|
79
|
-
edge_df = pd.DataFrame(edge_data)
|
|
80
|
-
tree_data["edge_df"] = edge_df
|
|
21
|
+
def get_leaves(self) -> Sequence[Any]:
|
|
22
|
+
return self.tree.leaves()
|
|
81
23
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if np.isscalar(vertex_labels) and vertex_labels:
|
|
86
|
-
tree_data["vertex_df"]["label"] = [
|
|
87
|
-
x.name for x in tree_data["vertices"].index
|
|
88
|
-
]
|
|
89
|
-
elif not np.isscalar(vertex_labels):
|
|
90
|
-
# If a dict-like object is passed, it can be incomplete (e.g. only the leaves):
|
|
91
|
-
# we fill the rest with empty strings which are not going to show up in the plot.
|
|
92
|
-
if isinstance(vertex_labels, pd.Series):
|
|
93
|
-
vertex_labels = dict(vertex_labels)
|
|
94
|
-
if isinstance(vertex_labels, dict):
|
|
95
|
-
for vertex in tree_data["vertex_df"].index:
|
|
96
|
-
if vertex not in vertex_labels:
|
|
97
|
-
vertex_labels[vertex] = ""
|
|
98
|
-
tree_data["vertex_df"]["label"] = pd.Series(vertex_labels)
|
|
24
|
+
@staticmethod
|
|
25
|
+
def get_children(node: Any) -> Sequence[Any]:
|
|
26
|
+
return node.children
|
|
99
27
|
|
|
100
|
-
|
|
28
|
+
@staticmethod
|
|
29
|
+
def get_branch_length(node: Any) -> Optional[float]:
|
|
30
|
+
return node.dist
|
|
101
31
|
|
|
102
|
-
|
|
32
|
+
@staticmethod
|
|
33
|
+
def check_dependencies() -> bool:
|
|
103
34
|
try:
|
|
104
35
|
from ete4 import Tree
|
|
105
36
|
except ImportError:
|
|
106
37
|
return False
|
|
107
38
|
return True
|
|
108
39
|
|
|
109
|
-
|
|
40
|
+
@staticmethod
|
|
41
|
+
def tree_type():
|
|
110
42
|
from ete4 import Tree
|
|
111
43
|
|
|
112
44
|
return Tree
|
|
@@ -1,112 +1,41 @@
|
|
|
1
1
|
from typing import (
|
|
2
|
+
Any,
|
|
2
3
|
Optional,
|
|
3
4
|
Sequence,
|
|
4
5
|
)
|
|
5
|
-
from collections.abc import Hashable
|
|
6
|
-
from operator import attrgetter
|
|
7
|
-
import numpy as np
|
|
8
|
-
import pandas as pd
|
|
9
|
-
|
|
10
|
-
from ....typing import (
|
|
11
|
-
TreeType,
|
|
12
|
-
LayoutType,
|
|
13
|
-
)
|
|
14
6
|
from ...typing import (
|
|
15
7
|
TreeDataProvider,
|
|
16
|
-
TreeData,
|
|
17
|
-
)
|
|
18
|
-
from ...heuristics import (
|
|
19
|
-
normalise_tree_layout,
|
|
20
8
|
)
|
|
21
9
|
|
|
22
10
|
|
|
23
11
|
class SkbioDataProvider(TreeDataProvider):
|
|
24
|
-
def
|
|
25
|
-
self
|
|
26
|
-
tree: TreeType,
|
|
27
|
-
layout: str | LayoutType,
|
|
28
|
-
orientation: str = "horizontal",
|
|
29
|
-
directed: bool | str = False,
|
|
30
|
-
vertex_labels: Optional[
|
|
31
|
-
Sequence[str] | dict[Hashable, str] | pd.Series | bool
|
|
32
|
-
] = None,
|
|
33
|
-
edge_labels: Optional[Sequence[str] | dict] = None,
|
|
34
|
-
) -> TreeData:
|
|
35
|
-
"""Create tree data object for iplotx from skbio.tree.TreeNode classes."""
|
|
36
|
-
|
|
37
|
-
root_fun = lambda tree: tree.root()
|
|
38
|
-
preorder_fun = lambda tree: tree.preorder()
|
|
39
|
-
postorder_fun = lambda tree: tree.postorder()
|
|
40
|
-
children_fun = attrgetter("children")
|
|
41
|
-
branch_length_fun = attrgetter("length")
|
|
42
|
-
leaves_fun = lambda tree: tree.tips()
|
|
43
|
-
|
|
44
|
-
tree_data = {
|
|
45
|
-
"root": root_fun(tree),
|
|
46
|
-
"leaves": leaves_fun(tree),
|
|
47
|
-
"rooted": True,
|
|
48
|
-
"directed": directed,
|
|
49
|
-
"ndim": 2,
|
|
50
|
-
"layout_name": layout,
|
|
51
|
-
}
|
|
12
|
+
def preorder(self) -> Sequence[Any]:
|
|
13
|
+
return self.tree.preorder()
|
|
52
14
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layout,
|
|
56
|
-
tree=tree,
|
|
57
|
-
orientation=orientation,
|
|
58
|
-
root_fun=root_fun,
|
|
59
|
-
preorder_fun=preorder_fun,
|
|
60
|
-
postorder_fun=postorder_fun,
|
|
61
|
-
children_fun=children_fun,
|
|
62
|
-
branch_length_fun=branch_length_fun,
|
|
63
|
-
)
|
|
64
|
-
if layout in ("radial",):
|
|
65
|
-
tree_data["layout_coordinate_system"] = "polar"
|
|
66
|
-
else:
|
|
67
|
-
tree_data["layout_coordinate_system"] = "cartesian"
|
|
15
|
+
def postorder(self) -> Sequence[Any]:
|
|
16
|
+
return self.tree.postorder()
|
|
68
17
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for node in preorder_fun(tree):
|
|
72
|
-
for child in children_fun(node):
|
|
73
|
-
if directed == "parent":
|
|
74
|
-
edge_data["_ipx_source"].append(child)
|
|
75
|
-
edge_data["_ipx_target"].append(node)
|
|
76
|
-
else:
|
|
77
|
-
edge_data["_ipx_source"].append(node)
|
|
78
|
-
edge_data["_ipx_target"].append(child)
|
|
79
|
-
edge_df = pd.DataFrame(edge_data)
|
|
80
|
-
tree_data["edge_df"] = edge_df
|
|
18
|
+
def get_leaves(self) -> Sequence[Any]:
|
|
19
|
+
return self.tree.tips()
|
|
81
20
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if np.isscalar(vertex_labels) and vertex_labels:
|
|
86
|
-
tree_data["vertex_df"]["label"] = [
|
|
87
|
-
x.name for x in tree_data["vertices"].index
|
|
88
|
-
]
|
|
89
|
-
elif not np.isscalar(vertex_labels):
|
|
90
|
-
# If a dict-like object is passed, it can be incomplete (e.g. only the leaves):
|
|
91
|
-
# we fill the rest with empty strings which are not going to show up in the plot.
|
|
92
|
-
if isinstance(vertex_labels, pd.Series):
|
|
93
|
-
vertex_labels = dict(vertex_labels)
|
|
94
|
-
if isinstance(vertex_labels, dict):
|
|
95
|
-
for vertex in tree_data["vertex_df"].index:
|
|
96
|
-
if vertex not in vertex_labels:
|
|
97
|
-
vertex_labels[vertex] = ""
|
|
98
|
-
tree_data["vertex_df"]["label"] = pd.Series(vertex_labels)
|
|
21
|
+
@staticmethod
|
|
22
|
+
def get_children(node: Any) -> Sequence[Any]:
|
|
23
|
+
return node.children
|
|
99
24
|
|
|
100
|
-
|
|
25
|
+
@staticmethod
|
|
26
|
+
def get_branch_length(node: Any) -> Optional[float]:
|
|
27
|
+
return node.length
|
|
101
28
|
|
|
102
|
-
|
|
29
|
+
@staticmethod
|
|
30
|
+
def check_dependencies() -> bool:
|
|
103
31
|
try:
|
|
104
32
|
from skbio import TreeNode
|
|
105
33
|
except ImportError:
|
|
106
34
|
return False
|
|
107
35
|
return True
|
|
108
36
|
|
|
109
|
-
|
|
37
|
+
@staticmethod
|
|
38
|
+
def tree_type():
|
|
110
39
|
from skbio import TreeNode
|
|
111
40
|
|
|
112
41
|
return TreeNode
|