diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f8fb0ba..70a4e895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.25.4] - 2025-04-04 +- Docs: Clean up docstring where possible and relevant. + ## [0.25.3] - 2025-03-30 ### Changed: - Docs: Deduplicate type hint and default value in docstrings. @@ -230,7 +233,7 @@ If there are clashing attributes, only those that have values will be replaced. assertion checks twice. This leads to 5% improvement in timings for a tree with 10000 nodes, averaged across 10 runs. - Misc: Abstract out assertion checks for empty dataframe and duplicate attribute. - Misc: Abstract out logic for checking null and filtering attributes. -- Misc: Optimization in dictionary and dataframe operations. +- Misc: Optimisation in dictionary and dataframe operations. ### Fixed - Tree Constructor: `dict_to_tree` no longer uses dataframe operations, leading to 33% improvement in timings for a tree with 10000 nodes, averaged across 10 runs. The resulting data type of node follows the dictionary exactly, @@ -658,7 +661,7 @@ ignore null attribute columns. ## [0.5.3] - 2022-11-11 ### Added -- DAG and Tree Exporter: More customizations allowed on edges. +- DAG and Tree Exporter: More customisations allowed on edges. - Misc: Add Tips and Tricks to documentation (Weighted Trees, Merging Trees). ### Fixed - Tree Modifier: Fix issue with `merge_children` argument not working as expected. @@ -725,7 +728,7 @@ ignore null attribute columns. ## [0.3.1] - 2022-11-07 ### Added -- Tree and DAG Exporter: More customizations for Node to dot and DAGNode to dot. +- Tree and DAG Exporter: More customisations for Node to dot and DAGNode to dot. ## [0.3.0] - 2022-11-05 ### Added @@ -733,7 +736,7 @@ ignore null attribute columns. - Utility Iterator: DAG traversal methods. ### Changed -- Tree Exporter and DAG Exporter: More customizations for Node to dot and DAGNode to dot. +- Tree Exporter and DAG Exporter: More customisations for Node to dot and DAGNode to dot. ## [0.2.0] - 2022-11-03 ### Added @@ -752,7 +755,8 @@ ignore null attribute columns. - Utility Iterator: Tree traversal methods. - Workflow To Do App: Tree use case with to-do list implementation. -[Unreleased]: https://github.com/kayjan/bigtree/compare/0.25.3...HEAD +[Unreleased]: https://github.com/kayjan/bigtree/compare/0.25.4...HEAD +[0.25.4]: https://github.com/kayjan/bigtree/compare/0.25.3...0.25.4 [0.25.3]: https://github.com/kayjan/bigtree/compare/0.25.2...0.25.3 [0.25.2]: https://github.com/kayjan/bigtree/compare/0.25.1...0.25.2 [0.25.1]: https://github.com/kayjan/bigtree/compare/0.25.0...0.25.1 diff --git a/assets/docs/dag_construct.png b/assets/docs/dag_construct.png deleted file mode 100644 index 88812891..00000000 Binary files a/assets/docs/dag_construct.png and /dev/null differ diff --git a/bigtree/__init__.py b/bigtree/__init__.py index 3a7e76c6..c7bcf218 100644 --- a/bigtree/__init__.py +++ b/bigtree/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.25.3" +__version__ = "0.25.4" from bigtree.binarytree.construct import list_to_binarytree from bigtree.dag.construct import dataframe_to_dag, dict_to_dag, list_to_dag diff --git a/bigtree/dag/construct.py b/bigtree/dag/construct.py index cec3c23a..b804431f 100644 --- a/bigtree/dag/construct.py +++ b/bigtree/dag/construct.py @@ -64,8 +64,8 @@ def dict_to_dag( parent_key: str = "parents", node_type: Type[T] = dagnode.DAGNode, # type: ignore[assignment] ) -> T: - """Construct DAG from nested dictionary, ``key``: child name, ``value``: dictionary of parent names, attribute - name, and attribute value. Note that node names must be unique. + """Construct DAG from nested dictionary, ``key``: child name, ``value``: dictionary of parent names and attributes. + Note that node names must be unique. Examples: >>> from bigtree import dict_to_dag, dag_iterator @@ -82,7 +82,7 @@ def dict_to_dag( Args: relation_attrs: dictionary containing node, node parents, and node attribute information, - key: child name, value: dictionary of parent names, node attribute, and attribute value + key: child name, value: dictionary of parent names and attributes parent_key: key of dictionary to retrieve list of parents name node_type: node type of DAG to be created diff --git a/bigtree/dag/export.py b/bigtree/dag/export.py index 34124efc..f526d8ef 100644 --- a/bigtree/dag/export.py +++ b/bigtree/dag/export.py @@ -58,9 +58,8 @@ def dag_to_dict( attr_dict: Dict[str, str] = {}, all_attrs: bool = False, ) -> Dict[str, Any]: - """Export DAG to dictionary. - - Exported dictionary will have key as child name, and parent names and node attributes as a nested dictionary. + """Export DAG to dictionary. Exported dictionary will have key as child name, and values as a dictionary of parent + names and node attributes. Examples: >>> from bigtree import DAGNode, dag_to_dict @@ -75,8 +74,8 @@ def dag_to_dict( Args: dag: DAG to be exported parent_key: dictionary key for `node.parent.node_name` - attr_dict: dictionary mapping node attributes to dictionary key, - key: node attributes, value: corresponding dictionary key + attr_dict: dictionary mapping node attributes to dictionary key, key: node attributes, value: corresponding + dictionary key all_attrs: indicator whether to retrieve all `Node` attributes Returns: @@ -145,8 +144,8 @@ def dag_to_dataframe( dag: DAG to be exported name_col: column name for `node.node_name` parent_col: column name for `node.parent.node_name` - attr_dict: dictionary mapping node attributes to column name, - key: node attributes, value: corresponding column in dataframe + attr_dict: dictionary mapping node attributes to column name, key: node attributes, value: corresponding column + in dataframe all_attrs: indicator whether to retrieve all `Node` attributes Returns: @@ -192,8 +191,8 @@ def dag_to_dot( node_attr: str = "", edge_attr: str = "", ) -> pydot.Dot: - r"""Export DAG or list of DAGs to image. Note that node names must be unique. Possible node attributes include - style, fillcolor, or shape. + r"""Export DAG or list of DAGs to image. Note that node names must be unique. Possible node attributes include style, + fillcolor, or shape. Examples: >>> from bigtree import DAGNode, dag_to_dot @@ -225,15 +224,15 @@ def dag_to_dot( Args: dag: DAG or list of DAGs to be exported - rankdir: set direction of graph layout, can be 'TB', 'BT, 'LR', or 'RL' + rankdir: set direction of graph layout, accepts 'TB', 'BT, 'LR', or 'RL' bg_colour: background color of image node_colour: fill colour of nodes node_shape: shape of nodes. Possible node_shape include "circle", "square", "diamond", "triangle" edge_colour: colour of edges - node_attr: node attribute for style, overrides node_colour. - Possible node attributes include {"style": "filled", "fillcolor": "gold"} - edge_attr: edge attribute for style, overrides edge_colour. - Possible edge attributes include {"style": "bold", "label": "edge label", "color": "black"} + node_attr: node attribute for style, overrides node_colour. Possible node attributes include {"style": "filled", + "fillcolor": "gold"} + edge_attr: edge attribute for style, overrides edge_colour. Possible edge attributes include {"style": "bold", + "label": "edge label", "color": "black"} Returns: pydot object of DAG diff --git a/bigtree/node/basenode.py b/bigtree/node/basenode.py index e4aeb92a..1976ab7c 100644 --- a/bigtree/node/basenode.py +++ b/bigtree/node/basenode.py @@ -15,11 +15,11 @@ class BaseNode: """ - BaseNode extends any Python class to a tree node. - Nodes can have attributes if they are initialized from `Node`, *dictionary*, or *pandas DataFrame*. + BaseNode extends any Python class to a tree node. Nodes can have attributes if they are initialized from `Node`, + *dictionary*, or *pandas DataFrame*. - Nodes can be linked to each other with `parent` and `children` setter methods, - or using bitshift operator with the convention `parent_node >> child_node` or `child_node << parent_node`. + Nodes can be linked to each other with `parent` and `children` setter methods, or using bitshift operator with the + convention `parent_node >> child_node` or `child_node << parent_node`. Examples: >>> from bigtree import Node, print_tree @@ -67,8 +67,8 @@ class BaseNode: **BaseNode Creation** - Node can be created by instantiating a `BaseNode` class or by using a *dictionary*. - If node is created with dictionary, all keys of dictionary will be stored as class attributes. + Node can be created by instantiating a `BaseNode` class or by using a *dictionary*. If node is created with + dictionary, all keys of dictionary will be stored as class attributes. >>> from bigtree import Node >>> root = Node.from_dict({"name": "a", "age": 90}) @@ -642,7 +642,7 @@ def set_attrs(self, attrs: Dict[str, Any]) -> None: Node(/a, age=90) Args: - attrs.: attribute dictionary, key: attribute name, value: attribute value + attrs: attribute dictionary, key: attribute name, value: attribute value """ self.__dict__.update(attrs) diff --git a/bigtree/node/binarynode.py b/bigtree/node/binarynode.py index 49a7766b..f0154a63 100644 --- a/bigtree/node/binarynode.py +++ b/bigtree/node/binarynode.py @@ -12,8 +12,8 @@ class BinaryNode(node.Node): BinaryNode is an extension of Node, and is able to extend to any Python class for Binary Tree implementation. Nodes can have attributes if they are initialized from `BinaryNode`, *dictionary*, or *pandas DataFrame*. - BinaryNode can be linked to each other with `children`, `left`, or `right` setter methods. - If initialized with `children`, it must be length 2, denoting left and right child. + BinaryNode can be linked to each other with `children`, `left`, or `right` setter methods. If initialized with + `children`, it must be length 2, denoting left and right child. Examples: >>> from bigtree import BinaryNode, print_tree @@ -39,8 +39,8 @@ class BinaryNode(node.Node): **BinaryNode Creation** - Node can be created by instantiating a `BinaryNode` class or by using a *dictionary*. - If node is created with dictionary, all keys of dictionary will be stored as class attributes. + Node can be created by instantiating a `BinaryNode` class or by using a *dictionary*. If node is created with + dictionary, all keys of dictionary will be stored as class attributes. >>> from bigtree import BinaryNode >>> a = BinaryNode.from_dict({"name": "1"}) diff --git a/bigtree/node/dagnode.py b/bigtree/node/dagnode.py index 13953e00..3b30fc22 100644 --- a/bigtree/node/dagnode.py +++ b/bigtree/node/dagnode.py @@ -9,15 +9,15 @@ class DAGNode: """ - Base DAGNode extends any Python class to a DAG node, for DAG implementation. - In DAG implementation, a node can have multiple parents. + Base DAGNode extends any Python class to a DAG node, for DAG implementation. In DAG implementation, a node can have + multiple parents. Parents and children cannot be reassigned once assigned, as Nodes are allowed to have multiple parents and children. - If each node only has one parent, use `Node` class. - DAGNodes can have attributes if they are initialized from `DAGNode` or dictionary. + If each node only has one parent, use `Node` class. DAGNodes can have attributes if they are initialized from + `DAGNode` or dictionary. - DAGNode can be linked to each other with `parents` and `children` setter methods, - or using bitshift operator with the convention `parent_node >> child_node` or `child_node << parent_node`. + DAGNode can be linked to each other with `parents` and `children` setter methods, or using bitshift operator with + the convention `parent_node >> child_node` or `child_node << parent_node`. Examples: >>> from bigtree import DAGNode @@ -55,8 +55,8 @@ class DAGNode: **DAGNode Creation** - Node can be created by instantiating a `DAGNode` class or by using a *dictionary*. - If node is created with dictionary, all keys of dictionary will be stored as class attributes. + Node can be created by instantiating a `DAGNode` class or by using a *dictionary*. If node is created with dictionary, + all keys of dictionary will be stored as class attributes. >>> from bigtree import DAGNode >>> a = DAGNode.from_dict({"name": "a", "age": 90}) diff --git a/bigtree/node/node.py b/bigtree/node/node.py index cf1887b7..ce3e2999 100644 --- a/bigtree/node/node.py +++ b/bigtree/node/node.py @@ -9,8 +9,8 @@ class Node(basenode.BaseNode): """ - Node is an extension of BaseNode, and is able to extend to any Python class. - Nodes can have attributes if they are initialized from `Node`, *dictionary*, or *pandas DataFrame*. + Node is an extension of BaseNode, and is able to extend to any Python class. Nodes can have attributes if they are + initialized from `Node`, *dictionary*, or *pandas DataFrame*. !!! note Node names cannot contain separator symbol! This will not throw error, but you might run into issues @@ -45,8 +45,8 @@ class Node(basenode.BaseNode): **Node Creation** - Node can be created by instantiating a `Node` class or by using a *dictionary*. - If node is created with dictionary, all keys of dictionary will be stored as class attributes. + Node can be created by instantiating a `Node` class or by using a *dictionary*. If node is created with dictionary, + all keys of dictionary will be stored as class attributes. >>> from bigtree import Node >>> a = Node.from_dict({"name": "a", "age": 90}) @@ -61,9 +61,8 @@ class Node(basenode.BaseNode): Get `Node` configuration - 1. ``node_name``: Get node name, without accessing `name` directly. - This is the preferred way to access node name as `node_name` is - immutable, whereas `name` is mutable. + 1. ``node_name``: Get node name, without accessing `name` directly. This is the preferred way to access node name + as `node_name` is immutable, whereas `name` is mutable 2. ``path_name``: Get path name from root, separated by `sep` **Node Methods** @@ -74,7 +73,7 @@ class Node(basenode.BaseNode): 1. ``show()``: Print tree to console 2. ``hshow()``: Print tree in horizontal orientation to console - 2. ``vshow()``: Print tree in vertical orientation to console + 3. ``vshow()``: Print tree in vertical orientation to console ---- diff --git a/bigtree/tree/construct/dataframes.py b/bigtree/tree/construct/dataframes.py index 72023e43..14c96b10 100644 --- a/bigtree/tree/construct/dataframes.py +++ b/bigtree/tree/construct/dataframes.py @@ -45,23 +45,23 @@ def add_dataframe_to_tree_by_path( ) -> T: """Add nodes and attributes to tree *in-place*, return root of tree. Adds to existing tree from pandas DataFrame. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + `path_col` and `attribute_cols` specify columns for node path and attributes to add to existing tree. If columns are + not specified, `path_col` takes first column and all other columns are `attribute_cols` - `path_col` and `attribute_cols` specify columns for node path and attributes to add to existing tree. - If columns are not specified, `path_col` takes first column and all other columns are `attribute_cols` + - Only attributes in `attribute_cols` with non-null values will be added to the tree Path in path column should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). - - Path separator `sep` is for the input `path` and can differ from existing tree. + - For example: Path string "a/b" refers to Node("b") with parent Node("a") + - Path separator `sep` is for the input `path` and can differ from existing tree Path in path column can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> import pandas as pd @@ -139,13 +139,12 @@ def add_dataframe_to_tree_by_name( ) -> T: """Add attributes to existing tree *in-place*. Adds to existing tree from pandas DataFrame. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + `name_col` and `attribute_cols` specify columns for node name and attributes to add to existing tree. If columns are + not specified, the first column will be taken as name column and all other columns as attributes. - `name_col` and `attribute_cols` specify columns for node name and attributes to add to existing tree. - If columns are not specified, the first column will be taken as name column and all other columns as attributes. - - Input data node names that are not existing node names will be ignored. - Note that if multiple nodes have the same name, attributes will be added to all nodes sharing same name. + - Only attributes in `attribute_cols` with non-null values will be added to the tree + - Input data node names that are not existing node names will be ignored. Note that if multiple nodes have the same + name, attributes will be added to all nodes sharing same name Examples: >>> import pandas as pd @@ -209,23 +208,23 @@ def add_polars_to_tree_by_path( ) -> T: """Add nodes and attributes to tree *in-place*, return root of tree. Adds to existing tree from polars DataFrame. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + `path_col` and `attribute_cols` specify columns for node path and attributes to add to existing tree. If columns are + not specified, `path_col` takes first column and all other columns are `attribute_cols` - `path_col` and `attribute_cols` specify columns for node path and attributes to add to existing tree. - If columns are not specified, `path_col` takes first column and all other columns are `attribute_cols` + - Only attributes in `attribute_cols` with non-null values will be added to the tree Path in path column should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). - - Path separator `sep` is for the input `path` and can differ from existing tree. + - For example: Path string "a/b" refers to Node("b") with parent Node("a") + - Path separator `sep` is for the input `path` and can differ from existing tree Path in path column can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> import polars as pl @@ -258,8 +257,8 @@ def add_polars_to_tree_by_path( tree: existing tree data: data containing node path and attribute information path_col: column of data containing `path_name` information, if not set, it will take the first column of data - attribute_cols: columns of data containing node attribute information, if not set, it will take all - columns of data except `path_col` + attribute_cols: columns of data containing node attribute information, if not set, it will take all columns of + data except `path_col` sep: path separator for input `path_col` duplicate_name_allowed: indicator if nodes with duplicate ``Node`` name is allowed @@ -305,13 +304,12 @@ def add_polars_to_tree_by_name( ) -> T: """Add attributes to existing tree *in-place*. Adds to existing tree from polars DataFrame. - Only attributes in `attribute_cols` with non-null values will be added to the tree. - - `name_col` and `attribute_cols` specify columns for node name and attributes to add to existing tree. - If columns are not specified, the first column will be taken as name column and all other columns as attributes. + `name_col` and `attribute_cols` specify columns for node name and attributes to add to existing tree. If columns are + not specified, the first column will be taken as name column and all other columns as attributes. - Input data node names that are not existing node names will be ignored. - Note that if multiple nodes have the same name, attributes will be added to all nodes sharing same name. + - Only attributes in `attribute_cols` with non-null values will be added to the tree + - Input data node names that are not existing node names will be ignored. Note that if multiple nodes have the same + name, attributes will be added to all nodes sharing same name Examples: >>> import polars as pl @@ -373,22 +371,22 @@ def dataframe_to_tree( ) -> T: """Construct tree from pandas DataFrame using path, return root of tree. - `path_col` and `attribute_cols` specify columns for node path and attributes to construct tree. - If columns are not specified, `path_col` takes first column and all other columns are `attribute_cols`. + `path_col` and `attribute_cols` specify columns for node path and attributes to construct tree. If columns are not + specified, `path_col` takes first column and all other columns are `attribute_cols`. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + - Only attributes in `attribute_cols` with non-null values will be added to the tree - Path in path column can start from root node `name`, or start with `sep`. + Path in path column should contain ``Node`` name, separated by `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string "a/b" refers to Node("b") with parent Node("a") - Path in path column should contain ``Node`` name, separated by `sep`. + Path in path column can start from root node `name`, or start with `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> import pandas as pd @@ -419,8 +417,8 @@ def dataframe_to_tree( Args: data: data containing path and node attribute information path_col: column of data containing `path_name` information, if not set, it will take the first column of data - attribute_cols: columns of data containing node attribute information, if not set, it will take all - columns of data except `path_col` + attribute_cols: columns of data containing node attribute information, if not set, it will take all columns of + data except `path_col` sep: path separator of input `path_col` and created tree duplicate_name_allowed: indicator if nodes with duplicate ``Node`` name is allowed node_type: node type of tree to be created @@ -482,16 +480,15 @@ def dataframe_to_tree_by_relation( Root node is inferred when parent name is empty, or when name appears in parent column but not in child column. - Since tree is created from parent-child names, only names of leaf nodes may be repeated. - Error will be thrown if names of intermediate nodes are repeated as there will be confusion. - This error can be ignored by setting `allow_duplicates` to be True. + Since tree is created from parent-child names, only names of leaf nodes may be repeated. Error will be thrown if + names of intermediate nodes are repeated as there will be confusion. This error can be ignored by setting + `allow_duplicates` to be True. - `child_col` and `parent_col` specify columns for child name and parent name to construct tree. - `attribute_cols` specify columns for node attribute for child name. - If columns are not specified, `child_col` takes first column, `parent_col` takes second column, and all other - columns are `attribute_cols`. + `child_col` and `parent_col` specify columns for child name and parent name to construct tree. `attribute_cols` specify + columns for node attribute for child name. If columns are not specified, `child_col` takes first column, `parent_col` + takes second column, and all other columns are `attribute_cols`. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + - Only attributes in `attribute_cols` with non-null values will be added to the tree Examples: >>> import pandas as pd @@ -525,8 +522,7 @@ def dataframe_to_tree_by_relation( parent_col: column of data containing parent name information, if not set, it will take the second column of data attribute_cols: columns of data containing node attribute information, if not set, it will take all columns of data except `child_col` and `parent_col` - allow_duplicates: allow duplicate intermediate nodes such that child node will - be tagged to multiple parent nodes + allow_duplicates: allow duplicate intermediate nodes such that child node will be tagged to multiple parent nodes node_type: node type of tree to be created Returns: @@ -606,22 +602,22 @@ def polars_to_tree( ) -> T: """Construct tree from polars DataFrame using path, return root of tree. - `path_col` and `attribute_cols` specify columns for node path and attributes to construct tree. - If columns are not specified, `path_col` takes first column and all other columns are `attribute_cols`. + `path_col` and `attribute_cols` specify columns for node path and attributes to construct tree. If columns are not + specified, `path_col` takes first column and all other columns are `attribute_cols`. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + - Only attributes in `attribute_cols` with non-null values will be added to the tree - Path in path column can start from root node `name`, or start with `sep`. + Path in path column should contain ``Node`` name, separated by `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string "a/b" refers to Node("b") with parent Node("a") - Path in path column should contain ``Node`` name, separated by `sep`. + Path in path column can start from root node `name`, or start with `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> import polars as pl @@ -716,16 +712,15 @@ def polars_to_tree_by_relation( Root node is inferred when parent name is empty, or when name appears in parent column but not in child column. - Since tree is created from parent-child names, only names of leaf nodes may be repeated. - Error will be thrown if names of intermediate nodes are repeated as there will be confusion. - This error can be ignored by setting `allow_duplicates` to be True. + Since tree is created from parent-child names, only names of leaf nodes may be repeated. Error will be thrown if + names of intermediate nodes are repeated as there will be confusion. This error can be ignored by setting + `allow_duplicates` to be True. - `child_col` and `parent_col` specify columns for child name and parent name to construct tree. - `attribute_cols` specify columns for node attribute for child name. - If columns are not specified, `child_col` takes first column, `parent_col` takes second column, and all other - columns are `attribute_cols`. + `child_col` and `parent_col` specify columns for child name and parent name to construct tree. `attribute_cols` specify + columns for node attribute for child name. If columns are not specified, `child_col` takes first column, `parent_col` + takes second column, and all other columns are `attribute_cols`. - Only attributes in `attribute_cols` with non-null values will be added to the tree. + - Only attributes in `attribute_cols` with non-null values will be added to the tree Examples: >>> import polars as pl @@ -759,8 +754,7 @@ def polars_to_tree_by_relation( parent_col: column of data containing parent name information, if not set, it will take the second column of data attribute_cols: columns of data containing node attribute information, if not set, it will take all columns of data except `child_col` and `parent_col` - allow_duplicates: allow duplicate intermediate nodes such that child node will - be tagged to multiple parent nodes + allow_duplicates: allow duplicate intermediate nodes such that child node will be tagged to multiple parent nodes node_type: node type of tree to be created Returns: diff --git a/bigtree/tree/construct/dictionaries.py b/bigtree/tree/construct/dictionaries.py index cb9c0ccc..1ce4dccd 100644 --- a/bigtree/tree/construct/dictionaries.py +++ b/bigtree/tree/construct/dictionaries.py @@ -29,16 +29,16 @@ def add_dict_to_tree_by_path( Path should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). - - Path separator `sep` is for the input `path` and can differ from existing tree. + - For example: Path string "a/b" refers to Node("b") with parent Node("a") + - Path separator `sep` is for the input `path` and can differ from existing tree Path can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> from bigtree import Node, add_dict_to_tree_by_path @@ -66,8 +66,8 @@ def add_dict_to_tree_by_path( Args: tree: existing tree - path_attrs: dictionary containing node path and attribute information, - key: node path, value: dict of node attribute name and attribute value + path_attrs: dictionary containing node path and attribute information, key: node path, value: dict of node + attribute name and attribute value sep: path separator for input `path_attrs` duplicate_name_allowed: indicator if nodes with duplicate ``Node`` name is allowed @@ -95,8 +95,8 @@ def add_dict_to_tree_by_name(tree: T, name_attrs: Dict[str, Dict[str, Any]]) -> All attributes in `name_attrs` will be added to the tree, including attributes with null values. - Input dictionary keys that are not existing node names will be ignored. - Note that if multiple nodes have the same name, attributes will be added to all nodes sharing the same name. + Input dictionary keys that are not existing node names will be ignored. Note that if multiple nodes have the same + name, attributes will be added to all nodes sharing the same name. Examples: >>> from bigtree import Node, add_dict_to_tree_by_name @@ -145,15 +145,15 @@ def dict_to_tree( Path should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). + - For example: Path string "a/b" refers to Node("b") with parent Node("a") Path can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node All attributes in `path_attrs` will be added to the tree, including attributes with null values. @@ -181,8 +181,8 @@ def dict_to_tree( └── f [age=38] Args: - path_attrs: dictionary containing path and node attribute information, - key: path, value: dict of tree attribute and attribute value + path_attrs: dictionary containing node path and attribute information, key: node path, value: dict of node + attribute name and attribute value sep: path separator of input `path_attrs` and created tree duplicate_name_allowed: indicator if nodes with duplicate ``Node`` name is allowed node_type: node type of tree to be created @@ -232,9 +232,9 @@ def nested_dict_to_tree( ) -> T: """Construct tree from nested recursive dictionary. - - ``key``: `name_key`, `child_key`, or any attributes key. - - ``value`` of `name_key`: node name. - - ``value`` of `child_key`: list of dict containing `name_key` and `child_key` (recursive). + - ``key``: `name_key`, `child_key`, or any attributes key + - ``value`` of `name_key`: node name + - ``value`` of `child_key`: list of dict containing `name_key` and `child_key` (recursive) Examples: >>> from bigtree import nested_dict_to_tree diff --git a/bigtree/tree/construct/lists.py b/bigtree/tree/construct/lists.py index f60ff5ce..dec9fbd6 100644 --- a/bigtree/tree/construct/lists.py +++ b/bigtree/tree/construct/lists.py @@ -33,15 +33,15 @@ def list_to_tree( Path should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). + - For example: Path string "a/b" refers to Node("b") with parent Node("a") Path can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node Examples: >>> from bigtree import list_to_tree @@ -93,9 +93,9 @@ def list_to_tree_by_relation( Root node is inferred when parent is empty, or when name appears as parent but not as child. - Since tree is created from parent-child names, only names of leaf nodes may be repeated. - Error will be thrown if names of intermediate nodes are repeated as there will be confusion. - This error can be ignored by setting `allow_duplicates` to be True. + Since tree is created from parent-child names, only names of leaf nodes may be repeated. Error will be thrown if + names of intermediate nodes are repeated as there will be confusion. This error can be ignored by setting + `allow_duplicates` to be True. Examples: >>> from bigtree import list_to_tree_by_relation diff --git a/bigtree/tree/construct/strings.py b/bigtree/tree/construct/strings.py index e75eec73..08846959 100644 --- a/bigtree/tree/construct/strings.py +++ b/bigtree/tree/construct/strings.py @@ -29,16 +29,16 @@ def add_path_to_tree( Path should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). - - Path separator `sep` is for the input `path` and can differ from existing tree. + - For example: Path string "a/b" refers to Node("b") with parent Node("a") + - Path separator `sep` is for the input `path` and can differ from existing tree Path can start from root node `name`, or start with `sep`. - - For example: Path string can be "/a/b" or "a/b", if sep is "/". + - For example: Path string can be "/a/b" or "a/b", if sep is "/" All paths should start from the same root node. - - For example: Path strings should be "a/b", "a/c", "a/b/d" etc., and should not start with another root node. + - For example: Path strings should be "a/b", "a/c", "a/b/d" etc. and should not start with another root node All attributes in `node_attrs` will be added to the tree, including attributes with null values. @@ -105,7 +105,7 @@ def str_to_tree( tree_prefix_list: List[str] = [], node_type: Type[T] = node.Node, # type: ignore[assignment] ) -> T: - r"""Construct tree from tree string + r"""Construct tree from tree string. Examples: >>> from bigtree import str_to_tree @@ -178,16 +178,16 @@ def newick_to_tree( In the Newick Notation (or New Hampshire Notation) - - Tree is represented in round brackets i.e., `(child1,child2,child3)parent`. - - If there are nested trees, they will be in nested round brackets i.e., `((grandchild1)child1,(grandchild2,grandchild3)child2)parent`. - - If there is length attribute, they will be beside the name i.e., `(child1:0.5,child2:0.1)parent`. - - If there are other attributes, attributes are represented in square brackets i.e., `(child1:0.5[S:human],child2:0.1[S:human])parent[S:parent]`. + - Tree is represented in round brackets i.e., `(child1,child2,child3)parent` + - If there are nested trees, they will be in nested round brackets i.e., `((grandchild1)child1,(grandchild2,grandchild3)child2)parent` + - If there is length attribute, they will be beside the name i.e., `(child1:0.5,child2:0.1)parent` + - If there are other attributes, attributes are represented in square brackets i.e., `(child1:0.5[S:human],child2:0.1[S:human])parent[S:parent]` Variations supported - Support special characters (`[`, `]`, `(`, `)`, `:`, `,`) in node name, attribute name, and attribute values if - they are enclosed in single quotes i.e., '(name:!)'. - - If there are no node names, it will be auto-filled with convention `nodeN` with N representing a number. + they are enclosed in single quotes i.e., '(name:!)' + - If there are no node names, it will be auto-filled with convention `nodeN` with N representing a number Examples: >>> from bigtree import newick_to_tree diff --git a/bigtree/tree/export/_stdout.py b/bigtree/tree/export/_stdout.py index f24137ee..c0799327 100644 --- a/bigtree/tree/export/_stdout.py +++ b/bigtree/tree/export/_stdout.py @@ -16,7 +16,7 @@ def calculate_stem_pos(length: int) -> int: - """Calculate stem position based on length + """Calculate stem position based on length. Args: length: length of node @@ -40,7 +40,7 @@ def format_node( min_width: int = 0, add_buffer: bool = True, ) -> List[str]: - """Format node to be same width, able to customise whether to add border + """Format node to be same width, able to customise whether to add border. Args: _node: node to format @@ -137,7 +137,7 @@ def format_node( def horizontal_join(node_displays: List[List[str]], spacing: int = 0) -> List[str]: - """Horizontally join multiple node displays, for displaying tree vertically + """Horizontally join multiple node displays, for displaying tree vertically. Args: node_displays: multiple node displays belonging to the same row @@ -167,7 +167,7 @@ def horizontal_join(node_displays: List[List[str]], spacing: int = 0) -> List[st def vertical_join(node_displays: List[List[str]]) -> List[str]: - """Vertically join multiple node displays, for displaying tree horizontally + """Vertically join multiple node displays, for displaying tree horizontally. Args: node_displays: multiple node displays belonging to the same column diff --git a/bigtree/tree/export/dataframes.py b/bigtree/tree/export/dataframes.py index 82df2276..b047ab3c 100644 --- a/bigtree/tree/export/dataframes.py +++ b/bigtree/tree/export/dataframes.py @@ -82,42 +82,41 @@ def tree_to_dataframe( Returns: pandas DataFrame containing tree information """ - tree = tree.copy() data_list = [] - def _recursive_append(node: T) -> None: + def _recursive_append(_node: T) -> None: """Recursively iterate through node and its children to export to dataframe. Args: - node (Node): current node + _node: current node """ - if node: + if _node: if ( - (not max_depth or node.depth <= max_depth) - and (not skip_depth or node.depth > skip_depth) - and (not leaf_only or node.is_leaf) + (not max_depth or _node.depth <= max_depth) + and (not skip_depth or _node.depth > skip_depth) + and (not leaf_only or _node.is_leaf) ): data_child: Dict[str, Any] = {} if path_col: - data_child[path_col] = node.path_name + data_child[path_col] = _node.path_name if name_col: - data_child[name_col] = node.node_name + data_child[name_col] = _node.node_name if parent_col: parent_name = None - if node.parent: - parent_name = node.parent.node_name + if _node.parent: + parent_name = _node.parent.node_name data_child[parent_col] = parent_name if all_attrs: data_child.update( - node.describe(exclude_attributes=["name"], exclude_prefix="_") + _node.describe(exclude_attributes=["name"], exclude_prefix="_") ) else: for k, v in attr_dict.items(): - data_child[v] = node.get_attr(k) + data_child[v] = _node.get_attr(k) data_list.append(data_child) - for _node in node.children: - _recursive_append(_node) + for _child in _node.children: + _recursive_append(_child) _recursive_append(tree) return pd.DataFrame(data_list) @@ -189,42 +188,41 @@ def tree_to_polars( Returns: polars DataFrame containing tree information """ - tree = tree.copy() data_list = [] - def _recursive_append(node: T) -> None: + def _recursive_append(_node: T) -> None: """Recursively iterate through node and its children to export to dataframe. Args: - node (Node): current node + _node: current node """ - if node: + if _node: if ( - (not max_depth or node.depth <= max_depth) - and (not skip_depth or node.depth > skip_depth) - and (not leaf_only or node.is_leaf) + (not max_depth or _node.depth <= max_depth) + and (not skip_depth or _node.depth > skip_depth) + and (not leaf_only or _node.is_leaf) ): data_child: Dict[str, Any] = {} if path_col: - data_child[path_col] = node.path_name + data_child[path_col] = _node.path_name if name_col: - data_child[name_col] = node.node_name + data_child[name_col] = _node.node_name if parent_col: parent_name = None - if node.parent: - parent_name = node.parent.node_name + if _node.parent: + parent_name = _node.parent.node_name data_child[parent_col] = parent_name if all_attrs: data_child.update( - node.describe(exclude_attributes=["name"], exclude_prefix="_") + _node.describe(exclude_attributes=["name"], exclude_prefix="_") ) else: for k, v in attr_dict.items(): - data_child[v] = node.get_attr(k) + data_child[v] = _node.get_attr(k) data_list.append(data_child) - for _node in node.children: - _recursive_append(_node) + for _child in _node.children: + _recursive_append(_child) _recursive_append(tree) return pl.DataFrame(data_list) diff --git a/bigtree/tree/export/dictionaries.py b/bigtree/tree/export/dictionaries.py index 687386d0..5a856606 100644 --- a/bigtree/tree/export/dictionaries.py +++ b/bigtree/tree/export/dictionaries.py @@ -57,43 +57,42 @@ def tree_to_dict( Returns: Dictionary containing tree information """ - tree = tree.copy() data_dict = {} - def _recursive_append(node: T) -> None: + def _recursive_append(_node: T) -> None: """Recursively iterate through node and its children to export to dictionary. Args: - node: current node + _node: current node """ - if node: + if _node: if ( - (not max_depth or node.depth <= max_depth) - and (not skip_depth or node.depth > skip_depth) - and (not leaf_only or node.is_leaf) + (not max_depth or _node.depth <= max_depth) + and (not skip_depth or _node.depth > skip_depth) + and (not leaf_only or _node.is_leaf) ): data_child: Dict[str, Any] = {} if name_key: - data_child[name_key] = node.node_name + data_child[name_key] = _node.node_name if parent_key: parent_name = None - if node.parent: - parent_name = node.parent.node_name + if _node.parent: + parent_name = _node.parent.node_name data_child[parent_key] = parent_name if all_attrs: data_child.update( dict( - node.describe( + _node.describe( exclude_attributes=["name"], exclude_prefix="_" ) ) ) else: for k, v in attr_dict.items(): - data_child[v] = node.get_attr(k) - data_dict[node.path_name] = data_child - for _node in node.children: - _recursive_append(_node) + data_child[v] = _node.get_attr(k) + data_dict[_node.path_name] = data_child + for _child in _node.children: + _recursive_append(_child) _recursive_append(tree) return data_dict @@ -135,37 +134,36 @@ def tree_to_nested_dict( Returns: Dictionary containing tree information """ - tree = tree.copy() data_dict: Dict[str, List[Dict[str, Any]]] = {} - def _recursive_append(node: T, parent_dict: Dict[str, Any]) -> None: + def _recursive_append(_node: T, parent_dict: Dict[str, Any]) -> None: """Recursively iterate through node and its children to export to nested dictionary. Args: - node: current node + _node: current node parent_dict: parent dictionary """ - if node: - if not max_depth or node.depth <= max_depth: - data_child = {name_key: node.node_name} + if _node: + if not max_depth or _node.depth <= max_depth: + data_child = {name_key: _node.node_name} if all_attrs: data_child.update( dict( - node.describe( + _node.describe( exclude_attributes=["name"], exclude_prefix="_" ) ) ) else: for k, v in attr_dict.items(): - data_child[v] = node.get_attr(k) + data_child[v] = _node.get_attr(k) if child_key in parent_dict: parent_dict[child_key].append(data_child) else: parent_dict[child_key] = [data_child] - for _node in node.children: - _recursive_append(_node, data_child) + for _child in _node.children: + _recursive_append(_child, data_child) _recursive_append(tree, data_dict) return data_dict[child_key][0] diff --git a/bigtree/tree/export/images.py b/bigtree/tree/export/images.py index 90c43de7..62aff995 100644 --- a/bigtree/tree/export/images.py +++ b/bigtree/tree/export/images.py @@ -54,9 +54,8 @@ def tree_to_dot( node_attr: Callable[[T], Dict[str, Any]] | str = "", edge_attr: Callable[[T], Dict[str, Any]] | str = "", ) -> pydot.Dot: - r"""Export tree or list of trees to pydot.Dot object. Object can be - converted to other format, such as png, dot file or dot string. Dot - string can be imported to work with networkx. + r"""Export tree or list of trees to pydot.Dot object. Object can be converted to other format, such as png, dot file + or dot string. Dot string can be imported to work with networkx. Possible node attributes include style, fillcolor, shape. @@ -140,18 +139,18 @@ def tree_to_dot( Args: tree: tree or list of trees to be exported directed: indicator whether graph should be directed or undirected - rankdir: layout direction, accepts 'TB' (top to bottom), 'BT' (bottom to top), 'LR' (left to - right), or 'RL' (right to left) + rankdir: layout direction, accepts 'TB' (top to bottom), 'BT' (bottom to top), 'LR' (left to right), or 'RL' + (right to left) bg_colour: background color of image node_colour: fill colour of nodes node_shape: shape of nodes. Possible node_shape include "circle", "square", "diamond", "triangle" edge_colour: colour of edges - node_attr: If string type, it refers to ``Node`` attribute for node style. If callable type, it takes - in the node itself and returns the node style. This overrides `node_colour` and `node_shape`. - Possible node styles include {"style": "filled", "fillcolor": "gold", "shape": "diamond"} - edge_attr: If string type, it refers to ``Node`` attribute for edge style. If callable type, it takes - in the node itself and returns the edge style. This overrides `edge_colour`. Possible edge styles - include {"style": "bold", "label": "edge label", "color": "black"} + node_attr: If string type, it refers to ``Node`` attribute for node style. If callable type, it takes in the + node itself and returns the node style. This overrides `node_colour` and `node_shape`. Possible node styles + include {"style": "filled", "fillcolor": "gold", "shape": "diamond"} + edge_attr: If string type, it refers to ``Node`` attribute for edge style. If callable type, it takes in the + node itself and returns the edge style. This overrides `edge_colour`. Possible edge styles include + {"style": "bold", "label": "edge label", "color": "black"} Returns: Dot object of tree @@ -204,14 +203,16 @@ def _recursive_append(parent_name: Optional[str], child_node: T) -> None: child_name = child_label + str( name_dict[child_label].index(child_node.path_name) ) - node = pydot.Node(name=child_name, label=child_label, **_node_style) - _graph.add_node(node) + pydot_child_node = pydot.Node( + name=child_name, label=child_label, **_node_style + ) + _graph.add_node(pydot_child_node) if parent_name is not None: edge = pydot.Edge(parent_name, child_name, **_edge_style) _graph.add_edge(edge) - for child in child_node.children: - if child: - _recursive_append(child_name, child) + for _child in child_node.children: + if _child: + _recursive_append(child_name, _child) _recursive_append(None, _tree.root) return _graph @@ -252,9 +253,8 @@ def tree_to_pillow_graph( rect_width: int = 1, **kwargs: Any, ) -> Image.Image: - r"""Export tree to PIL.Image.Image object. Object can be converted to other - formats, such as jpg, or png. Image will look like a tree/graph-like structure, - accepts additional keyword arguments as input to `yield_tree`. + r"""Export tree to PIL.Image.Image object. Object can be converted to other formats, such as jpg, or png. Image will + look like a tree/graph-like structure, accepts additional keyword arguments as input to `yield_tree`. Customisations: @@ -440,9 +440,8 @@ def tree_to_pillow( bg_colour: Union[Tuple[int, int, int], str] = "white", **kwargs: Any, ) -> Image.Image: - """Export tree to PIL.Image.Image object. Object can be converted to other - formats, such as jpg, or png. Image will be similar format as `print_tree`, - accepts additional keyword arguments as input to `yield_tree`. + """Export tree to PIL.Image.Image object. Object can be converted to other formats, such as jpg, or png. Image will + be similar format as `print_tree`, accepts additional keyword arguments as input to `yield_tree`. Examples: >>> from bigtree import Node, tree_to_pillow @@ -534,7 +533,7 @@ def tree_to_mermaid( ) -> str: r"""Export tree to mermaid Markdown text. Accepts additional keyword arguments as input to `yield_tree`. - Parameters for customizations that apply to entire flowchart include: + Parameters for customisations that apply to entire flowchart include: - Title, `title` - Theme, `theme` - Layout direction, `rankdir` @@ -545,7 +544,7 @@ def tree_to_mermaid( - Node shape, `node_shape` - Edge arrow style, `edge_arrow` - Parameters for customizations that apply to customized nodes: + Parameters for customisations that apply to customised nodes: - Fill colour of nodes, fill under `node_attr` - Border colour of nodes, stroke under `node_attr` - Border width of nodes, stroke-width under `node_attr` @@ -560,7 +559,7 @@ def tree_to_mermaid( - neutral: great for black and white documents - dark: great for dark-mode - forest: shades of geen - - base: theme that can be modified, use it for customizations + - base: theme that can be modified, use it for customisations Possible rankdir: - `TB`: top-to-bottom @@ -638,7 +637,7 @@ def tree_to_mermaid( classDef default stroke-width:1 ``` - **Customize node shape, edge label, edge arrow, and custom node attributes** + **Customise node shape, edge label, edge arrow, and custom node attributes** >>> graph = tree_to_mermaid( ... root, @@ -668,21 +667,23 @@ def tree_to_mermaid( tree: tree to be exported title: title theme: theme or colour scheme - rankdir: layout direction, accepts 'TB' (top to bottom), 'BT' (bottom to top), 'LR' (left to right), 'RL' (right to left) + rankdir: layout direction, accepts 'TB' (top to bottom), 'BT' (bottom to top), 'LR' (left to right), 'RL' (right + to left) line_shape: line shape or curvature node_colour: fill colour of nodes, can be colour name or hexcode node_border_colour: border colour of nodes, can be colour name or hexcode node_border_width: width of node border node_shape: node shape, sets the shape of every node - node_shape_attr: If string type, it refers to ``Node`` attribute for node shape. If callable type, it takes in the node itself - and returns the node shape. This sets the shape of custom nodes, and overrides default `node_shape` + node_shape_attr: If string type, it refers to ``Node`` attribute for node shape. If callable type, it takes in + the node itself and returns the node shape. This sets the shape of custom nodes, and overrides default + `node_shape` edge_arrow: edge arrow style from parent to itself, sets the arrow style of every edge - edge_arrow_attr: If string type, it refers to ``Node`` attribute for edge arrow style. If callable type, it takes in the node - itself and returns the edge arrow style. This sets the edge arrow style of custom nodes from parent to itself, and - overrides default `edge_arrow` + edge_arrow_attr: If string type, it refers to ``Node`` attribute for edge arrow style. If callable type, it takes + in the node itself and returns the edge arrow style. This sets the edge arrow style of custom nodes from + parent to itself, and overrides default `edge_arrow` edge_label: ``Node`` attribute for edge label from parent to itself - node_attr: If string type, it refers to ``Node`` attribute for node style. If callable type, it takes in the node itself and - returns the node style. This overrides `node_colour`, `node_border_colour`, and `node_border_width` + node_attr: If string type, it refers to ``Node`` attribute for node style. If callable type, it takes in the node + itself and returns the node style. This overrides `node_colour`, `node_border_colour`, and `node_border_width` Returns: Mermaid string of tree diff --git a/bigtree/tree/export/stdout.py b/bigtree/tree/export/stdout.py index 5905b0c5..3170afd6 100644 --- a/bigtree/tree/export/stdout.py +++ b/bigtree/tree/export/stdout.py @@ -34,18 +34,18 @@ def print_tree( - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` - - Able to choose which attributes to show or show all attributes, using `attr_name_filter` and `all_attrs` + - Able to customise for maximum depth to print, using `max_depth` + - Able to choose which attributes to show or show all attributes, using `all_attrs` and `attr_list` - Able to omit showing of attributes if it is null, using `attr_omit_null` - - Able to customize open and close brackets if attributes are shown, using `attr_bracket` - - Able to customize style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` + - Able to customise open and close brackets if attributes are shown, using `attr_bracket` + - Able to customise style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (List[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters - - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, `RoundedPrintStyle`, - `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` + - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, + `RoundedPrintStyle`, `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` Examples: **Printing tree** @@ -226,15 +226,15 @@ def yield_tree( """Generator method for customizing printing of tree, starting from `tree`. - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` - - Able to customize style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` + - Able to customise for maximum depth to print, using `max_depth` + - Able to customise style, to choose from str, List[str], or inherit from constants.BasePrintStyle, using `style` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (List[str]): Choose own style for stem, branch, and final stem icons, they must have the same number of characters - - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, `RoundedPrintStyle`, - `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` + - (constants.BasePrintStyle): `ANSIPrintStyle`, `ASCIIPrintStyle`, `ConstPrintStyle`, `ConstBoldPrintStyle`, + `RoundedPrintStyle`, `DoublePrintStyle` style or inherit from `constants.BasePrintStyle` Examples: **Yield tree** @@ -374,21 +374,28 @@ def hprint_tree( - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` + - Able to customise for maximum depth to print, using `max_depth` - Able to hide names of intermediate nodes, using `intermediate_node_name` - Able to select horizontal spacing between nodes, using `spacing` - - Able to customize style, to choose from str, Iterable[str], or inherit from constants.BaseHPrintStyle, using `style` + - Able to customise style, to choose from str, Iterable[str], or inherit from constants.BaseHPrintStyle, using `style` - Able to toggle border, with border style to choose from str, Iterable[str], or inherit from constants.BorderStyle, using `border_style` - Able to have constant width output string or to strip the trailing spaces, using `strip` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must have the same number of characters - (constants.BaseHPrintStyle): `ANSIHPrintStyle`, `ASCIIHPrintStyle`, `ConstHPrintStyle`, `ConstBoldHPrintStyle`, `RoundedHPrintStyle`, `DoubleHPrintStyle` style or inherit from constants.BaseHPrintStyle + For border_style, + + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (Iterable[str]): Choose own style icons, they must be 1 character long + - (constants.BorderStyle): `ANSIBorderStyle`, `ASCIIBorderStyle`, `ConstBorderStyle`, `ConstBoldBorderStyle`, + `RoundedBorderStyle`, `DoubleBorderStyle` style or inherit from constants.BorderStyle + Examples: **Printing tree** @@ -527,21 +534,28 @@ def hyield_tree( - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` + - Able to customise for maximum depth to print, using `max_depth` - Able to hide names of intermediate nodes, using `intermediate_node_name` - Able to select horizontal spacing between nodes, using `spacing` - - Able to customize style, to choose from str, Iterable[str], or inherit from constants.BaseHPrintStyle, using `style` + - Able to customise style, to choose from str, Iterable[str], or inherit from constants.BaseHPrintStyle, using `style` - Able to toggle border, with border style to choose from str, Iterable[str], or inherit from constants.BorderStyle, using `border_style` - Able to have constant width output string or to strip the trailing spaces, using `strip` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must be 1 character long - (constants.BaseHPrintStyle): `ANSIHPrintStyle`, `ASCIIHPrintStyle`, `ConstHPrintStyle`, `ConstBoldHPrintStyle`, `RoundedHPrintStyle`, `DoubleHPrintStyle` style or inherit from constants.BaseHPrintStyle + For border_style, + + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (Iterable[str]): Choose own style icons, they must be 1 character long + - (constants.BorderStyle): `ANSIBorderStyle`, `ASCIIBorderStyle`, `ConstBorderStyle`, `ConstBoldBorderStyle`, + `RoundedBorderStyle`, `DoubleBorderStyle` style or inherit from constants.BorderStyle + Examples: **Printing tree** @@ -681,29 +695,28 @@ def vprint_tree( strip: bool = False, **kwargs: Any, ) -> None: - """Print tree in vertical orientation to console, starting from `tree`. - Accepts kwargs for print() function. + """Print tree in vertical orientation to console, starting from `tree`. Accepts kwargs for print() function. - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` + - Able to customise for maximum depth to print, using `max_depth` - Able to hide names of intermediate nodes, using `intermediate_node_name` - Able to select horizontal spacing between nodes, using `spacing` - - Able to customize style, to choose from str, Iterable[str], or inherit from constants.BaseVPrintStyle, using `style` + - Able to customise style, to choose from str, Iterable[str], or inherit from constants.BaseVPrintStyle, using `style` - Able to toggle border, with border style to choose from str, Iterable[str], or inherit from constants.BorderStyle, using `border_style` - Able to have constant width output string or to strip the trailing spaces, using `strip` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must be 1 character long - (constants.BaseVPrintStyle): `ANSIVPrintStyle`, `ASCIIVPrintStyle`, `ConstVPrintStyle`, `ConstBoldVPrintStyle`, `RoundedVPrintStyle`, `DoubleVPrintStyle` style or inherit from constants.BaseVPrintStyle For border_style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must be 1 character long - (constants.BorderStyle): `ANSIBorderStyle`, `ASCIIBorderStyle`, `ConstBorderStyle`, `ConstBoldBorderStyle`, `RoundedBorderStyle`, `DoubleBorderStyle` style or inherit from constants.BorderStyle @@ -904,24 +917,24 @@ def vyield_tree( - Able to have alias for node name if alias attribute is present, else it falls back to node_name, using `alias` - Able to select which node to print from, resulting in a subtree, using `node_name_or_path` - - Able to customize for maximum depth to print, using `max_depth` + - Able to customise for maximum depth to print, using `max_depth` - Able to hide names of intermediate nodes, using `intermediate_node_name` - Able to select horizontal spacing between nodes, using `spacing` - - Able to customize style, to choose from str, Iterable[str], or inherit from constants.BaseVPrintStyle, using `style` + - Able to customise style, to choose from str, Iterable[str], or inherit from constants.BaseVPrintStyle, using `style` - Able to toggle border, with border style to choose from str, Iterable[str], or inherit from constants.BorderStyle, using `border_style` - Able to have constant width output string or to strip the trailing spaces, using `strip` For style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must be 1 character long - (constants.BaseVPrintStyle): `ANSIVPrintStyle`, `ASCIIVPrintStyle`, `ConstVPrintStyle`, `ConstBoldVPrintStyle`, `RoundedVPrintStyle`, `DoubleVPrintStyle` style or inherit from constants.BaseVPrintStyle For border_style, - - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style + - (str): `ansi`, `ascii`, `const` (default), `const_bold`, `rounded`, `double` style - (Iterable[str]): Choose own style icons, they must be 1 character long - (constants.BorderStyle): `ANSIBorderStyle`, `ASCIIBorderStyle`, `ConstBorderStyle`, `ConstBoldBorderStyle`, `RoundedBorderStyle`, `DoubleBorderStyle` style or inherit from constants.BorderStyle @@ -1114,17 +1127,19 @@ def tree_to_newick( ) -> str: """Export tree to Newick notation. Useful for describing phylogenetic tree. - In the Newick Notation (or New Hampshire Notation), - - Tree is represented in round brackets i.e., `(child1,child2,child3)parent`. - - If there are nested trees, they will be in nested round brackets i.e., `((grandchild1)child1,(grandchild2,grandchild3)child2)parent`. - - If there is length attribute, they will be beside the name i.e., `(child1:0.5,child2:0.1)parent`. - - If there are other attributes, attributes are represented in square brackets i.e., `(child1:0.5[S:human],child2:0.1[S:human])parent[S:parent]`. + In the Newick Notation (or New Hampshire Notation) + + - Tree is represented in round brackets i.e., `(child1,child2,child3)parent` + - If there are nested trees, they will be in nested round brackets i.e., `((grandchild1)child1,(grandchild2,grandchild3)child2)parent` + - If there is length attribute, they will be beside the name i.e., `(child1:0.5,child2:0.1)parent` + - If there are other attributes, attributes are represented in square brackets i.e., `(child1:0.5[S:human],child2:0.1[S:human])parent[S:parent]` + + Customisations include - Customizations include: - - Omitting names of root and intermediate nodes, default all node names are shown. - - Changing length separator to another symbol, default is `:`. - - Adding an attribute prefix, default is `&&NHX:`. - - Changing the attribute separator to another symbol, default is `:`. + - Omitting names of root and intermediate nodes, default all node names are shown + - Changing length separator to another symbol, default is `:` + - Adding an attribute prefix, default is `&&NHX:` + - Changing the attribute separator to another symbol, default is `:` Examples: >>> from bigtree import Node, tree_to_newick @@ -1169,13 +1184,13 @@ def tree_to_newick( attr_sep = attr_sep.value def _serialize(item: Any) -> Any: - """Serialize item if it contains special Newick characters. + """Serialise item if it contains special Newick characters. Args: - item (Any): item to serialize + item: item to serialise Returns: - (Any) + Serialised item """ if isinstance(item, str) and set(item).intersection( constants.NewickCharacter.values() diff --git a/bigtree/tree/helper.py b/bigtree/tree/helper.py index 03478320..8585c075 100644 --- a/bigtree/tree/helper.py +++ b/bigtree/tree/helper.py @@ -24,8 +24,7 @@ def clone_tree(tree: basenode.BaseNode, node_type: Type[BaseNodeT]) -> BaseNodeT: - """Clone tree to another ``Node`` type. - If the same type is needed, simply do a tree.copy(). + """Clone tree to another ``Node`` type. If the same type is needed, simply do a tree.copy(). Examples: >>> from bigtree import BaseNode, Node, clone_tree @@ -39,7 +38,7 @@ def clone_tree(tree: basenode.BaseNode, node_type: Type[BaseNodeT]) -> BaseNodeT node_type: type of cloned tree Returns: - Cloned tree + Cloned tree of another Node type """ assertions.assert_tree_type(tree, basenode.BaseNode, "BaseNode") @@ -72,7 +71,8 @@ def get_subtree( node_name_or_path: str = "", max_depth: int = 0, ) -> NodeT: - """Get subtree based on node name or node path, and/or maximum depth of tree. + """Get subtree based on node name or node path, and/or maximum depth of tree. Subtrees are smaller trees with + different root. Returns a copy of the tree; does not affect original tree. Examples: >>> from bigtree import Node, get_subtree @@ -130,22 +130,23 @@ def prune_tree( sep: str = "/", max_depth: int = 0, ) -> Union[BinaryNodeT, NodeT]: - """Prune tree by path or depth, returns the root of a *copy* of the original tree. + """Prune tree by path or depth. Pruned trees are smaller trees with same root. Returns a copy of the tree; does not + affect original tree. For pruning by `prune_path`, - - All siblings along the prune path will be removed. All descendants will be kept by default. - - If ``exact=True``, all descendants of prune path will be removed. - - Prune path can be string (only one path) or a list of strings (multiple paths). - - Prune path name should be unique, can be full path, partial path (trailing part of path), or node name. + - All siblings along the prune path will be removed. All descendants will be kept by default + - If ``exact=True``, all descendants of prune path will be removed + - Prune path can be string (only one path) or a list of strings (multiple paths) + - Prune path name should be unique, can be full path, partial path (trailing part of path), or node name For pruning by `max_depth`, - - All nodes that are beyond `max_depth` will be removed. + - All nodes that are beyond `max_depth` will be removed Path should contain ``Node`` name, separated by `sep`. - - For example: Path string "a/b" refers to Node("b") with parent Node("a"). + - For example: Path string "a/b" refers to Node("b") with parent Node("a") Examples: >>> from bigtree import Node, prune_tree @@ -444,9 +445,9 @@ def get_tree_diff( # Comparing tree structure - - (+) and (-) will be added to node name relative to `tree`. - - For example: (+) refers to nodes that are in `other_tree` but not `tree`. - - For example: (-) refers to nodes that are in `tree` but not `other_tree`. + - (+) and (-) will be added to node name relative to `tree` + - For example: (+) refers to nodes that are in `other_tree` but not `tree` + - For example: (-) refers to nodes that are in `tree` but not `other_tree` If `detail=True`, (added) and (moved to) will be used instead of (+), (removed) and (moved from) will be used instead of (-). @@ -550,7 +551,7 @@ def get_tree_diff( # Comparing tree attribute - - (~) will be added to node name if there are differences in tree attributes defined in `attr_list`. + - (~) will be added to node name if there are differences in tree attributes defined in `attr_list` - The node's attributes will be a list of [value in `tree`, value in `other_tree`] >>> # Create original tree diff --git a/bigtree/tree/modify.py b/bigtree/tree/modify.py index f9e8f269..68842619 100644 --- a/bigtree/tree/modify.py +++ b/bigtree/tree/modify.py @@ -36,45 +36,45 @@ def shift_nodes( """Shift nodes from `from_paths` to `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable). - - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden). - - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge). - - Able to merge children and remove intermediate parent node, defaults to False (nodes are shifted; not merged). + - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) + - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden) + - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge) + - Able to merge children and remove intermediate parent node, defaults to False (nodes are shifted; not merged) - Able to merge leaf nodes and remove all intermediate nodes, defaults to False (nodes are shifted; not merged) - - Able to shift node only and delete children, defaults to False (nodes are shifted together with children). + - Able to shift node only and delete children, defaults to False (nodes are shifted together with children) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. - - Can set to empty string or None to delete the path in `from_paths`, note that ``copy`` must be set to False. + - Path name must be full path + - Can set to empty string or None to delete the path in `from_paths`, note that ``copy`` must be set to False If ``merge_children=True``, - - If `to_path` is not present, it shifts children of `from_path`. - - If `to_path` is present, and ``overriding=False``, original and new children are merged. - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained. + - If `to_path` is not present, it shifts children of `from_path` + - If `to_path` is present, and ``overriding=False``, original and new children are merged + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained If ``merge_leaves=True``, - - If `to_path` is not present, it shifts leaves of `from_path`. - - If `to_path` is present, and ``overriding=False``, original children and leaves are merged. + - If `to_path` is not present, it shifts leaves of `from_path` + - If `to_path` is present, and ``overriding=False``, original children and leaves are merged - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained, - original non-leaf nodes in `from_path` are retained. + original child nodes in `from_path` are retained Note: - - `merge_children` and `merge_leaves` cannot be both True at the same time. - - `overriding` and `merge_attribute` cannot be both True at the same time. + - `merge_children` and `merge_leaves` cannot be both True at the same time + - `overriding` and `merge_attribute` cannot be both True at the same time Examples: >>> from bigtree import list_to_tree, str_to_tree, shift_nodes @@ -136,8 +136,8 @@ def shift_nodes( In ``merge_children=True`` case, child nodes are shifted instead of the parent node. - - If the path already exists, child nodes are merged with existing children. - - Otherwise, the child nodes of the node are merged with the node's parent. + - If the path already exists, child nodes are merged with existing children + - Otherwise, the child nodes of the node are merged with the node's parent >>> root = str_to_tree( ... "Downloads\\n" @@ -183,8 +183,8 @@ def shift_nodes( In ``merge_leaves=True`` case, leaf nodes are shifted instead of the parent node. - - If the path already exists, leaf nodes are merged with existing children. - - Otherwise, the leaf nodes of the node are merged with the node's parent. + - If the path already exists, leaf nodes are merged with existing children + - Otherwise, the leaf nodes of the node are merged with the node's parent >>> root = str_to_tree( ... "Downloads\\n" @@ -303,43 +303,44 @@ def copy_nodes( """Copy nodes from `from_paths` to `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable). - - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden). - - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge). - - Able to merge children and remove intermediate parent node, defaults to False (nodes are copied; not merged). + - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) + - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden) + - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge) + - Able to merge children and remove intermediate parent node, defaults to False (nodes are copied; not merged) - Able to merge only leaf nodes and remove all intermediate nodes, defaults to False (nodes are copied; not merged) - - Able to copy node only and delete children, defaults to False (nodes are copied together with children). + - Able to copy node only and delete children, defaults to False (nodes are copied together with children) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. + - Path name must be full path If ``merge_children=True``, - - If `to_path` is not present, it copies children of `from_path`. - - If `to_path` is present, and ``overriding=False``, original and new children are merged. - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained. + - If `to_path` is not present, it copies children of `from_path` + - If `to_path` is present, and ``overriding=False``, original and new children are merged + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained If ``merge_leaves=True``, - - If `to_path` is not present, it copies leaves of `from_path`. - - If `to_path` is present, and ``overriding=False``, original children and leaves are merged. - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained. + - If `to_path` is not present, it copies leaves of `from_path` + - If `to_path` is present, and ``overriding=False``, original children and leaves are merged + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained, + original child nodes in `from_path` are retained Note: - - `merge_children` and `merge_leaves` cannot be both True at the same time. - - `overriding` and `merge_attribute` cannot be both True at the same time. + - `merge_children` and `merge_leaves` cannot be both True at the same time + - `overriding` and `merge_attribute` cannot be both True at the same time Examples: >>> from bigtree import list_to_tree, str_to_tree, copy_nodes @@ -393,8 +394,8 @@ def copy_nodes( In ``merge_children=True`` case, child nodes are copied instead of the parent node. - - If the path already exists, child nodes are merged with existing children. - - Otherwise, the child nodes of the node are merged with the node's parent. + - If the path already exists, child nodes are merged with existing children + - Otherwise, the child nodes of the node are merged with the node's parent >>> root = str_to_tree( ... "Downloads\\n" @@ -444,8 +445,8 @@ def copy_nodes( In ``merge_leaves=True`` case, leaf nodes are copied instead of the parent node. - - If the path already exists, leaf nodes are merged with existing children. - - Otherwise, the leaf nodes of the node are merged with the node's parent. + - If the path already exists, leaf nodes are merged with existing children + - Otherwise, the leaf nodes of the node are merged with the node's parent >>> root = str_to_tree( ... "Downloads\\n" @@ -565,23 +566,23 @@ def shift_and_replace_nodes( """Shift nodes from `from_paths` to *replace* `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable). - - Able to shift node only and delete children, defaults to False (nodes are shifted together with children). + - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) + - Able to shift node only and delete children, defaults to False (nodes are shifted together with children) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. - - Path must exist, node-to-be-replaced must be present. + - Path name must be full path + - Path must exist, node-to-be-replaced must be present Examples: >>> from bigtree import str_to_tree, shift_and_replace_nodes @@ -667,43 +668,44 @@ def copy_nodes_from_tree_to_tree( """Copy nodes from `from_paths` to `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable). - - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden). - - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge). - - Able to merge children and remove intermediate parent node, defaults to False (nodes are shifted; not merged). - - Able to merge only leaf nodes and remove all intermediate nodes, defaults to False (nodes are shifted; not merged) - - Able to copy node only and delete children, defaults to False (nodes are copied together with children). + - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) + - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden) + - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge) + - Able to merge children and remove intermediate parent node, defaults to False (nodes are shifted; not merged) + - Able to merge leaf nodes and remove all intermediate nodes, defaults to False (nodes are shifted; not merged) + - Able to copy node only and delete children, defaults to False (nodes are copied together with children) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. + - Path name must be full path If ``merge_children=True``, - If `to_path` is not present, it copies children of `from_path` - If `to_path` is present, and ``overriding=False``, original and new children are merged - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained. + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained If ``merge_leaves=True``, - - If `to_path` is not present, it copies leaves of `from_path`. - - If `to_path` is present, and ``overriding=False``, original children and leaves are merged. - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained. + - If `to_path` is not present, it copies leaves of `from_path` + - If `to_path` is present, and ``overriding=False``, original children and leaves are merged + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained, + original child nodes in `from_path` are retained Note: - - `merge_children` and `merge_leaves` cannot be both True at the same time. - - `overriding` and `merge_attribute` cannot be both True at the same time. + - `merge_children` and `merge_leaves` cannot be both True at the same time + - `overriding` and `merge_attribute` cannot be both True at the same time Examples: >>> from bigtree import Node, str_to_tree, copy_nodes_from_tree_to_tree @@ -770,8 +772,8 @@ def copy_nodes_from_tree_to_tree( In ``merge_children=True`` case, child nodes are copied instead of the parent node. - - If the path already exists, child nodes are merged with existing children. - - Otherwise, the child nodes of the node are merged with the node's parent. + - If the path already exists, child nodes are merged with existing children + - Otherwise, the child nodes of the node are merged with the node's parent >>> root_other = str_to_tree( ... "Documents\\n" @@ -800,8 +802,8 @@ def copy_nodes_from_tree_to_tree( In ``merge_leaves=True`` case, leaf nodes are copied instead of the parent node. - - If the path already exists, leaf nodes are merged with existing children. - - Otherwise, the leaf nodes of the node are merged with the node's parent. + - If the path already exists, leaf nodes are merged with existing children + - Otherwise, the leaf nodes of the node are merged with the node's parent >>> root_other = str_to_tree( ... "Documents\\n" @@ -889,23 +891,23 @@ def copy_and_replace_nodes_from_tree_to_tree( """Copy nodes from `from_paths` to *replace* `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable). - - Able to copy node only and delete children, defaults to False (nodes are copied together with children). + - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) + - Able to copy node only and delete children, defaults to False (nodes are copied together with children) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. - - Path must exist, node-to-be-replaced must be present. + - Path name must be full path + - Path must exist, node-to-be-replaced must be present Examples: >>> from bigtree import str_to_tree, copy_and_replace_nodes_from_tree_to_tree @@ -1077,47 +1079,47 @@ def copy_or_shift_logic( """Shift or copy nodes from `from_paths` to `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to copy node, defaults to False (nodes are shifted; not copied). + - Able to copy node, defaults to False (nodes are shifted; not copied) - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) - Able to override existing node if it exists, defaults to False (to-nodes must not exist; not overridden) - - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge). + - Able to merge attributes of node if it exists, defaults to False (to-nodes must not exist; no attributes to merge) - Able to merge children and remove intermediate parent node, defaults to False (nodes are shifted; not merged) - Able to merge only leaf nodes and remove all intermediate nodes, defaults to False (nodes are shifted; not merged) - - Able to shift/copy node only and delete children, defaults to False (nodes are shifted/copied together with children). + - Able to shift/copy node only and delete children, defaults to False (nodes are shifted/copied together with children) - Able to shift/copy nodes from one tree to another tree, defaults to None (shifting/copying happens within same tree) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. - - Can set to empty string or None to delete the path in `from_paths`, note that ``copy`` must be set to False. + - Path name must be full path + - Can set to empty string or None to delete the path in `from_paths`, note that ``copy`` must be set to False If ``merge_children=True``, - - If `to_path` is not present, it shifts/copies children of `from_path`. - - If `to_path` is present, and ``overriding=False``, original and new children are merged. - - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained. + - If `to_path` is not present, it shifts/copies children of `from_path` + - If `to_path` is present, and ``overriding=False``, original and new children are merged + - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new children are retained If ``merge_leaves=True``, - - If `to_path` is not present, it shifts/copies leaves of `from_path`. - - If `to_path` is present, and ``overriding=False``, original children and leaves are merged. + - If `to_path` is not present, it shifts/copies leaves of `from_path` + - If `to_path` is present, and ``overriding=False``, original children and leaves are merged - If `to_path` is present and ``overriding=True``, it behaves like overriding and only new leaves are retained, - original non-leaf nodes in `from_path` are retained. + original child nodes in `from_path` are retained Note: - - `merge_children` and `merge_leaves` cannot be both True at the same time. - - `overriding` and `merge_attribute` cannot be both True at the same time. + - `merge_children` and `merge_leaves` cannot be both True at the same time + - `overriding` and `merge_attribute` cannot be both True at the same time Args: tree: tree to modify @@ -1365,25 +1367,25 @@ def replace_logic( """Shift or copy nodes from `from_paths` to *replace* `to_paths` *in-place*. - Creates intermediate nodes if to-path is not present - - Able to copy node, defaults to False (nodes are shifted; not copied). + - Able to copy node, defaults to False (nodes are shifted; not copied) - Able to skip nodes if from-path is not found, defaults to False (from-nodes must be found; not skippable) - - Able to replace node only and delete children, defaults to False (nodes are shifted/copied together with children). + - Able to replace node only and delete children, defaults to False (nodes are shifted/copied together with children) - Able to shift/copy nodes from one tree to another tree, defaults to None (shifting/copying happens within same tree) For paths in `from_paths` and `to_paths`, - - Path name can be with or without leading tree path separator symbol. + - Path name can be with or without leading tree path separator symbol For paths in `from_paths`, - - Path name can be partial path (trailing part of path) or node name. - - If ``with_full_path=True``, path name must be full path. - - Path name must be unique to one node. + - Path name can be partial path (trailing part of path) or node name + - If ``with_full_path=True``, path name must be full path + - Path name must be unique to one node For paths in `to_paths`, - - Path name must be full path. - - Path must exist, node-to-be-replaced must be present. + - Path name must be full path + - Path must exist, node-to-be-replaced must be present Args: tree: tree to modify @@ -1423,9 +1425,8 @@ def replace_logic( ] ): raise ValueError( - "Invalid path in `from_paths` not starting with the root node. " - "Check your `from_paths` parameter, alternatively set `with_full_path=False` to shift " - "partial path instead of full path." + "Invalid path in `from_paths` not starting with the root node. Check your `from_paths` parameter, " + "alternatively set `with_full_path=False` to shift partial path instead of full path." ) if not all( [ @@ -1449,8 +1450,7 @@ def replace_logic( if not from_node: if not skippable: raise exceptions.NotFoundError( - f"Unable to find from_path {from_path}\n" - f"Set `skippable` to True to skip shifting for nodes not found" + f"Unable to find from_path {from_path}\nSet `skippable` to True to skip shifting for nodes not found" ) else: logging.info(f"Unable to find from_path {from_path}") @@ -1463,8 +1463,8 @@ def replace_logic( if to_node: if from_node == to_node: raise exceptions.TreeError( - f"Attempting to replace the same node {from_node.node_name}\n" - f"Check from path {from_path} and to path {to_path}" + f"Attempting to replace the same node {from_node.node_name}\nCheck from path {from_path} and to " + f"path {to_path}" ) # To node not found diff --git a/bigtree/tree/search.py b/bigtree/tree/search.py index c176f28b..1c9cf66c 100644 --- a/bigtree/tree/search.py +++ b/bigtree/tree/search.py @@ -29,7 +29,7 @@ def __check_result_count( result: Tuple[Any, ...], min_count: int, max_count: int ) -> None: - """Check result fulfil min_count and max_count requirements + """Check result fulfil min_count and max_count requirements. Args: result: result of search @@ -173,7 +173,7 @@ def find_relative_path(tree: NodeT, path_name: str) -> NodeT: - Supports unix folder expression for relative path, i.e., '../../node_name' - Supports wildcards, i.e., '\*/node_name' - - If path name starts with leading separator symbol, it will start at root node. + - If path name starts with leading separator symbol, it will start at root node Examples: >>> from bigtree import Node, find_relative_path @@ -215,7 +215,7 @@ def find_relative_paths( - Supports unix folder expression for relative path, i.e., '../../node_name' - Supports wildcards, i.e., '\*/node_name' - - If path name starts with leading separator symbol, it will start at root node. + - If path name starts with leading separator symbol, it will start at root node Examples: >>> from bigtree import Node, find_relative_paths @@ -258,7 +258,7 @@ def find_relative_paths( resolved_nodes: List[NodeT] = [] def resolve(_node: NodeT, path_idx: int) -> None: - """Resolve node based on path name + """Resolve node based on path name. Args: _node: current node @@ -299,8 +299,8 @@ def find_full_path(tree: NodeT, path_name: str) -> NodeT: """ Search tree for a single node matching path attribute. - - Path name can be with or without leading tree path separator symbol. - - Path name must be full path, works similar to `find_path` but faster. + - Path name can be with or without leading tree path separator symbol + - Path name must be full path, works similar to `find_path` but faster Examples: >>> from bigtree import Node, find_full_path @@ -338,8 +338,8 @@ def find_path(tree: NodeT, path_name: str) -> NodeT: """ Search tree for a single node matching path attribute. - - Path name can be with or without leading tree path separator symbol. - - Path name can be full path or partial path (trailing part of path) or node name. + - Path name can be with or without leading tree path separator symbol + - Path name can be full path or partial path (trailing part of path) or node name Examples: >>> from bigtree import Node, find_path @@ -367,8 +367,8 @@ def find_paths(tree: NodeT, path_name: str) -> Iterable[NodeT]: """ Search tree for one or more nodes matching path attribute. - - Path name can be with or without leading tree path separator symbol. - - Path name can be partial path (trailing part of path) or node name. + - Path name can be with or without leading tree path separator symbol + - Path name can be partial path (trailing part of path) or node name Examples: >>> from bigtree import Node, find_paths diff --git a/bigtree/utils/assertions.py b/bigtree/utils/assertions.py index e4565c95..8cc1fdb5 100644 --- a/bigtree/utils/assertions.py +++ b/bigtree/utils/assertions.py @@ -76,7 +76,7 @@ def assert_not_reserved_keywords( parameter_dict_or_df: Union[Dict[str, Any], pd.DataFrame], reserved_keywords: List[str], ) -> None: - """Raise ValueError is parameter is in key of dictionary + """Raise ValueError is parameter is in key of dictionary. Args: parameter_dict_or_df (Dict[str, Any]/pd.DataFrame): argument input for parameter diff --git a/bigtree/utils/exceptions.py b/bigtree/utils/exceptions.py index 955b7254..0ee54336 100644 --- a/bigtree/utils/exceptions.py +++ b/bigtree/utils/exceptions.py @@ -46,9 +46,8 @@ def deprecated( ) -> Callable[[Callable[..., T]], Callable[..., T]]: # pragma: no cover def decorator(func: Callable[..., T]) -> Callable[..., T]: """ - This is a decorator which can be used to mark functions as deprecated. - It will raise a DeprecationWarning when the function is used. - Source: https://stackoverflow.com/a/30253848 + This is a decorator which can be used to mark functions as deprecated. It will raise a DeprecationWarning when + the function is used. Source: https://stackoverflow.com/a/30253848 """ @wraps(func) @@ -74,8 +73,8 @@ def optional_dependencies_pandas( func: Callable[..., T], ) -> Callable[..., T]: # pragma: no cover """ - This is a decorator which can be used to import optional pandas dependency. - It will raise a ImportError if the module is not found. + This is a decorator which can be used to import optional pandas dependency. It will raise an ImportError if the + module is not found. """ @wraps(func) @@ -96,8 +95,8 @@ def optional_dependencies_polars( func: Callable[..., T], ) -> Callable[..., T]: # pragma: no cover """ - This is a decorator which can be used to import optional polars dependency. - It will raise a ImportError if the module is not found. + This is a decorator which can be used to import optional polars dependency. It will raise an ImportError if the + module is not found. """ @wraps(func) @@ -118,8 +117,8 @@ def optional_dependencies_matplotlib( func: Callable[..., T], ) -> Callable[..., T]: # pragma: no cover """ - This is a decorator which can be used to import optional matplotlib dependency. - It will raise a ImportError if the module is not found. + This is a decorator which can be used to import optional matplotlib dependency. It will raise an ImportError if the + module is not found. """ @wraps(func) @@ -141,8 +140,8 @@ def optional_dependencies_image( ) -> Callable[[Callable[..., T]], Callable[..., T]]: def decorator(func: Callable[..., T]) -> Callable[..., T]: """ - This is a decorator which can be used to import optional image dependency. - It will raise a ImportError if the module is not found. + This is a decorator which can be used to import optional image dependency. It will raise an ImportError if the + module is not found. """ @wraps(func) diff --git a/bigtree/utils/iterators.py b/bigtree/utils/iterators.py index 13d712ff..9bbd8a63 100644 --- a/bigtree/utils/iterators.py +++ b/bigtree/utils/iterators.py @@ -38,10 +38,10 @@ def inorder_iter( ) -> Iterable[BinaryNodeT]: """Iterate through all children of a tree. - In-Order Iteration Algorithm, LNR - 1. Recursively traverse the current node's left subtree. - 2. Visit the current node. - 3. Recursively traverse the current node's right subtree. + In-Order Iteration Algorithm LNR: + 1. Recursively traverse the current node's left subtree + 2. Visit the current node + 3. Recursively traverse the current node's right subtree Examples: >>> from bigtree import BinaryNode, list_to_binarytree, inorder_iter @@ -89,10 +89,10 @@ def preorder_iter( ) -> Iterable[T]: """Iterate through all children of a tree. - Pre-Order Iteration Algorithm, NLR - 1. Visit the current node. - 2. Recursively traverse the current node's left subtree. - 3. Recursively traverse the current node's right subtree. + Pre-Order Iteration Algorithm NLR: + 1. Visit the current node + 2. Recursively traverse the current node's left subtree + 3. Recursively traverse the current node's right subtree It is topologically sorted because a parent node is processed before its child nodes. @@ -150,10 +150,10 @@ def postorder_iter( ) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. - Post-Order Iteration Algorithm, LRN - 1. Recursively traverse the current node's left subtree. - 2. Recursively traverse the current node's right subtree. - 3. Visit the current node. + Post-Order Iteration Algorithm LRN: + 1. Recursively traverse the current node's left subtree + 2. Recursively traverse the current node's right subtree + 3. Visit the current node Examples: >>> from bigtree import Node, list_to_tree, postorder_iter @@ -211,8 +211,8 @@ def levelorder_iter( ) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. - Level-Order Iteration Algorithm - 1. Recursively traverse the nodes on same level. + Level-Order Iteration Algorithm: + 1. Recursively traverse the nodes on same level Examples: >>> from bigtree import Node, list_to_tree, levelorder_iter @@ -254,10 +254,10 @@ def _levelorder_iter(trees: List[BaseNodeT]) -> Iterable[BaseNodeT]: """Iterate through all children of a tree. Args: - trees (List[BaseNode]): trees to get children for next level + trees: trees to get children for next level Returns: - (Iterable[BaseNode]) + Iterable of nodes """ next_level = [] for _tree in trees: @@ -282,8 +282,8 @@ def levelordergroup_iter( ) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. - Level-Order Group Iteration Algorithm - 1. Recursively traverse the nodes on same level, returns nodes level by level in a nested list. + Level-Order Group Iteration Algorithm: + 1. Recursively traverse the nodes on same level, returns nodes level by level in a nested list Examples: >>> from bigtree import Node, list_to_tree, levelordergroup_iter @@ -352,10 +352,10 @@ def zigzag_iter( stop_condition: Optional[Callable[[BaseNodeT], bool]] = None, max_depth: int = 0, ) -> Iterable[BaseNodeT]: - """Iterate through all children of a tree. + """ "Iterate through all children of a tree. - ZigZag Iteration Algorithm - 1. Recursively traverse the nodes on same level, in a zigzag manner across different levels. + ZigZag Iteration Algorithm: + 1. Recursively traverse the nodes on same level, in a zigzag manner across different levels Examples: >>> from bigtree import Node, list_to_tree, zigzag_iter @@ -399,11 +399,11 @@ def _zigzag_iter( """Iterate through all children of a tree. Args: - trees (List[BaseNode]): trees to get children for next level - reverse_indicator (bool): indicator whether it is in reverse order + trees: trees to get children for next level + reverse_indicator: indicator whether it is in reverse order Returns: - (Iterable[BaseNode]) + Iterable of nodes """ next_level = [] for _tree in trees: @@ -433,9 +433,9 @@ def zigzaggroup_iter( ) -> Iterable[Iterable[BaseNodeT]]: """Iterate through all children of a tree. - ZigZag Group Iteration Algorithm - 1. Recursively traverse the nodes on same level, in a zigzag manner across different levels, - returns nodes level by level in a nested list. + ZigZag Group Iteration Algorithm: + 1. Recursively traverse the nodes on same level, in a zigzag manner across different levels, returns nodes level + by level in a nested list Examples: >>> from bigtree import Node, list_to_tree, zigzaggroup_iter @@ -507,12 +507,13 @@ def _zigzaggroup_iter( def dag_iterator(dag: DAGNodeT) -> Iterable[Tuple[DAGNodeT, DAGNodeT]]: - """Iterate through all nodes of a Directed Acyclic Graph (DAG). Note that node names - must be unique. Note that DAG must at least have two nodes to be shown on graph. + """Iterate through all nodes of a Directed Acyclic Graph (DAG). Note that node names must be unique. Note that DAG + must at least have two nodes to be shown on graph. - 1. Visit the current node. - 2. Recursively traverse the current node's parents. - 3. Recursively traverse the current node's children. + DAG Iteration: + 1. Visit the current node + 2. Recursively traverse the current node's parents + 3. Recursively traverse the current node's children Examples: >>> from bigtree import DAGNode, dag_iterator diff --git a/bigtree/utils/plot.py b/bigtree/utils/plot.py index 8e1c228f..7d241e1e 100644 --- a/bigtree/utils/plot.py +++ b/bigtree/utils/plot.py @@ -29,17 +29,19 @@ def reingold_tilford( reverse: bool = False, ) -> None: """ - Algorithm for drawing tree structure, retrieves `(x, y)` coordinates for a tree structure. - Adds `x` and `y` attributes to every node in the tree. Modifies tree in-place. + Algorithm for drawing tree structure, retrieves `(x, y)` coordinates for a tree structure. Adds `x` and `y` + attributes to every node in the tree. Modifies tree in-place. This algorithm[1] is an improvement over Reingold Tilford algorithm[2]. According to Reingold Tilford's paper, a tree diagram should satisfy the following aesthetic rules, - 1. Nodes at the same depth should lie along a straight line, and the straight lines defining the depths should be parallel. - 2. A left child should be positioned to the left of its parent node and a right child to the right. - 3. A parent should be centered over their children. - 4. A tree and its mirror image should produce drawings that are reflections of one another; a subtree should be drawn the same way regardless of where it occurs in the tree. + 1. Nodes at the same depth should lie along a straight line, and the straight lines defining the depths should be + parallel + 2. A left child should be positioned to the left of its parent node and a right child to the right + 3. A parent should be centered over their children + 4. A tree and its mirror image should produce drawings that are reflections of one another; a subtree should be + drawn the same way regardless of where it occurs in the tree Examples: >>> from bigtree import reingold_tilford, list_to_tree @@ -91,8 +93,8 @@ def reingold_tilford( def plot_tree( tree_node: T, *args: Any, ax: Optional[plt.Axes] = None, **kwargs: Any ) -> plt.Figure: - """Plot tree in line form. Tree should have `x` and `y` attribute from Reingold Tilford. - Accepts existing matplotlib Axes. Accepts args and kwargs for matplotlib.pyplot.plot() function. + """Plot tree in line form. Tree should have `x` and `y` attribute from Reingold Tilford. Accepts existing + matplotlib Axes. Accepts args and kwargs for matplotlib.pyplot.plot() function. Examples: >>> import matplotlib.pyplot as plt @@ -134,8 +136,7 @@ def _first_pass( tree_node: T, sibling_separation: float, subtree_separation: float ) -> None: """ - Performs post-order traversal of tree and assigns `x`, `mod` and `shift` values to each node. - Modifies tree in-place. + Performs post-order traversal of tree and assigns `x`, `mod` and `shift` values to each node. Modifies tree in-place. Notation: - `lsibling`: left-sibling of node @@ -153,11 +154,13 @@ def _first_pass( - :math:`x = 0` for leftmost node and :math:`x = lsibling.x + sibling distance` for other nodes - Special case when leftmost node has children, then it will try to center itself, :math:`x = midpoint` - `mod` value is the amount to shift the subtree (all descendant nodes excluding itself) to make the children centered with itself - - :math:`mod = 0` for node does not have children (no need to shift subtree) or it is a leftmost node (parent is already centered, from above point) + `mod` value is the amount to shift the subtree (all descendant nodes excluding itself) to make the children + centered with itself + - :math:`mod = 0` for node does not have children (no need to shift subtree) or it is a leftmost node (parent + is already centered, from above point) - Special case when non-leftmost nodes have children, :math:`mod = x - midpoint` - 2. In the second part, we assign `shift` value of nodes due to overlapping subtrees. + 2. In the second part, we assign `shift` value of nodes due to overlapping subtrees For each node on the same level, ensure that the leftmost descendant does not intersect with the rightmost descendant of any left sibling at every subsequent level. Intersection happens when the subtrees are not @@ -343,8 +346,7 @@ def _second_pass( x_adjustment: Optional[float] = 0.0, ) -> float: """ - Performs pre-order traversal of tree and determines the final `x` and `y` values for each node. - Modifies tree in-place. + Performs pre-order traversal of tree and determines the final `x` and `y` values for each node. Modifies tree in-place. Notation: - `depth`: maximum depth of tree @@ -402,8 +404,8 @@ def _second_pass( def _third_pass(tree_node: basenode.BaseNode, x_adjustment: float) -> None: - """Adjust all x-coordinates by an adjustment value so that every x-coordinate is greater than or equal to 0. - Modifies tree in-place. + """Adjust all x-coordinates by an adjustment value so that every x-coordinate is greater than or equal to 0. Modifies + tree in-place. Args: tree_node: tree to compute (x, y) coordinate diff --git a/bigtree/workflows/app_todo.py b/bigtree/workflows/app_todo.py index a3eec3c9..739cf89e 100644 --- a/bigtree/workflows/app_todo.py +++ b/bigtree/workflows/app_todo.py @@ -13,8 +13,8 @@ class AppToDo: """ To-Do List Implementation with Big Tree. - - To-Do List has three levels - app name, list name, and item name. - - If list name is not given, item will be assigned to a `General` list. + - To-Do List has three levels - app name, list name, and item name + - If list name is not given, item will be assigned to a `General` list Examples: # *Initializing and Adding Items* diff --git a/docs/_static/playground.py b/docs/_static/playground.py index 6d5de393..19cfcd05 100644 --- a/docs/_static/playground.py +++ b/docs/_static/playground.py @@ -376,7 +376,7 @@ def execute(cmd, no_except=True, inline=False, init="", g=None): console = "" - # Setup global initialization + # Setup global initialisation if g is None: g = { "Ramp": Ramp, diff --git a/docs/bigtree/dag/construct.md b/docs/bigtree/dag/construct.md index 25fbdd69..2a5f2655 100644 --- a/docs/bigtree/dag/construct.md +++ b/docs/bigtree/dag/construct.md @@ -14,10 +14,6 @@ Construct Directed Acyclic Graph (DAG) from list, dictionary, and pandas DataFra | Dictionary | `dict_to_dag` | Yes | | DataFrame | `dataframe_to_dag` | Yes | -These functions are not standalone functions. Under the hood, they have the following dependency, - -![DAG Constructor Dependency Diagram](https://github.com/kayjan/bigtree/raw/master/assets/docs/dag_construct.png "DAG Constructor Dependency Diagram") - ----- ::: bigtree.dag.construct diff --git a/docs/bigtree/tree/export.md b/docs/bigtree/tree/export.md index a199db21..f4b66d10 100644 --- a/docs/bigtree/tree/export.md +++ b/docs/bigtree/tree/export.md @@ -20,7 +20,7 @@ Export Tree to list, dictionary, and pandas DataFrame. | Mermaid Markdown (for .md) | `tree_to_mermaid` | -## Tree Export Customizations +## Tree Export Customisations While exporting to another data type, methods can take in arguments to determine what information to extract. diff --git a/docs/bigtree/tree/modify.md b/docs/bigtree/tree/modify.md index c8a67d06..f30d87ed 100644 --- a/docs/bigtree/tree/modify.md +++ b/docs/bigtree/tree/modify.md @@ -6,13 +6,13 @@ title: Tree Modify There are two types of modification available -1. **Non-replacing scenario**: Shift or copy nodes within same tree or between two trees using `from_paths` (list of paths) and `to_paths` (list of paths). -2. **Replacing scenario**: Shift or copy nodes within same tree or between two trees *while replacing the to-node* using `from_paths` (list of paths) and `to_paths` (list of paths). +1. **Non-replacing scenario**: Shift or copy nodes within same tree or between two trees using `from_paths` (list of paths) and `to_paths` (list of paths) +2. **Replacing scenario**: Shift or copy nodes within same tree or between two trees *while replacing the to-node* using `from_paths` (list of paths) and `to_paths` (list of paths) -## Available Configurations for Customization +## Available Configurations for Customisation -In **non-replacing scenario**, there are several configurations available for customization. +In **non-replacing scenario**, there are several configurations available for customisation. | Configuration | Description | Default Value | |-------------------|----------------------------------------------------------------------------------------|-----------------------------------------------------------| @@ -26,8 +26,8 @@ In **non-replacing scenario**, there are several configurations available for cu | `delete_children` | Shift/copy node only and delete its children | False (nodes are shifted/copied together with children) | In **replacing scenario**, all the configurations are also available except `overriding`, `merge_attribute`, -`merge_children`, and `merge_leaves` as it is doing a one-to-one replacement. -It is by default overriding, and there is nothing to merge. +`merge_children`, and `merge_leaves` as it is doing a one-to-one replacement. It is by default overriding, and there is +nothing to merge. !!! note @@ -44,9 +44,9 @@ It is by default overriding, and there is nothing to merge. ## Tree Modification Permutations -There are several ways you can mix and match the tree modification methods. -If you know all the parameters to choose, feel free to use ``copy_or_shift_logic`` or ``replace_logic`` methods as they are the most customizable. -All other methods calls these 2 methods directly. +There are several ways you can mix and match the tree modification methods. If you know all the parameters to choose, +feel free to use ``copy_or_shift_logic`` or ``replace_logic`` methods as they are the most customizable. All other +methods call these 2 methods directly. | Shift / Copy? | Same tree / Between two trees? | Replace destination node? | Method to use | |---------------|--------------------------------|---------------------------|----------------------------------------------| @@ -117,14 +117,14 @@ What about the permutations between the parameters? - `copy`, `skippable`, `delete_children` - These parameters have some interaction: - `overriding` and `merge_attribute` with `merge_children` and `merge_leaves` - - `overriding` + `merge_children`: Behaves like `merge_children` when there is no clash in node name, otherwise behaves like `overriding`. - Note that clashes will preserve origin node parent and destination nodes' children. - - `overriding` + `merge_leaves`: Behaves like `merge_leaves` when there is no clash in node name, otherwise behaves like `overriding`. - Note that clashes will preserve origin node parent and destination nodes' leaves. - - `merge_attribute` + `merge_children`: Behaves like `merge_children` when there is no clash in node name, otherwise behaves like `merge_attribute`. - Note that attributes will be merged for node and all descendants, and will preserve origin and destination nodes' children. - - `merge_attribute` + `merge_leaves`: Behaves like `merge_leaves` when there is no clash in node name, otherwise behaves like `merge_attribute`. - Note that attributes will be merged for node and all descendants, and will preserve origin nodes' children and destination nodes' leaves. + - `overriding` + `merge_children`: Behaves like `merge_children` when there is no clash in node name, otherwise behaves like `overriding` + Note that clashes will preserve origin node parent and destination nodes' children + - `overriding` + `merge_leaves`: Behaves like `merge_leaves` when there is no clash in node name, otherwise behaves like `overriding` + Note that clashes will preserve origin node parent and destination nodes' leaves + - `merge_attribute` + `merge_children`: Behaves like `merge_children` when there is no clash in node name, otherwise behaves like `merge_attribute` + Note that attributes will be merged for node and all descendants, and will preserve origin and destination nodes' children + - `merge_attribute` + `merge_leaves`: Behaves like `merge_leaves` when there is no clash in node name, otherwise behaves like `merge_attribute` + Note that attributes will be merged for node and all descendants, and will preserve origin nodes' children and destination nodes' leaves ----- diff --git a/docs/gettingstarted/demo/tree.md b/docs/gettingstarted/demo/tree.md index 4914dfdd..0ac6393e 100644 --- a/docs/gettingstarted/demo/tree.md +++ b/docs/gettingstarted/demo/tree.md @@ -382,7 +382,7 @@ vprint_tree(root) # (3)! 2. Alternatively, `root.hshow()` can be used 3. Alternatively, `root.vshow()` can be used -Other customizations for printing are also available, such as: +Other customisations for printing are also available, such as: - Printing alias instead of node name, if present - Printing subtree @@ -504,7 +504,7 @@ Alternatively, the `plot_tree` method can be used, but remember to run the `rein first to retrieve the *x* and *y* coordinates. Arguments and keyword arguments can be passed in as long as they are compatible with the `plt.plot()` -function. A *plt.Figure* object is returned if you want to do further customizations such as add title or +function. A *plt.Figure* object is returned if you want to do further customisations such as add title or save the figure to image. ```python hl_lines="9-10" diff --git a/docs_sphinx/source/bigtree/tree/export.rst b/docs_sphinx/source/bigtree/tree/export.rst index 3ff2550b..276c50a4 100644 --- a/docs_sphinx/source/bigtree/tree/export.rst +++ b/docs_sphinx/source/bigtree/tree/export.rst @@ -26,7 +26,7 @@ Export Tree to list, dictionary, and pandas DataFrame. While exporting to another data type, methods can take in arguments to determine what information to extract. -.. list-table:: Tree Export Customizations +.. list-table:: Tree Export Customisations :widths: 40 30 30 30 30 30 :header-rows: 1 diff --git a/docs_sphinx/source/bigtree/tree/modify.rst b/docs_sphinx/source/bigtree/tree/modify.rst index 55db424b..b3f95eca 100644 --- a/docs_sphinx/source/bigtree/tree/modify.rst +++ b/docs_sphinx/source/bigtree/tree/modify.rst @@ -6,9 +6,9 @@ There are two types of modification available, 1. **Non-replacing scenario**: Shift or copy nodes within same tree or between two trees using `from_paths` (list of paths) and `to_paths` (list of paths). 2. **Replacing scenario**: Shift or copy nodes within same tree or between two trees *while replacing the to-node* using `from_paths` (list of paths) and `to_paths` (list of paths). -In **non-replacing scenario**, there are several configurations available for customization. +In **non-replacing scenario**, there are several configurations available for customisation. -.. list-table:: Available Configurations for Customization +.. list-table:: Available Configurations for Customisation :widths: 20 40 40 :header-rows: 1