MultiScaleTreeGraph.jl functions
Here is a list of all exported functions from MultiScaleTreeGraph.jl. For more details, click on the link and you'll be directed to the function help.
Notable attribute APIs:
attribute,attribute!,attributes,attribute_namesadd_column!,drop_column!,rename_column!descendants_strategy,descendants_strategy!(choose automatic/direct/indexed descendants retrieval)to_table(Tables.jlsource/sink entry-point)
MetaGraphsNext.MetaGraphMultiScaleTreeGraph.AbstractNodeMTGMultiScaleTreeGraph.ColumnarStoreMultiScaleTreeGraph.MutableNodeMTGMultiScaleTreeGraph.NodeMultiScaleTreeGraph.NodeMTGAbstractTrees.childrenAbstractTrees.parentBase.:(==)Base.:(==)Base.append!Base.getindexBase.getindexBase.lengthBase.namesBase.parentBase.printMultiScaleTreeGraph._maybe_traversal_cacheMultiScaleTreeGraph.addchild!MultiScaleTreeGraph.ancestorsMultiScaleTreeGraph.attributeMultiScaleTreeGraph.attribute!MultiScaleTreeGraph.attribute_namesMultiScaleTreeGraph.attributesMultiScaleTreeGraph.branching_order!MultiScaleTreeGraph.cache_nameMultiScaleTreeGraph.cache_nodes!MultiScaleTreeGraph.clean_cache!MultiScaleTreeGraph.columnarize!MultiScaleTreeGraph.componentsMultiScaleTreeGraph.delete_node!MultiScaleTreeGraph.delete_nodes!MultiScaleTreeGraph.descendantsMultiScaleTreeGraph.descendants!MultiScaleTreeGraph.descendants_!MultiScaleTreeGraph.descendants_strategyMultiScaleTreeGraph.expand_node!MultiScaleTreeGraph.filter_fun_nothingMultiScaleTreeGraph.get_attributesMultiScaleTreeGraph.get_classesMultiScaleTreeGraph.get_descriptionMultiScaleTreeGraph.get_featuresMultiScaleTreeGraph.get_nodeMultiScaleTreeGraph.get_node_printing!MultiScaleTreeGraph.get_printingMultiScaleTreeGraph.get_rootMultiScaleTreeGraph.indexMultiScaleTreeGraph.index!MultiScaleTreeGraph.insert_child!MultiScaleTreeGraph.insert_children!MultiScaleTreeGraph.insert_generation!MultiScaleTreeGraph.insert_generations!MultiScaleTreeGraph.insert_nodes!MultiScaleTreeGraph.insert_parent!MultiScaleTreeGraph.insert_parents!MultiScaleTreeGraph.insert_sibling!MultiScaleTreeGraph.insert_siblings!MultiScaleTreeGraph.is_filteredMultiScaleTreeGraph.is_segment!MultiScaleTreeGraph.isleafMultiScaleTreeGraph.isrootMultiScaleTreeGraph.issectionMultiScaleTreeGraph.issectionMultiScaleTreeGraph.lastchildMultiScaleTreeGraph.lastsiblingMultiScaleTreeGraph.linkMultiScaleTreeGraph.link!MultiScaleTreeGraph.list_nodesMultiScaleTreeGraph.max_idMultiScaleTreeGraph.mtg_tableMultiScaleTreeGraph.new_child_linkMultiScaleTreeGraph.new_idMultiScaleTreeGraph.new_node_MTGMultiScaleTreeGraph.next_line!MultiScaleTreeGraph.nleavesMultiScaleTreeGraph.nleaves!MultiScaleTreeGraph.nleaves_siblings!MultiScaleTreeGraph.no_node_filtersMultiScaleTreeGraph.node_attributesMultiScaleTreeGraph.node_attributes!MultiScaleTreeGraph.node_idMultiScaleTreeGraph.node_mtgMultiScaleTreeGraph.parse_MTG_nodeMultiScaleTreeGraph.parse_MTG_node_attrMultiScaleTreeGraph.parse_line_to_node!MultiScaleTreeGraph.parse_macro_argsMultiScaleTreeGraph.parse_mtg!MultiScaleTreeGraph.parse_section!MultiScaleTreeGraph.pipe_model!MultiScaleTreeGraph.pipe_model!MultiScaleTreeGraph.prune!MultiScaleTreeGraph.read_mtgMultiScaleTreeGraph.rechildren!MultiScaleTreeGraph.reparent!MultiScaleTreeGraph.rewrite_expr!MultiScaleTreeGraph.scaleMultiScaleTreeGraph.scale!MultiScaleTreeGraph.scalesMultiScaleTreeGraph.select!MultiScaleTreeGraph.siblingsMultiScaleTreeGraph.split_MTG_elementsMultiScaleTreeGraph.strip_commentsMultiScaleTreeGraph.symbolMultiScaleTreeGraph.symbol!MultiScaleTreeGraph.symbol_tableMultiScaleTreeGraph.symbolsMultiScaleTreeGraph.to_tableMultiScaleTreeGraph.transformMultiScaleTreeGraph.transform!MultiScaleTreeGraph.traverseMultiScaleTreeGraph.traverse!MultiScaleTreeGraph.unsafe_getindexMultiScaleTreeGraph.write_mtgMultiScaleTreeGraph.@mutate_mtg!MultiScaleTreeGraph.@mutate_node!
MetaGraphsNext.MetaGraph — Method
MetaGraph(g::Node)Convert an MTG into a MetaGraph.
Examples
# Importing an mtg from the package:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
MetaGraph(mtg)MultiScaleTreeGraph.AbstractNodeMTG — Type
Abstract supertype for all types describing the MTG coding for a node.
See NodeMTG and MutableNodeMTG for examples of implementation.
MultiScaleTreeGraph.ColumnarStore — Type
Marker type used by read_mtg to request the columnar attribute backend.
MultiScaleTreeGraph.MutableNodeMTG — Type
NodeMTG(link, symbol, index, scale)
MutableNodeMTG(link, symbol, index, scale)NodeMTG structure
Builds an MTG node to hold data about the link to the previous node, the symbol of the node, and its index.
Note
- The symbol should match the possible values listed in the
SYMBOLcolumn of theCLASSESsection
in the mtg file if read from a file.
- The index is totaly free, and can be used as a way to e.g. keep track of the branching order.
NodeMTG("<", "Leaf", 2, 0)MultiScaleTreeGraph.Node — Type
Node(MTG<:AbstractNodeMTG)
Node(parent::Node, MTG<:AbstractNodeMTG)
Node(id::Int, MTG<:AbstractNodeMTG, attributes)
Node(id::Int, parent::Node, MTG<:AbstractNodeMTG, attributes)
Node(id::Int, parent::Node, children::Vector{Node}, MTG<:AbstractNodeMTG, attributes)
Node(
id::Int,
parent::Node,
children::Vector{Node},
MTG<:AbstractNodeMTG,
attributes;
traversal_cache
)Type that defines an MTG node (i.e. an element) with:
id: The unique id of node (unique in the whole MTG)parent: the parent node (if not the root node)children: an optional array of children nodesMTG: the MTG description, or encoding (seeNodeMTGor
attributes: the node attributes, that can be anything but
usually a Dict{String,Any}
traversal_cache: a cache for the traversal, used by e.g.traverseto traverse more efficiently particular nodes in the MTG
The node is an entry point to a Mutli-Scale Tree Graph, meaning we can move through the MTG from any of its node. The root node is the node without parent. A leaf node is a node without any children. Root and leaf nodes are used with their computer science meaning throughout the package, not in the biological sense.
Note that it is possible to create a whole MTG using only the Node type, because it has methods to create a node as a child of another node (see example below).
Examples
mtg = Node(NodeMTG("/", "Plant", 1, 1))
internode = Node(mtg, NodeMTG("/", "Internode", 1, 2))
# Note that the node is created with a parent, so it is not necessary to add it as a child of the `mtg ` Node
mtgMultiScaleTreeGraph.NodeMTG — Type
NodeMTG(link, symbol, index, scale)
MutableNodeMTG(link, symbol, index, scale)NodeMTG structure
Builds an MTG node to hold data about the link to the previous node, the symbol of the node, and its index.
Note
- The symbol should match the possible values listed in the
SYMBOLcolumn of theCLASSESsection
in the mtg file if read from a file.
- The index is totaly free, and can be used as a way to e.g. keep track of the branching order.
NodeMTG("<", "Leaf", 2, 0)AbstractTrees.children — Method
AbstractTrees.children(node::Node{T,A}) where {T,A}Get the children of a MultiScaleTreeGraph node.
AbstractTrees.parent — Method
AbstractTrees.parent(node::Node{T,A})Get the parent of a MultiScaleTreeGraph node. If the node is the root, it returns nothing.
See also reparent! to update the parent of a node.
Base.:(==) — Method
==(a::Node, b::Node)Test AbstractNodeMTG equality.
Base.:(==) — Method
==(a::Node, b::Node)Test Node equality. The parent, children and siblings are not tested, only their id is.
Base.append! — Method
append!(node::Node{M<:AbstractNodeMTG, <:MutableNamedTuple, GenericNode}, attr)
append!(node::Node{M<:AbstractNodeMTG, <:Dict, GenericNode}, attr)Append new attributes to a node attributes.
Base.getindex — Method
Indexing Node attributes from node, e.g. node[:length] or node["length"]
Base.getindex — Method
Indexing a Node using an integer will index in its children
Base.length — Method
Returns the length of the subtree below the node (including it)
Base.names — Method
names(mtg)Get all attributes names available on the mtg and its children. This is an alias for get_attributes.
Base.parent — Method
Base.parent(node::Node{T,A})Get the parent of a MultiScaleTreeGraph node. If the node is the root, it returns nothing.
See also reparent! to update the parent of a node.
Base.print — Method
Print a node to io using an UTF-8 formatted representation of the tree. Most of the code from DataTrees.jl
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
mtg
# / 1: $
# └─ / 2: Individual
# └─ / 3: Axis
# └─ / 4: Internode
# ├─ + 5: Leaf
# └─ < 6: Internode
# └─ + 7: LeafMultiScaleTreeGraph._maybe_traversal_cache — Method
node_traversal_cache(node::Node)Get the traversal cache of the node if any.
MultiScaleTreeGraph.addchild! — Method
addchild!(p::Node, id::Int, MTG<:AbstractNodeMTG, attributes)
addchild!(p::Node, MTG<:AbstractNodeMTG, attributes)
addchild!(p::Node, MTG<:AbstractNodeMTG)
addchild!(p::Node, child::Node; force=false)Add a new child to a parent node (p), and add the parent node as the parent. Returns the child node.
See also insert_child!, or directly Node where we can pass the parent, and it uses addchild! under the hood.
Examples
# Create a root node:
mtg = MultiScaleTreeGraph.Node(
NodeMTG("/", "Plant", 1, 1),
Dict{Symbol,Any}()
)
roots = addchild!(
mtg,
NodeMTG("+", "RootSystem", 1, 2)
)
stem = addchild!(
mtg,
NodeMTG("+", "Stem", 1, 2)
)
phyto = addchild!(
stem,
NodeMTG("/", "Phytomer", 1, 3)
)
mtgMultiScaleTreeGraph.ancestors — Method
ancestors(node::Node,key,<keyword arguments>)
ancestors(node::Node,<keyword arguments>)Get attribute values from the ancestors (basipetal), or the ancestor nodes that are not filtered-out.
Arguments
Mandatory arguments
node::Node: The node to start at.key: The key, or attribute name. It is only mandatory for the first method that search for attributes values. The second method returns the node directly.
Make it a Symbol for faster computation time.
Keyword Arguments
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Char.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that
is filtered out (false).
self = false: is the value for the current node needed ?filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.recursivity_level = -1: The maximum number of recursions allowed (considering filters).
E.g. to get the parent only: recursivity_level = 1, for parent + grand-parent: recursivity_level = 2. If a negative value is provided (the default), the function returns all valid values from the node to the root.
ignore_nothing = false: filter-out the nodes withnothingvalues for the givenkeytype::Union{Union,DataType}: Deprecated. Return types are inferred automatically.
Examples
# Importing an example mtg from the package:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Using a leaf node from the mtg:
leaf_node = get_node(mtg, 5)
ancestors(leaf_node, :Length)
# Filter by scale:
ancestors(leaf_node, :XX, scale = 1)
ancestors(leaf_node, :Length, scale = 3, ignore_nothing=true)
# Filter by symbol:
ancestors(leaf_node, :Length, symbol = :Internode)
ancestors(leaf_node, :Length, symbol = (:Axis,:Internode))MultiScaleTreeGraph.attribute! — Method
attribute!(node::Node, key::Symbol, value)Set one attribute on a node.
MultiScaleTreeGraph.attribute — Method
attribute(node::Node, key::Symbol; default=nothing)Get one attribute from a node.
MultiScaleTreeGraph.attribute_names — Method
attribute_names(node::Node)Return the attribute names available for this node.
MultiScaleTreeGraph.attributes — Method
attributes(node::Node; format=:namedtuple)Get all attributes from a node as a snapshot.
MultiScaleTreeGraph.branching_order! — Method
branching_order!(mtg; ascend = true)Compute the topological branching order of the nodes in an mtg.
Arguments
mtg: the mtg, e.g. output fromread_mtg()ascend: Iftrue, the order is computed from the base (acropetal), iffalse,
it is computed from the tip (basipetal).
Notes
The order of a node is computed from the maximum order of their children when using the basipetal computation.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
branching_order!(mtg)
DataFrame(mtg, :branching_order)
# 7×2 DataFrame
# Row │ tree branching_order
# │ String Int64
# ─────┼───────────────────────────────────────────────
# 1 │ / 1: $ 1
# 2 │ └─ / 2: Individual 1
# 3 │ └─ / 3: Axis 1
# 4 │ └─ / 4: Internode 1
# 5 │ ├─ + 5: Leaf 2
# 6 │ └─ < 6: Internode 1
# 7 │ └─ + 7: Leaf 2
branching_order!(mtg, ascend = false)
DataFrame(mtg, :branching_order)
# 7×2 DataFrame
# Row │ tree branching_order
# │ String Int64
# ─────┼───────────────────────────────────────────────
# 1 │ / 1: $ 2
# 2 │ └─ / 2: Individual 2
# 3 │ └─ / 3: Axis 2
# 4 │ └─ / 4: Internode 2
# 5 │ ├─ + 5: Leaf 1
# 6 │ └─ < 6: Internode 2
# 7 │ └─ + 7: Leaf 1MultiScaleTreeGraph.cache_name — Method
cache_name(vars...)Make a unique name based on the vars names.
Examples
cache_name("test","var")MultiScaleTreeGraph.cache_nodes! — Method
cache_nodes!(node; scale=nothing, symbol=nothing, link=nothing, filter_fun=nothing, overwrite=false)Cache the nodes of the mtg based on the filters that would be applied to a traversal. This is used automatically when traversing using traverse! or transform!.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))), "test", "files", "simple_plant.mtg")
mtg = read_mtg(file)
# Cache all leaf nodes:
cache_nodes!(mtg, symbol=:Leaf)
# Cached nodes are stored in the traversal_cache field of the mtg (here, the two leaves):
@test MultiScaleTreeGraph.node_traversal_cache(mtg)["_cache_c0bffb8cc8a9b075e40d26be9c2cac6349f2a790"] == [get_node(mtg, 5), get_node(mtg, 7)]
# Then you can use the cached nodes in a traversal:
traverse(mtg, x -> symbol(x), symbol=:Leaf) == [:Leaf, :Leaf]MultiScaleTreeGraph.clean_cache! — Method
clean_cache!(mtg)Clean the cached variables in the mtg, usually added from descendants!.
MultiScaleTreeGraph.columnarize! — Method
columnarize!(mtg::Node)Bind all node attributes to a single MTGAttributeStore.
MultiScaleTreeGraph.components — Function
symbols(mtg)
components(mtg)Get all the symbols names, a.k.a. components of an MTG.
MultiScaleTreeGraph.delete_node! — Method
deletenode!(node; childlinkfun = newchild_link)
Delete a node and re-parent the children to its own parent.
If the node is a root and it has only one child, the child becomes the root, if it has several children, it returns an error.
child_link_fun is a function that takes the child node of a deleted node as input and returns its new link. The default function is new_child_link, which tries to be clever considering the parent and child links. See its help page for more information. If the link shouldn't be modified, use the link function instead.
The function returns the parent node (or the new root if the node is a root)
MultiScaleTreeGraph.delete_nodes! — Method
delete_nodes!(mtg::Node,<keyword arguments>)
Delete nodes in mtg following filters rules.
Arguments
Mandatory arguments
node::Node: The node to start at.
Keyword Arguments (filters)
scale = nothing: The scale to delete. Usually a Tuple-alike of integers.symbol = nothing: The symbol to delete. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to delete. Usually a Tuple-alike of Symbol.all::Bool = true: Continue after the first deletion (true), or stop?filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf
to decide whether to delete a node or not.
child_link_fun = new_child_link: a function that takes the child node of a deleted node
as input and returns its new link (see details).
Notes
- The function is acropetal, meaning it will apply the deletion from leaves to the root to ensure
that one pass is enough and we don't repeat the process of visiting already visited children.
- The function does not do anything fancy, it let the user take care of its own rules when
deleting nodes, except for the link (see below).
- The package provides some pre-made functions for filtering. See for example
is_segment!
to re-compute the mtg at a given scale to have only nodes at branching points. This is often used to match automatic reconstructions from e.g. LiDAR point cloud with manual measurements.
- The default function used for
child_link_funisnew_child_link, which tries to be
clever considering the parent and child links. See its help page for more information. If the link shouldn't be modified, use the link function instead.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
delete_nodes!(mtg, scale = 2) # Will remove all nodes of scale 2
# Delete the leaves:
delete_nodes!(mtg, :Leaf)
# Delete the leaves and internodes:
delete_nodes!(mtg, symbol = (:Leaf,:Internode))MultiScaleTreeGraph.descendants — Function
descendants(node::Node,key;<keyword arguments>)
descendants(node::Node;<keyword arguments>)
descendants!(node::Node,key;<keyword arguments>)
descendants!(out::AbstractVector,node::Node,key;<keyword arguments>)Get attribute values from the descendants of the node (acropetal). The first method returns an array of values, the second an array of nodes that respect the filters, and the third the mutating version of the first one that caches the results in the mtg.
The mutating version (descendants!) cache the results in a cached variable named after the hash of the function call. This version is way faster when descendants is called repeateadly for the same computation on large trees, but require to clean the chache sometimes (see clean_cache!).
Arguments
Mandatory arguments
node::Node: The node to start at.key: The key, or attribute name (only mandatory for the first and third methods). Make it aSymbolfor faster computation time.
Keyword Arguments
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that
is filtered out (false).
self = false: is the value for the current node needed ?filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.recursivity_level = Inf: The maximum number of recursions allowed (considering filters).
E.g. to get the first level children only: recursivity_level = 1, for children + grand-children: recursivity_level = 2. If Inf (the default) or a negative value is provided, there is no recursion limitation.
ignore_nothing = false: filter-out the nodes withnothingvalues for the givenkeytype::Union{Union,DataType}: Deprecated. Return types are inferred automatically.
Tips
To get the values of the leaves use isleaf as the filtering function, e.g.: descendants(mtg, :Width; filter_fun = isleaf).
Examples
# Importing the mtg from the github repo:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
descendants(mtg, :Length)
# Filter by scale:
descendants(mtg, :XEuler, scale = 3)
descendants(mtg, :Length, scale = 3, ignore_nothing=true) # No `nothing` in output
# Filter by symbol:
descendants(mtg, :Length, symbol = :Leaf)
descendants(mtg, :Length, symbol = (:Leaf,:Internode))
# Filter by function, e.g. get the values for the leaves only:
descendants(mtg, :Width; filter_fun = isleaf)
# You can also ask for different attributes by passing them as a vector:
descendants(mtg, [:Width, :Length]; filter_fun = isleaf)
# The output is an array of arrays of length of the attributes you asked for.
# It is possible to cache the results in the mtg using the mutating version `descendants!` (note the `!`
# at the end of the function name):
transform!(mtg, node -> sum(descendants!(node, :Length)) => :subtree_length, symbol = :Internode)
# Or using `@mutate_mtg!` instead of `transform!`:
@mutate_mtg!(mtg, subtree_length = sum(descendants!(node, :Length)), symbol = :Internode)
# The cache is stored in a temporary variable with a name that starts with `_cache_` followed by the SHA
# of the function call, *e.g.*: `:_cache_5c1e97a3af343ce623cbe83befc851092ca61c8d`:
node_attributes(mtg[1][1][1])
# You can then clean the cache to avoid using too much memory:
clean_cache!(mtg)
node_attributes(mtg[1][1][1])MultiScaleTreeGraph.descendants! — Function
descendants(node::Node,key;<keyword arguments>)
descendants(node::Node;<keyword arguments>)
descendants!(node::Node,key;<keyword arguments>)
descendants!(out::AbstractVector,node::Node,key;<keyword arguments>)Get attribute values from the descendants of the node (acropetal). The first method returns an array of values, the second an array of nodes that respect the filters, and the third the mutating version of the first one that caches the results in the mtg.
The mutating version (descendants!) cache the results in a cached variable named after the hash of the function call. This version is way faster when descendants is called repeateadly for the same computation on large trees, but require to clean the chache sometimes (see clean_cache!).
Arguments
Mandatory arguments
node::Node: The node to start at.key: The key, or attribute name (only mandatory for the first and third methods). Make it aSymbolfor faster computation time.
Keyword Arguments
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that
is filtered out (false).
self = false: is the value for the current node needed ?filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.recursivity_level = Inf: The maximum number of recursions allowed (considering filters).
E.g. to get the first level children only: recursivity_level = 1, for children + grand-children: recursivity_level = 2. If Inf (the default) or a negative value is provided, there is no recursion limitation.
ignore_nothing = false: filter-out the nodes withnothingvalues for the givenkeytype::Union{Union,DataType}: Deprecated. Return types are inferred automatically.
Tips
To get the values of the leaves use isleaf as the filtering function, e.g.: descendants(mtg, :Width; filter_fun = isleaf).
Examples
# Importing the mtg from the github repo:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
descendants(mtg, :Length)
# Filter by scale:
descendants(mtg, :XEuler, scale = 3)
descendants(mtg, :Length, scale = 3, ignore_nothing=true) # No `nothing` in output
# Filter by symbol:
descendants(mtg, :Length, symbol = :Leaf)
descendants(mtg, :Length, symbol = (:Leaf,:Internode))
# Filter by function, e.g. get the values for the leaves only:
descendants(mtg, :Width; filter_fun = isleaf)
# You can also ask for different attributes by passing them as a vector:
descendants(mtg, [:Width, :Length]; filter_fun = isleaf)
# The output is an array of arrays of length of the attributes you asked for.
# It is possible to cache the results in the mtg using the mutating version `descendants!` (note the `!`
# at the end of the function name):
transform!(mtg, node -> sum(descendants!(node, :Length)) => :subtree_length, symbol = :Internode)
# Or using `@mutate_mtg!` instead of `transform!`:
@mutate_mtg!(mtg, subtree_length = sum(descendants!(node, :Length)), symbol = :Internode)
# The cache is stored in a temporary variable with a name that starts with `_cache_` followed by the SHA
# of the function call, *e.g.*: `:_cache_5c1e97a3af343ce623cbe83befc851092ca61c8d`:
node_attributes(mtg[1][1][1])
# You can then clean the cache to avoid using too much memory:
clean_cache!(mtg)
node_attributes(mtg[1][1][1])MultiScaleTreeGraph.descendants_! — Method
Fast version of descendants_ that mutates the mtg nodes to cache the information.
MultiScaleTreeGraph.descendants_strategy — Method
descendants_strategy(node::Node)
descendants_strategy!(node::Node, strategy::Symbol)Get or set how descendants(node, key, ...) is computed for columnar MTGs.
:auto(default): choose automatically based on workload.:pointer: always follow parent/children links directly in the graph.:indexed: use a precomputed index for descendant lookups.
The index is based on a Depth-First Search (DFS) visit order (visit a branch deeply, then the next branch). It can speed up repeated descendant requests on mostly stable trees, while :pointer is often better when the tree structure changes very frequently.
MultiScaleTreeGraph.expand_node! — Method
Expand MTG line
Expand the elements denoted by the syntactic sugar "<<", "<.<", "++" or "+.+"
Arguments
x::Array{String}: A split MTG line (e.g. c("/P1","/A1"))line::Array{Int64,1}: The current line index (mutated) in the file. Only
used as information when erroring.
Returns
A Tuple of:
- the split MTG line with all nodes explicitly
- the nodes with common attributes (when using
<.<or+.+)
Examples
x = split("/A1+U85/U86<U87<.<U93<U94<.<U96<U97+.+U100",r"(?<=.)(?=[</+])");
nodes, shared = MultiScaleTreeGraph.expand_node!(x,1)
(AbstractString["/A1", "+U85", "/U86", "<U87", "<U88", "<U89", "<U90", "<U91", "<U92", "<U93", "<U94", "<U95", "<U96", "<U97", "+U98", "+U99", "+U100"], Any[87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100])MultiScaleTreeGraph.filter_fun_nothing — Method
filter_fun_nothing(filter_fun, ignore_nothing, attr_keys)Returns a new filtering function that adds a filter on the keys value for nothing if ignore_nothing is true
MultiScaleTreeGraph.get_attributes — Method
get_attributes(mtg)Get all attributes names available on the mtg and its children.
MultiScaleTreeGraph.get_classes — Method
get_classes(mtg)Compute the mtg classes based on its content. Usefull after having mutating the mtg nodes.
MultiScaleTreeGraph.get_description — Method
get_description(mtg)Returns nothing, because we can't really predict the description section from an mtg.
MultiScaleTreeGraph.get_features — Method
get_features(mtg)Compute the mtg features section based on its attributes. Usefull after having computed new attributes in the mtg.
MultiScaleTreeGraph.get_node — Method
get_node(node::Node, id::Int)Get a node in an mtg by id.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
node_6_2 = get_node(mtg, 6)MultiScaleTreeGraph.get_node_printing! — Function
get_node_printing!(node, lead, ref, print_node, node_lead=0, node_ref="")Get the number of tabulation (in lead) and the "^" (in ref) used as a prefix for the node when writting it to a file, based on the topology of its parent. Also get the node printing (e.g. "/Axis0") in print_node.
The function modifies the lead, ref and print_node vectors in place.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
lead = Int[]
ref = String[]
get_node_printing!(mtg, lead, ref)
lead
refMultiScaleTreeGraph.get_printing — Method
get_printing(node::Node; leading::AbstractString = "")Format the printing of the tree according to link: follow or branching
MultiScaleTreeGraph.get_root — Method
Find the root node of a tree, given any node in the tree.
MultiScaleTreeGraph.index! — Method
index!(node::Node, new_index)Set the index of the MTG encoding of the node. The index should be some kind of integer.
MultiScaleTreeGraph.index — Method
index(node::Node)Get the index from the MTG encoding of the node.
MultiScaleTreeGraph.insert_child! — Function
insert_parent!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_generation!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_child!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_sibling!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])Insert a node in an MTG as:
- a new parent of node:
insert_parent! - a new child of node:
insert_child! - a new sibling of node:
insert_sibling! - a new child of node, but the children of node become the children of the inserted node:
insert_generation!
Arguments
node::Node: The node from which to insert a node (as its parent, child or sibling).template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr_fun: A function to compute new attributes based on the filtered node. Must return
attribute values of the same type as the one used in other nodes from the MTG (e.g. Dict or NamedTuple). If you just need to pass attributes values to a node use x -> your_values.
max_id::Vector{Int64}: The maximum id of the nodes in the MTG as a vector of length one. It is incremented in the function,
and use by default the value from max_id.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
template = MultiScaleTreeGraph.MutableNodeMTG("/", "Shoot", 0, 1)
insert_parent!(mtg[1][1], template)
mtg
# The template can be a function that returns the template. For example a dummy example would
# be a function that uses the NodeMTG of the first child of the node:
insert_parent!(
mtg[1][1],
node -> (
link = link(node[1]),
symbol = symbol(node[1]),
index = index(node[1]),
scale = scale(node[1])
)
)MultiScaleTreeGraph.insert_children! — Function
insert_parents!(node::Node, template, <keyword arguments>)
insert_generations!(node::Node, template, <keyword arguments>)
insert_children!(node::Node, template, <keyword arguments>)
insert_siblings!(node::Node, template, <keyword arguments>)Insert new nodes in the mtg following filters rules. It is important to note the function always return the root node, whether it is the old one or a new inserted one, so the user is encouraged to assign the results to an object.
Insert nodes programmatically in an MTG as:
- new parents of the filtered nodes:
insert_parents! - new children of the filtered nodes:
insert_children! - new siblings of the filtered node:
insert_siblings! - new children of the filtered nodes, but the previous children of the filtered node become
the children of the inserted node: insert_generations!
Arguments
Mandatory arguments
node::Node: The node to start at.template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr: Attributes for the node. Similarly totemplate, can be:- An attribute of the same type as of node attributes (e.g. a Dict or a NamedTuple)
- A function to compute new attributes (should also return same type for the attributes)
Keyword Arguments (filters)
scale = nothing: The scale at which to insert. Usually a Tuple-alike of integers.symbol = nothing: The symbol at which to insert. Usually a Tuple-alike of Symbols.link = nothing: The link with at which to insert. Usually a Tuple-alike of Symbols.all::Bool = true: Continue after the first insertion (true), or stop.filter_fun = nothing: Any function taking a node as input, e.g.isleafto decide
on which node the insertion will be based on.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Insert new Shoot nodes before all scale 2 nodes:
mtg = insert_parents!(mtg, MultiScaleTreeGraph.MutableNodeMTG(:/, :Shoot, 0, 1), scale = 2)
mtgMultiScaleTreeGraph.insert_generation! — Function
insert_parent!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_generation!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_child!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_sibling!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])Insert a node in an MTG as:
- a new parent of node:
insert_parent! - a new child of node:
insert_child! - a new sibling of node:
insert_sibling! - a new child of node, but the children of node become the children of the inserted node:
insert_generation!
Arguments
node::Node: The node from which to insert a node (as its parent, child or sibling).template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr_fun: A function to compute new attributes based on the filtered node. Must return
attribute values of the same type as the one used in other nodes from the MTG (e.g. Dict or NamedTuple). If you just need to pass attributes values to a node use x -> your_values.
max_id::Vector{Int64}: The maximum id of the nodes in the MTG as a vector of length one. It is incremented in the function,
and use by default the value from max_id.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
template = MultiScaleTreeGraph.MutableNodeMTG("/", "Shoot", 0, 1)
insert_parent!(mtg[1][1], template)
mtg
# The template can be a function that returns the template. For example a dummy example would
# be a function that uses the NodeMTG of the first child of the node:
insert_parent!(
mtg[1][1],
node -> (
link = link(node[1]),
symbol = symbol(node[1]),
index = index(node[1]),
scale = scale(node[1])
)
)MultiScaleTreeGraph.insert_generations! — Function
insert_parents!(node::Node, template, <keyword arguments>)
insert_generations!(node::Node, template, <keyword arguments>)
insert_children!(node::Node, template, <keyword arguments>)
insert_siblings!(node::Node, template, <keyword arguments>)Insert new nodes in the mtg following filters rules. It is important to note the function always return the root node, whether it is the old one or a new inserted one, so the user is encouraged to assign the results to an object.
Insert nodes programmatically in an MTG as:
- new parents of the filtered nodes:
insert_parents! - new children of the filtered nodes:
insert_children! - new siblings of the filtered node:
insert_siblings! - new children of the filtered nodes, but the previous children of the filtered node become
the children of the inserted node: insert_generations!
Arguments
Mandatory arguments
node::Node: The node to start at.template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr: Attributes for the node. Similarly totemplate, can be:- An attribute of the same type as of node attributes (e.g. a Dict or a NamedTuple)
- A function to compute new attributes (should also return same type for the attributes)
Keyword Arguments (filters)
scale = nothing: The scale at which to insert. Usually a Tuple-alike of integers.symbol = nothing: The symbol at which to insert. Usually a Tuple-alike of Symbols.link = nothing: The link with at which to insert. Usually a Tuple-alike of Symbols.all::Bool = true: Continue after the first insertion (true), or stop.filter_fun = nothing: Any function taking a node as input, e.g.isleafto decide
on which node the insertion will be based on.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Insert new Shoot nodes before all scale 2 nodes:
mtg = insert_parents!(mtg, MultiScaleTreeGraph.MutableNodeMTG(:/, :Shoot, 0, 1), scale = 2)
mtgMultiScaleTreeGraph.insert_nodes! — Method
Actual workhorse of insertparents!, insertgenerations!, insertchildren!, insertsiblings!
MultiScaleTreeGraph.insert_parent! — Function
insert_parent!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_generation!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_child!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_sibling!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])Insert a node in an MTG as:
- a new parent of node:
insert_parent! - a new child of node:
insert_child! - a new sibling of node:
insert_sibling! - a new child of node, but the children of node become the children of the inserted node:
insert_generation!
Arguments
node::Node: The node from which to insert a node (as its parent, child or sibling).template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr_fun: A function to compute new attributes based on the filtered node. Must return
attribute values of the same type as the one used in other nodes from the MTG (e.g. Dict or NamedTuple). If you just need to pass attributes values to a node use x -> your_values.
max_id::Vector{Int64}: The maximum id of the nodes in the MTG as a vector of length one. It is incremented in the function,
and use by default the value from max_id.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
template = MultiScaleTreeGraph.MutableNodeMTG("/", "Shoot", 0, 1)
insert_parent!(mtg[1][1], template)
mtg
# The template can be a function that returns the template. For example a dummy example would
# be a function that uses the NodeMTG of the first child of the node:
insert_parent!(
mtg[1][1],
node -> (
link = link(node[1]),
symbol = symbol(node[1]),
index = index(node[1]),
scale = scale(node[1])
)
)MultiScaleTreeGraph.insert_parents! — Function
insert_parents!(node::Node, template, <keyword arguments>)
insert_generations!(node::Node, template, <keyword arguments>)
insert_children!(node::Node, template, <keyword arguments>)
insert_siblings!(node::Node, template, <keyword arguments>)Insert new nodes in the mtg following filters rules. It is important to note the function always return the root node, whether it is the old one or a new inserted one, so the user is encouraged to assign the results to an object.
Insert nodes programmatically in an MTG as:
- new parents of the filtered nodes:
insert_parents! - new children of the filtered nodes:
insert_children! - new siblings of the filtered node:
insert_siblings! - new children of the filtered nodes, but the previous children of the filtered node become
the children of the inserted node: insert_generations!
Arguments
Mandatory arguments
node::Node: The node to start at.template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr: Attributes for the node. Similarly totemplate, can be:- An attribute of the same type as of node attributes (e.g. a Dict or a NamedTuple)
- A function to compute new attributes (should also return same type for the attributes)
Keyword Arguments (filters)
scale = nothing: The scale at which to insert. Usually a Tuple-alike of integers.symbol = nothing: The symbol at which to insert. Usually a Tuple-alike of Symbols.link = nothing: The link with at which to insert. Usually a Tuple-alike of Symbols.all::Bool = true: Continue after the first insertion (true), or stop.filter_fun = nothing: Any function taking a node as input, e.g.isleafto decide
on which node the insertion will be based on.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Insert new Shoot nodes before all scale 2 nodes:
mtg = insert_parents!(mtg, MultiScaleTreeGraph.MutableNodeMTG(:/, :Shoot, 0, 1), scale = 2)
mtgMultiScaleTreeGraph.insert_sibling! — Function
insert_parent!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_generation!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_child!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])
insert_sibling!(node, template, attr_fun = node -> typeof(node_attributes(node))(), max_id = [max_id(node)])Insert a node in an MTG as:
- a new parent of node:
insert_parent! - a new child of node:
insert_child! - a new sibling of node:
insert_sibling! - a new child of node, but the children of node become the children of the inserted node:
insert_generation!
Arguments
node::Node: The node from which to insert a node (as its parent, child or sibling).template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr_fun: A function to compute new attributes based on the filtered node. Must return
attribute values of the same type as the one used in other nodes from the MTG (e.g. Dict or NamedTuple). If you just need to pass attributes values to a node use x -> your_values.
max_id::Vector{Int64}: The maximum id of the nodes in the MTG as a vector of length one. It is incremented in the function,
and use by default the value from max_id.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
template = MultiScaleTreeGraph.MutableNodeMTG("/", "Shoot", 0, 1)
insert_parent!(mtg[1][1], template)
mtg
# The template can be a function that returns the template. For example a dummy example would
# be a function that uses the NodeMTG of the first child of the node:
insert_parent!(
mtg[1][1],
node -> (
link = link(node[1]),
symbol = symbol(node[1]),
index = index(node[1]),
scale = scale(node[1])
)
)MultiScaleTreeGraph.insert_siblings! — Function
insert_parents!(node::Node, template, <keyword arguments>)
insert_generations!(node::Node, template, <keyword arguments>)
insert_children!(node::Node, template, <keyword arguments>)
insert_siblings!(node::Node, template, <keyword arguments>)Insert new nodes in the mtg following filters rules. It is important to note the function always return the root node, whether it is the old one or a new inserted one, so the user is encouraged to assign the results to an object.
Insert nodes programmatically in an MTG as:
- new parents of the filtered nodes:
insert_parents! - new children of the filtered nodes:
insert_children! - new siblings of the filtered node:
insert_siblings! - new children of the filtered nodes, but the previous children of the filtered node become
the children of the inserted node: insert_generations!
Arguments
Mandatory arguments
node::Node: The node to start at.template:- A template
NodeMTGorMutableNodeMTGused for the inserted node, - A NamedTuple with values for link, symbol, index, and scale
- Or a function taking the node as input and returning said template
- A template
attr: Attributes for the node. Similarly totemplate, can be:- An attribute of the same type as of node attributes (e.g. a Dict or a NamedTuple)
- A function to compute new attributes (should also return same type for the attributes)
Keyword Arguments (filters)
scale = nothing: The scale at which to insert. Usually a Tuple-alike of integers.symbol = nothing: The symbol at which to insert. Usually a Tuple-alike of Symbols.link = nothing: The link with at which to insert. Usually a Tuple-alike of Symbols.all::Bool = true: Continue after the first insertion (true), or stop.filter_fun = nothing: Any function taking a node as input, e.g.isleafto decide
on which node the insertion will be based on.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Insert new Shoot nodes before all scale 2 nodes:
mtg = insert_parents!(mtg, MultiScaleTreeGraph.MutableNodeMTG(:/, :Shoot, 0, 1), scale = 2)
mtgMultiScaleTreeGraph.is_filtered — Method
is_filtered(node, scale, symbol, link, filter_fun)Is a node filtered in ? Returns true if the node is kept, false if it is filtered-out.
MultiScaleTreeGraph.is_segment! — Method
is_segment(node)Checks if a node (n) has only one child (n+1). This is usefull to simplify a complex mtg to become an mtg with nodes only at the branching points, has it is often measured on the field.
The function also takes care of passing the link of the node (n) to its child (n+1) if the node (n) branches or decompose its parent (n-1). This allows a conservation of the relationships as they previously were in the mtg.
See delete_nodes! for an example of application.
MultiScaleTreeGraph.isleaf — Method
isleaf(node::Node)Test whether a node is a leaf or not.
MultiScaleTreeGraph.isroot — Method
isroot(node::Node)Return true if node is the root node (meaning, it has no parent).
MultiScaleTreeGraph.issection — Method
issection(string,section)Is a section
Is a string part of an MTG section ? Returns true if it does, false otherwise.
Arguments
string::String: The string to test.section::String: The section to test.
issection("CODE :", "CODE")MultiScaleTreeGraph.issection — Method
issection(string)Is a section
Is a string part of an MTG section ? Returns true if it does, false otherwise.
issection("CODE :")MultiScaleTreeGraph.lastchild — Method
lastchild(node::Node)Get the last child of node, or nothing if the node is a leaf.
MultiScaleTreeGraph.lastsibling — Method
lastsibling(node::Node)Return the last sibling of node (or nothing if non-existant).
MultiScaleTreeGraph.link! — Method
link!(node::Node, new_link)Set the link of the MTG encoding of the node. It can be one of "/", "<", or "+".
MultiScaleTreeGraph.link — Method
link(node::Node)Get the link from the MTG encoding of the node.
MultiScaleTreeGraph.list_nodes — Method
list_nodes(mtg)List all nodes IDs in the subtree of mtg.
MultiScaleTreeGraph.max_id — Method
max_id(mtg)Returns the maximum id of the mtg
MultiScaleTreeGraph.mtg_table — Function
mtg_table(mtg::Node, vars=nothing)Return a unified traversal-ordered table view of an MTG (Tables.jl-compatible). Absent attributes are represented as missing. If vars is provided (Symbol/String/vector/tuple), only these attributes are included.
MultiScaleTreeGraph.new_child_link — Function
new_child_link(node)Compute the new link of the child node when deleting a parent node. The rule is to give the child node link of its parent node that is deleted, except when the parent was following its own parent.
The node given as input is the child node here.
The rule is summarized in the following table:
| Deleted node link | Child node link | New child node link | warning |
|---|---|---|---|
| / | / | / | |
| / | + | + | yes (1) |
| / | < | / | |
| + | / | / | yes (2) |
| + | + | + | |
| + | < | + | |
| < | / | / | |
| < | + | + | |
| < | < | < |
The warnings happens when there is no satisfactory way to handle the new link, i.e. when mixing branching and change in scale.
Note that in the case (1) of the warning the first child only takes the "/" link, the others keep their links.
MultiScaleTreeGraph.new_id — Method
new_id(mtg)
new_id(mtg, max_id)Make a new unique identifier by incrementing on the maximum node id. Hint: prefer using max_id = max_id(mtg) and then new_id(mtg, max_is) for performance if you do it repeatidely.
MultiScaleTreeGraph.new_node_MTG — Method
new_node_MTG(node, template<:Union{NodeMTG,MutableNodeMTG,NamedTuple,MutableNamedTuple})
new_node_MTG(node, fn)Returns a new NodeMTG matching the one used in node (either NodeMTG or MutableNodeMTG) based on a template, or on a function that takes a node as input and return said template.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# using a NodeMTG as a template:
MultiScaleTreeGraph.new_node_MTG(mtg, NodeMTG(:/, :Leaf, 1, 2))
# Note that it returns a MutableNodeMTG because `mtg` is using this type instead of a `NodeMTG`
# using a NamedTuple as a template:
MultiScaleTreeGraph.new_node_MTG(mtg, (link = :/, symbol = :Leaf, index = 1, scale = 2))
# using a function that returns a template based on the first child of the node:
MultiScaleTreeGraph.new_node_MTG(
mtg,
x -> (
link = link(x[1]),
symbol = symbol(x[1]),
index = index(x[1]),
scale = scale(x[1]))
)MultiScaleTreeGraph.next_line! — Method
next_line!(f,line)Read line
Read the next line in the IO stream, strip the comments, the missing values and increment the line index.
Arguments
f::IOStream: A buffered IO stream to the mtg file, e.g.f = open(file, "r").line::Array{Int64,1}: The line number at which f is at the start of the funtion (mutated).whitespace::Bool: remove leading whitespaces.
MultiScaleTreeGraph.nleaves — Function
nleaves(node)
nleaves!(node)Get the total number of leaves a node is bearing, i.e. the number of terminal nodes. nleaves! is faster than nleaves but cache the results in a variable so it uses more memory. Please use clean_cache! after calling nleaves! to clean the temporary variables.
Examples
# Importing the mtg from the github repo:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
nleaves!(mtg)
clean_cache!(mtg)MultiScaleTreeGraph.nleaves! — Function
nleaves(node)
nleaves!(node)Get the total number of leaves a node is bearing, i.e. the number of terminal nodes. nleaves! is faster than nleaves but cache the results in a variable so it uses more memory. Please use clean_cache! after calling nleaves! to clean the temporary variables.
Examples
# Importing the mtg from the github repo:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
nleaves!(mtg)
clean_cache!(mtg)MultiScaleTreeGraph.nleaves_siblings! — Method
nleaves_siblings!(x)Compute how many leaves the siblings of node x bear.
Please call clean_cache! after using nleaves_siblings! because it creates temporary variables.
MultiScaleTreeGraph.no_node_filters — Function
check_filters(node; scale = nothing, symbol = nothing, link = nothing)Check if the filters are consistant with the mtg onto which they are applied
Examples
check_filters(mtg, scale = 1)
check_filters(mtg, scale = (1,2))
check_filters(mtg, scale = (1,2), symbol = :Leaf, link = :<)MultiScaleTreeGraph.node_attributes! — Method
node_attributes!(node::Node)Set the attributes of a node, i.e. replace the whole structure by another. This function is internal, and should not be used directly. Use e.g. node.key = value to set a single attribute of the node.
MultiScaleTreeGraph.node_attributes — Method
node_attributes(node::Node)Get the attributes of a node.
MultiScaleTreeGraph.node_id — Method
node_id(node::Node)Get the unique id of the node in the MTG.
MultiScaleTreeGraph.node_mtg — Method
node_mtg(node::Node)Get the MTG encoding of the node, i.e. the MTG description (see NodeMTG or MutableNodeMTG):
scale: the scale of the node (e.g. 1)symbol: the symbol of the node (e.g. "Axis")index: the index of the node (e.g. 1, this is free)link: the link of the node ("/", "+" or "<")
MultiScaleTreeGraph.parse_MTG_node — Method
Parse MTG node
Parse MTG nodes (called from parse_mtg!())
Arguments
l::String: An MTG node (e.g. "/Individual0")
Return
A parsed node in the form of a Dict of three:
- the link
- the symbol
- and the index
MultiScaleTreeGraph.parse_MTG_node_attr — Method
Parse MTG node attributes names, values and type
Arguments
node_data::String: A splitted mtg node data (attributes)features::ColumnTable: The parsed features tableattr_column_start::Integer: The index of the column of the first attributeline::Integer: The current line of the mtg fileforce::Bool: force data reading even if errors are met during conversion ?
Return
A list of attributes
MultiScaleTreeGraph.parse_line_to_node! — Method
parse_line_to_node!(tree_dict, l, line, attr_column_start, node_id, mtg_type, features,classes)Parse a line of the MTG file to a node and add it to the tree dictionary. It may also add several nodes if the line contains several MTG elements.
MultiScaleTreeGraph.parse_macro_args — Method
parse_macro_args(args)Parse filters and arguments given as a collection of expressions. This function is used to get the filters as keyword arguments in macros.
Examples
args = (:(x = node_id(node)), :(y = node.x + 2), :(scale = 2))
MultiScaleTreeGraph.parse_macro_args(args)MultiScaleTreeGraph.parse_mtg! — Method
Parse MTG section
Arguments
f::IOStream: A buffered IO stream to the mtg file, e.g.f = open(file, "r")classes::Array: The class section data as returned byparse_section!description::Array: The description section data as returned byparse_section!features::Array: The features section data as returned byparse_section!line::Array{Int64,1}: The current line index (mutated). Must be given as line ofMTG:l::Array{String,1}: the current lineattr_type::DataType: the type of the structure used to hold the attributes
Note
The buffered IO stream (f) should start at the line of the section.
Returns
The parsed MTG section
MultiScaleTreeGraph.parse_section! — Method
Parse MTG section
Arguments
f::IOStream: A buffered IO stream to the mtg file, e.g.f = open(file, "r").header::Array{String,1}: A string defining the expected header for the class.section::String: The section name.line::Array{Int64,1}: The line number at which f is at the start of the funtion (mutated).l::Array{String,1}: the current line
Note
The buffered IO stream (f) should start at the line of the section.
Returns
The parsed section of the MTG
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
f = open(file, "r")
line = [0] ; l = [""]; l[1] = MultiScaleTreeGraph.next_line!(f,line)
while MultiScaleTreeGraph.issection(l[1]) || MultiScaleTreeGraph.issection(l[1],"CLASSES")
l[1] = MultiScaleTreeGraph.next_line!(f,line)
end
classes = MultiScaleTreeGraph.parse_section!(f,["SYMBOL","SCALE","DECOMPOSITION","INDEXATION","DEFINITION"],"CLASSES",line,l)
close(f)MultiScaleTreeGraph.pipe_model! — Method
pipe_model!(node, var_name, threshold_value; allow_missing = false)Same than pipe_model! but uses another variable as the reference down until a threshold value. This is used for example in the case of LiDAR measurements, where we know the cross-section (:var_name) is well measured down to e.g. 2-3cm of diameter, but should be computed below.
This function allows to compute the cross-section using the pipe model only for some sub-trees with values of :var_name <= threshold_value.
Arguments
node: the mtg, or a specific node at which to start from.var_name: the name of the cross-section attribute name in the nodesthreshold_value: the threshold defining the value below which the cross-section will be
re-computed using the pipe model instead of using var_name.
allow_missing=false: Allow missing values forvar_name, in which case the cross-section is
recomputed using the pipe model. Please use this option only if you know why.
Details
The node cross-section is partitioned from parent to children according to the number of leaves (i.e. terminal nodes) each child subtree has, unless one or more children has a :var_name > threshold_value. In this case the shared cross-section is the one from the parent minus the one of these nodes for which we simply use the measured value. The cross-section of the siblings with :var_name <= threshold_value will be shared as usual using their number of leaves. If :var_name of the siblings are higher than the parent value, the cross-section of the node is computed only using the number of leaves as it should not be bigger.
Word of caution
Some tips when using this function:
- User must ensure that
:var_namehas a value for all nodes in the mtg before calling this
version of pipe_model!, unless allow_missing=true.
- Nodes with untrusted values should be
set to a value below the threshold value to make pipe_model! recompute them.
MultiScaleTreeGraph.pipe_model! — Method
pipe_model!(node, root_value; name=:_cache_a7118a60b2a624134bf9eac2d64f2bb32798626a)Computes the cross-section of node considering its topological environment and the cross-section at the root node (root_value).
The pipe model helps compute the cross-section of the nodes in an mtg by following the rule that the sum of the cross-sections of the children of a node is equal to the node cross-section.
The implementation uses the following algorithm:
First, check how many children a node has.
If it has one child only, the child cross-section is equal to the node cross-section.
If more children, the node cross-section is shared between the children according to the number of leaf nodes their subtree has, i.e. the total number of terminal nodes of their subtree.
Please call clean_cache! after using pipe_model! because it creates temporary variables.
MultiScaleTreeGraph.prune! — Method
prune!(node)Prune a tree at node, i.e. delete the entire sub-tree starting at node (including it).
Returns an error if the node is a root, or the parent node of the (deleted) node.
Examples
using MultiScaleTreeGraph
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
prune!(get_node(mtg, 6))
mtgMultiScaleTreeGraph.read_mtg — Function
read_mtg(file, mtg_type = MutableNodeMTG; sheet_name = nothing)Read an MTG file
Arguments
file::String: The path to the MTG file.mtg_type = MutableNodeMTG: the type used to hold the mtg encoding for each node (i.e.
link, symbol, index, scale). See details section below.
sheet_name = nothing: the sheet name in case you're reading anxlsxorxlsmfile. It
reads the first sheet if nothing (default behavior).
Details
Attributes are always stored as ColumnarAttrs (typed columnar backend). Input values from the file are converted automatically.
The MultiScaleTreeGraph package provides two types for mtg_type, one immutable (NodeMTG), and one mutable (MutableNodeMTG). If you're planning on modifying the mtg encoding of some of your nodes, you should use MutableNodeMTG, and if you don't want to modify anything, use NodeMTG instead as it should be faster.
Note
See the documentation of the MTG format from the package documentation for further details, e.g. The MTG concept.
Returns
The MTG root node.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# We can also read an mtg directly from an excel file from the field:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","tree3h.xlsx")
mtg = read_mtg(file)MultiScaleTreeGraph.rechildren! — Method
rechildren!(node::Node{T,A}, chnodes::Vector{Node{T,A}}) where {T,A}Set the children of the node.
MultiScaleTreeGraph.reparent! — Method
reparent!(node::N, p::N) where N<:Node{T,A}Set the parent of the node.
MultiScaleTreeGraph.rewrite_expr! — Method
rewrite_expr!(arguments)Re-write the call to the variables of a node in an expression to match their location: leave it as it is if the variable is a node field, or add attributes after the node if it is an attribute.
Examples
test = :(x = node.var)
MultiScaleTreeGraph.rewrite_expr!(:mtg,test)
test
# :(mtg[:x] = mtg[:var])
test = :(x = node.foo)
MultiScaleTreeGraph.rewrite_expr!(:mtg,test)
test
# :(mtg[:x] = mtg[:foo])
test = :(x = symbol(node))
MultiScaleTreeGraph.rewrite_expr!(:mtg,test)
test
# :(mtg[:x] = symbol(mtg))
test = :(x = node_mtg(node) |> symbol)
MultiScaleTreeGraph.rewrite_expr!(:mtg,test)
test
# :(mtg[:x] = node_mtg(mtg) |> symbol)MultiScaleTreeGraph.scale! — Method
scale!(node::Node, new_scale)Set the scale of the MTG encoding of the node. The scale should be some kind of integer.
MultiScaleTreeGraph.scale — Method
scale(node::Node)Get the scale from the MTG encoding of the node.
MultiScaleTreeGraph.scales — Method
scales(mtg)Get all the scales of an MTG.
MultiScaleTreeGraph.select! — Method
select!(node::Node, args..., <keyword arguments>)
select(node::Node, args..., <keyword arguments>)Delete all attributes not selected in args..., and optionally apply transformations on the fly on the selected variables. This function works similarly to transform! except it keeps only the selected variables, while transform! add new variables.
See the documentation of transform! for more details on the format of args and on how to use the arguments.
This function adds one more form to args...: a variable name to just select a variable.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
select!(mtg, :Length => (x -> x / 10) => :length_m, :Width, ignore_nothing = true)MultiScaleTreeGraph.siblings — Method
siblings(node::Node)Return the siblings of node as a vector of nodes (or nothing if non-existant).
MultiScaleTreeGraph.split_MTG_elements — Method
split_MTG_elements(l)Split MTG line
Split the elements (e.g. inter-node, growth unit...) in an MTG line
Arguments
l::String: A string for an MTG line (e.g. "/P1/A1").
Return
A vector of elements (keeping their link, e.g. + or <)
split_MTG_elements("/A1+U85/U86<U87<.<U93<U94<.<U96<U97+.+U100")MultiScaleTreeGraph.strip_comments — Function
Strip comments from a string
striplinecomment{T<:String,U<:String}(a::T, cchars::U="#;")Arguments
a::String: the string from which the comments has to be strippedcchars::String: the characters that defines comments
From https://rosettacode.org/wiki/Stripcommentsfromastring#Julia
whitespace::Bool: remove leading whitespaces.
strip_comments("test1")
strip_comments("test2 # with a comment")
strip_comments("# just a comment")
""MultiScaleTreeGraph.symbol! — Method
symbol!(node::Node, symbol)Set the symbol of the MTG encoding node.
MultiScaleTreeGraph.symbol — Method
symbol(node::Node)Get the symbol from the MTG encoding of the node.
MultiScaleTreeGraph.symbol_table — Function
symbol_table(mtg::Node, symbol, vars=nothing)Return a per-symbol column table view (Tables.jl-compatible). If vars is provided (Symbol/String/vector/tuple), only these attributes are included.
MultiScaleTreeGraph.symbols — Function
symbols(mtg)
components(mtg)Get all the symbols names, a.k.a. components of an MTG.
MultiScaleTreeGraph.to_table — Method
to_table(mtg::Node; symbol=nothing, vars=nothing, sink=nothing)Generic table conversion entry-point.
symbol=nothing: unified traversal-ordered tablesymbol=<symbol>: per-symbol tablevars: optional attribute selection (Symbol/String/vector/tuple)sink: optional sink materialization (e.g.sink=DataFrameifDataFrames.jlis loaded)
MultiScaleTreeGraph.transform — Function
transform!(node::Node, args..., <keyword arguments>)
transform(node::Node, args..., <keyword arguments>)Transform (mutate) an MTG (node) in place (transform!) or on a copy (transform) to add attributes specified by args....
Arguments
node::Node: An MTG node (e.g. the whole mtg returned byread_mtg()).args::Any: the transformations (see details)<keyword arguments>:
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.ignore_nothing = false: filter-out the nodes withnothingvalues for the given
attributes used as inputs (apply only to the form :var_name => ...)
Returns
transform!: Nothing, mutates the (sub-)tree in-place. transform: A mutated copy of node.
Notes
Carefull, transform is much slower than transform! because it makes a copy of the whole MTG each time.
Details
The interface of the function is inspired from the one used in DataFrames.jl, but adapted to an MTG.
The args... provided can be of the following forms:
- a
:var_name => :new_var_namepair. This form is used to rename an attribute name - a
:var_name => function => :new_var_nameor
[:var_name1, :var_name2...] => function => :new_var_name pair. The variables are declared as a Symbol or a String (or a vector of), and they are passed as positional arguments to the function. The new attribute name is optional and is automatically generated if not provided by concatenating the source column name(s) and the function name if any.
- a
function => :new_var_nameform that applies a function to a node and puts the results
in a new attribute. This form is usually applied when searching ancestors or descendants values.
- a
functionform that applies a mutating function to a node, without expecting any output.
This form is adapted when using a function that already mutates the node, without the need to return anything, e.g. branching_order!.
Carefull to the form you use! Form 2 expect a function that takes one or more node attributes (== variables) as inputs, while form 3 and 4 expect a function that takes a node.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# We can use transform to apply a function over all nodes (same as using [`traverse!`](@ref))
transform!(mtg, node -> isleaf(node) ? println(node_id(x)," is a leaf") : nothing)
node_5 is a leaf
node_7 is a leaf
# We can compute a new variable based on another. For example to know if the value of the
# `:Length` attribute is provided or not, we can do:
transform!(mtg, :Length => isnothing)
# To check the values we first call [`get_attributes`](@ref) to know the new variable name:
get_attributes(mtg)
# And then we get the values using [`descendants`](@ref)
descendants(mtg, :Length_isnothing, self = true)
# Or DataFrame:
DataFrame(mtg, :Length_isnothing)
# We can also set the attribute name ourselves like so:
transform!(mtg, :Length => isnothing => :no_length)
descendants(mtg, :no_length, self = true)
# We can provide anonymous functions if we want to:
transform!(mtg, :Length => (x -> isnothing(x)) => :no_length)
descendants(mtg, :no_length, self = true)
# When a node does not have an attribute, it returns `nothing`. Most basic functions do not
# handle those very well, e.g.:
transform!(mtg, :Length => log)
# It does not work because some nodes have no value for `:Length`.
# To remove automatically the nodes with `nothing` values, use `ignore_nothing`:
transform!(mtg, :Length => log => :log_length, ignore_nothing = true)
descendants(mtg, :log_length, self = true)
# Or you could handle these manually in your function if you prefer:
transform!(mtg, :Length => (x -> x === nothing ? nothing : log(x)) => :log_length2)
descendants(mtg, :log_length2, self = true)
# Another way is to give a filtering function as an argument:
transform!(mtg, :Length => log => :log_length, filter_fun = x -> x[:Length] !== nothing)
# We can use more than one attribute as input to our function like so:
transform!(
mtg,
[:Width, :Length] => ((x, y) -> (x/2)^2 * π * y) => :volume,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
descendants(mtg, :volume, self = true)
# Note that `filter_fun` filter the node, so we use the node[:attribute] notation here.
# We can also chain operations, and they will be executed sequentially so we can use variables
# computed on the instruction just before:
density = 0.6
transform!(
mtg,
[:Width, :Length] => ((x, y) -> (x/2)^2 * π * y) => :vol,
:vol => (x -> x * density) => :biomass,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
DataFrame(mtg, [:vol, :biomass])
# We can also rename a variable like so:
transform!(
mtg,
:biomass => :mass,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
DataFrame(mtg, [:vol, :mass])
# Finally, we can use variables from ancestors/descendants using the `function => :new_var` form:
function get_mass_descendants(x)
masses = descendants(x, :mass, ignore_nothing = true)
if length(masses) == 0
nothing
else
sum(masses)
end
end
transform!(
mtg,
get_mass_descendants => :mass_beared
)
DataFrame(mtg, [:mass, :mass_beared])MultiScaleTreeGraph.transform! — Function
transform!(node::Node, args..., <keyword arguments>)
transform(node::Node, args..., <keyword arguments>)Transform (mutate) an MTG (node) in place (transform!) or on a copy (transform) to add attributes specified by args....
Arguments
node::Node: An MTG node (e.g. the whole mtg returned byread_mtg()).args::Any: the transformations (see details)<keyword arguments>:
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.ignore_nothing = false: filter-out the nodes withnothingvalues for the given
attributes used as inputs (apply only to the form :var_name => ...)
Returns
transform!: Nothing, mutates the (sub-)tree in-place. transform: A mutated copy of node.
Notes
Carefull, transform is much slower than transform! because it makes a copy of the whole MTG each time.
Details
The interface of the function is inspired from the one used in DataFrames.jl, but adapted to an MTG.
The args... provided can be of the following forms:
- a
:var_name => :new_var_namepair. This form is used to rename an attribute name - a
:var_name => function => :new_var_nameor
[:var_name1, :var_name2...] => function => :new_var_name pair. The variables are declared as a Symbol or a String (or a vector of), and they are passed as positional arguments to the function. The new attribute name is optional and is automatically generated if not provided by concatenating the source column name(s) and the function name if any.
- a
function => :new_var_nameform that applies a function to a node and puts the results
in a new attribute. This form is usually applied when searching ancestors or descendants values.
- a
functionform that applies a mutating function to a node, without expecting any output.
This form is adapted when using a function that already mutates the node, without the need to return anything, e.g. branching_order!.
Carefull to the form you use! Form 2 expect a function that takes one or more node attributes (== variables) as inputs, while form 3 and 4 expect a function that takes a node.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# We can use transform to apply a function over all nodes (same as using [`traverse!`](@ref))
transform!(mtg, node -> isleaf(node) ? println(node_id(x)," is a leaf") : nothing)
node_5 is a leaf
node_7 is a leaf
# We can compute a new variable based on another. For example to know if the value of the
# `:Length` attribute is provided or not, we can do:
transform!(mtg, :Length => isnothing)
# To check the values we first call [`get_attributes`](@ref) to know the new variable name:
get_attributes(mtg)
# And then we get the values using [`descendants`](@ref)
descendants(mtg, :Length_isnothing, self = true)
# Or DataFrame:
DataFrame(mtg, :Length_isnothing)
# We can also set the attribute name ourselves like so:
transform!(mtg, :Length => isnothing => :no_length)
descendants(mtg, :no_length, self = true)
# We can provide anonymous functions if we want to:
transform!(mtg, :Length => (x -> isnothing(x)) => :no_length)
descendants(mtg, :no_length, self = true)
# When a node does not have an attribute, it returns `nothing`. Most basic functions do not
# handle those very well, e.g.:
transform!(mtg, :Length => log)
# It does not work because some nodes have no value for `:Length`.
# To remove automatically the nodes with `nothing` values, use `ignore_nothing`:
transform!(mtg, :Length => log => :log_length, ignore_nothing = true)
descendants(mtg, :log_length, self = true)
# Or you could handle these manually in your function if you prefer:
transform!(mtg, :Length => (x -> x === nothing ? nothing : log(x)) => :log_length2)
descendants(mtg, :log_length2, self = true)
# Another way is to give a filtering function as an argument:
transform!(mtg, :Length => log => :log_length, filter_fun = x -> x[:Length] !== nothing)
# We can use more than one attribute as input to our function like so:
transform!(
mtg,
[:Width, :Length] => ((x, y) -> (x/2)^2 * π * y) => :volume,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
descendants(mtg, :volume, self = true)
# Note that `filter_fun` filter the node, so we use the node[:attribute] notation here.
# We can also chain operations, and they will be executed sequentially so we can use variables
# computed on the instruction just before:
density = 0.6
transform!(
mtg,
[:Width, :Length] => ((x, y) -> (x/2)^2 * π * y) => :vol,
:vol => (x -> x * density) => :biomass,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
DataFrame(mtg, [:vol, :biomass])
# We can also rename a variable like so:
transform!(
mtg,
:biomass => :mass,
filter_fun = x -> x[:Length] !== nothing && x[:Width] !== nothing
)
DataFrame(mtg, [:vol, :mass])
# Finally, we can use variables from ancestors/descendants using the `function => :new_var` form:
function get_mass_descendants(x)
masses = descendants(x, :mass, ignore_nothing = true)
if length(masses) == 0
nothing
else
sum(masses)
end
end
transform!(
mtg,
get_mass_descendants => :mass_beared
)
DataFrame(mtg, [:mass, :mass_beared])MultiScaleTreeGraph.traverse — Function
traverse!(node::Node, f::Function[, args...], <keyword arguments>)
traverse(node::Node, f::Function[, args...], <keyword arguments>)Traverse the nodes of a (sub-)tree, given any starting node in the tree, and apply a function which is either mutating (use traverse!) or not (use traverse).
Arguments
node::Node: An MTG node (e.g. the whole mtg returned byread_mtg()).f::Function: a function to apply over each nodeargs::Any: any argument to pass to the function<keyword arguments>:
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that is filtered out (false).type::Type = Any: The elements type of the returned array. This can speed-up things. Only available for the non-mutating version.recursivity_level::Int = Inf: The maximum depth of the traversal. Default isInf(i.e. no limit).
Returns
nothing for traverse! because it mutates the (sub-)tree in-place, or an Array{type} (or Array{Any} if type is not given) for traverse.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
traverse!(mtg, node -> isleaf(node) ? println(node_id(node)," is a leaf") : nothing)
node_5 is a leaf
node_7 is a leaf
# We can also use the `do...end` block notation when we have a complex set of instructions:
traverse!(mtg) do node
if isleaf(node)
println(node_id(x)," is a leaf")
end
endMultiScaleTreeGraph.traverse! — Function
traverse!(node::Node, f::Function[, args...], <keyword arguments>)
traverse(node::Node, f::Function[, args...], <keyword arguments>)Traverse the nodes of a (sub-)tree, given any starting node in the tree, and apply a function which is either mutating (use traverse!) or not (use traverse).
Arguments
node::Node: An MTG node (e.g. the whole mtg returned byread_mtg()).f::Function: a function to apply over each nodeargs::Any: any argument to pass to the function<keyword arguments>:
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Symbols.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Symbols.filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that is filtered out (false).type::Type = Any: The elements type of the returned array. This can speed-up things. Only available for the non-mutating version.recursivity_level::Int = Inf: The maximum depth of the traversal. Default isInf(i.e. no limit).
Returns
nothing for traverse! because it mutates the (sub-)tree in-place, or an Array{type} (or Array{Any} if type is not given) for traverse.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
traverse!(mtg, node -> isleaf(node) ? println(node_id(node)," is a leaf") : nothing)
node_5 is a leaf
node_7 is a leaf
# We can also use the `do...end` block notation when we have a complex set of instructions:
traverse!(mtg) do node
if isleaf(node)
println(node_id(x)," is a leaf")
end
endMultiScaleTreeGraph.unsafe_getindex — Method
Indexing Node attributes from node, e.g. node[:length] or node["length"], but in an unsafe way, meaning it returns nothing when the key is not found instead of returning an error. It is primarily used when traversing the tree, so if a node does not have a field, it does not return an error.
MultiScaleTreeGraph.write_mtg — Method
write_mtg(file, mtg; kwargs...)
write_mtg(file, mtg, classes, description, features)Write an mtg file to disk.
Arguments
file::String: The path to the MTG file to write.mtg: the mtgclasses: the classes sectiondescription: the description sectionfeatures: the features section
Note
kwargs can be used to give zero, one or two of the classes, description and features instead of all. In this case the missing ones are recomputed using get_classes, get_features or get_description.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
write_mtg("test.mtg",mtg)MultiScaleTreeGraph.@mutate_mtg! — Macro
@mutate_mtg!(node, args...,kwargs...)Mutate the mtg nodes in place.
Arguments
mtg: the mtg to mutateargs...: The computations to apply to the nodes (see examples)kwargs...: Optional keyword arguments for traversing and filtering (see details)
Details
As for descendants and ancestors, kwargs can be any filter from:
scale = nothing: The scale to filter-in (i.e. to keep). Usually a Tuple-alike of integers.symbol = nothing: The symbol to filter-in. Usually a Tuple-alike of Strings.link = nothing: The link with the previous node to filter-in. Usually a Tuple-alike of Char.all::Bool = true: Return all filtered-in nodes (true), or stop at the first node that
is filtered out (false).
filter_fun = nothing: Any filtering function taking a node as input, e.g.isleaf.traversal: The type of tree traversal. By default it is usingAbstractTrees.PreOrderDFS.
Examples
# Importing an mtg from the package:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Compute a new attribute with the scales and add 2 to its values:
@mutate_mtg!(mtg, scaling = node.scales .+ 2, filter_fun = node -> node.scales !== nothing)
# Compute several new attributes, some based on others:
@mutate_mtg!(mtg, x = length(node_id(node)), y = node.x + 2, z = sum(node.y))
# We can also use it without parenthesis:
@mutate_mtg! mtg x = length(node_id(node))MultiScaleTreeGraph.@mutate_node! — Macro
@mutate_node!(node, args...)Mutate a single node in place.
Arguments
node: the node to mutateargs...: The computations to apply to the node (see examples)
See also
@mutate_mtg! to mutate all nodes of an mtg.
Examples
# Importing an mtg from the package:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Compute a new attribute with the scales and add 2 to its values:
@mutate_node!(mtg, scaling = node.scales .+ 2)
# The computation is only applied to the root node. To apply it to all nodes,
# see @mutate_mtg!
# Compute several new attributes, some based on others:
@mutate_node!(mtg, x = length(node_id(node)), y = node.x + 2, z = sum(node.y))
# We can also use it without parenthesis:
@mutate_node! mtg x = length(node_id(node))