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.
@@ -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(tree, layout, **kwargs)
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
- def check_dependencies(self) -> bool:
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
- def graph_type(self):
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
- def check_dependencies(self) -> bool:
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
- def graph_type(self):
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 collections.abc import Hashable
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 __call__(
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
- tree_data = {
38
- "root": tree.root,
39
- "leaves": tree.get_terminals(),
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
- # Add vertex_df including layout
47
- tree_data["vertex_df"] = normalise_tree_layout(
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
- # Add edge_df
63
- edge_data = {"_ipx_source": [], "_ipx_target": []}
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
- # Add vertex labels
76
- if vertex_labels is None:
77
- vertex_labels = False
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
- return tree_data
31
+ @staticmethod
32
+ def get_branch_length(node: Any) -> Optional[float]:
33
+ return node.branch_length
94
34
 
95
- def check_dependencies(self) -> bool:
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
- def tree_type(self):
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 __call__(
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
- # Add vertex_df including layout
54
- tree_data["vertex_df"] = normalise_tree_layout(
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
- # Add edge_df
70
- edge_data = {"_ipx_source": [], "_ipx_target": []}
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
- # Add vertex labels
83
- if vertex_labels is None:
84
- vertex_labels = False
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
- return tree_data
25
+ @staticmethod
26
+ def get_branch_length(node: Any) -> Optional[float]:
27
+ return node.length
101
28
 
102
- def check_dependencies(self) -> bool:
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
- def tree_type(self):
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 collections.abc import Hashable
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 __call__(
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 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
- # Add vertex_df including layout
54
- tree_data["vertex_df"] = normalise_tree_layout(
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
- # Add edge_df
70
- edge_data = {"_ipx_source": [], "_ipx_target": []}
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
- # Add vertex labels
83
- if vertex_labels is None:
84
- vertex_labels = False
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
- return tree_data
28
+ @staticmethod
29
+ def get_branch_length(node: Any) -> Optional[float]:
30
+ return node.dist
101
31
 
102
- def check_dependencies(self) -> bool:
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
- def tree_type(self):
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 __call__(
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
- # Add vertex_df including layout
54
- tree_data["vertex_df"] = normalise_tree_layout(
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
- # Add edge_df
70
- edge_data = {"_ipx_source": [], "_ipx_target": []}
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
- # Add vertex labels
83
- if vertex_labels is None:
84
- vertex_labels = False
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
- return tree_data
25
+ @staticmethod
26
+ def get_branch_length(node: Any) -> Optional[float]:
27
+ return node.length
101
28
 
102
- def check_dependencies(self) -> bool:
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
- def tree_type(self):
37
+ @staticmethod
38
+ def tree_type():
110
39
  from skbio import TreeNode
111
40
 
112
41
  return TreeNode