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.
DataFrames.DataFrame
MetaGraphsNext.MetaGraph
MultiScaleTreeGraph.AbstractNodeMTG
MultiScaleTreeGraph.MutableNodeMTG
MultiScaleTreeGraph.Node
MultiScaleTreeGraph.NodeMTG
AbstractTrees.children
AbstractTrees.parent
Base.:==
Base.:==
Base.append!
Base.getindex
Base.getindex
Base.length
Base.names
Base.parent
Base.print
DataFrames.select!
DataFrames.transform
DataFrames.transform!
MultiScaleTreeGraph.addchild!
MultiScaleTreeGraph.ancestors
MultiScaleTreeGraph.branching_order!
MultiScaleTreeGraph.cache_name
MultiScaleTreeGraph.cache_nodes!
MultiScaleTreeGraph.check_filters
MultiScaleTreeGraph.clean_cache!
MultiScaleTreeGraph.components
MultiScaleTreeGraph.delete_node!
MultiScaleTreeGraph.delete_nodes!
MultiScaleTreeGraph.descendants
MultiScaleTreeGraph.descendants!
MultiScaleTreeGraph.descendants_!
MultiScaleTreeGraph.expand_node!
MultiScaleTreeGraph.filter_fun_nothing
MultiScaleTreeGraph.get_attributes
MultiScaleTreeGraph.get_classes
MultiScaleTreeGraph.get_description
MultiScaleTreeGraph.get_features
MultiScaleTreeGraph.get_node
MultiScaleTreeGraph.get_node_printing!
MultiScaleTreeGraph.get_printing
MultiScaleTreeGraph.get_root
MultiScaleTreeGraph.index
MultiScaleTreeGraph.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_filtered
MultiScaleTreeGraph.is_segment!
MultiScaleTreeGraph.isleaf
MultiScaleTreeGraph.isroot
MultiScaleTreeGraph.issection
MultiScaleTreeGraph.issection
MultiScaleTreeGraph.lastchild
MultiScaleTreeGraph.lastsibling
MultiScaleTreeGraph.link
MultiScaleTreeGraph.link!
MultiScaleTreeGraph.list_nodes
MultiScaleTreeGraph.max_id
MultiScaleTreeGraph.new_child_link
MultiScaleTreeGraph.new_id
MultiScaleTreeGraph.new_node_MTG
MultiScaleTreeGraph.next_line!
MultiScaleTreeGraph.nleaves
MultiScaleTreeGraph.nleaves!
MultiScaleTreeGraph.nleaves_siblings!
MultiScaleTreeGraph.node_attributes
MultiScaleTreeGraph.node_attributes!
MultiScaleTreeGraph.node_id
MultiScaleTreeGraph.node_mtg
MultiScaleTreeGraph.node_traversal_cache
MultiScaleTreeGraph.parse_MTG_node
MultiScaleTreeGraph.parse_MTG_node_attr
MultiScaleTreeGraph.parse_line_to_node!
MultiScaleTreeGraph.parse_macro_args
MultiScaleTreeGraph.parse_mtg!
MultiScaleTreeGraph.parse_node_attributes
MultiScaleTreeGraph.parse_section!
MultiScaleTreeGraph.pipe_model!
MultiScaleTreeGraph.pipe_model!
MultiScaleTreeGraph.prune!
MultiScaleTreeGraph.read_mtg
MultiScaleTreeGraph.rechildren!
MultiScaleTreeGraph.reparent!
MultiScaleTreeGraph.rewrite_expr!
MultiScaleTreeGraph.scale
MultiScaleTreeGraph.scale!
MultiScaleTreeGraph.scales
MultiScaleTreeGraph.siblings
MultiScaleTreeGraph.split_MTG_elements
MultiScaleTreeGraph.strip_comments
MultiScaleTreeGraph.symbol
MultiScaleTreeGraph.symbol!
MultiScaleTreeGraph.symbols
MultiScaleTreeGraph.traverse
MultiScaleTreeGraph.traverse!
MultiScaleTreeGraph.unsafe_getindex
MultiScaleTreeGraph.write_mtg
MultiScaleTreeGraph.@mutate_mtg!
MultiScaleTreeGraph.@mutate_node!
DataFrames.DataFrame
— MethodDataFrame(mtg::Node)
DataFrame(mtg::Node, key)
Convert an MTG into a DataFrame.
Arguments
mtg::Node
: An mtg node (usually the root node).key
: The attribute(s) name(s). Select a list of variables given either as a Symbol
(faster), a String, or an Array of (or a Tuple).
Examples
# Importing an mtg from the package:
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
# Full DataFrame:
DataFrame(mtg)
# Select just :Length:
DataFrame(mtg, :Length)
# Select just :Length and :Width:
DataFrame(mtg, [:Length, :Width])
MetaGraphsNext.MetaGraph
— MethodMetaGraph(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
— TypeAbstract supertype for all types describing the MTG coding for a node.
See NodeMTG
and MutableNodeMTG
for examples of implementation.
MultiScaleTreeGraph.MutableNodeMTG
— TypeNodeMTG(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
SYMBOL
column of theCLASSES
section
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
— TypeNode(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 (seeNodeMTG
or
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.traverse
to 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
mtg
MultiScaleTreeGraph.NodeMTG
— TypeNodeMTG(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
SYMBOL
column of theCLASSES
section
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
— MethodAbstractTrees.children(node::Node{T,A}) where {T,A}
Get the children of a MultiScaleTreeGraph node.
AbstractTrees.parent
— MethodAbstractTrees.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!
— Methodappend!(node::Node{M<:AbstractNodeMTG, <:MutableNamedTuple, GenericNode}, attr)
append!(node::Node{M<:AbstractNodeMTG, <:Dict, GenericNode}, attr)
Append new attributes to a node attributes.
Base.getindex
— MethodIndexing Node attributes from node, e.g. node[:length] or node["length"]
Base.getindex
— MethodIndexing a Node using an integer will index in its children
Base.length
— MethodReturns the length of the subtree below the node (including it)
Base.names
— Methodnames(mtg)
Get all attributes names available on the mtg and its children. This is an alias for get_attributes
.
Base.parent
— MethodBase.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
— MethodPrint 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: Leaf
DataFrames.select!
— Methodselect!(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)
DataFrames.transform
— Functiontransform!(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 Strings.link = nothing
: The link with the previous node to filter-in. Usually a Tuple-alike of Char.filter_fun = nothing
: Any filtering function taking a node as input, e.g.isleaf
.ignore_nothing = false
: filter-out the nodes withnothing
values 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_name
pair. This form is used to rename an attribute name - a
:var_name => function => :new_var_name
or
[: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_name
form 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
function
form 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])
DataFrames.transform!
— Functiontransform!(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 Strings.link = nothing
: The link with the previous node to filter-in. Usually a Tuple-alike of Char.filter_fun = nothing
: Any filtering function taking a node as input, e.g.isleaf
.ignore_nothing = false
: filter-out the nodes withnothing
values 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_name
pair. This form is used to rename an attribute name - a
:var_name => function => :new_var_name
or
[: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_name
form 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
function
form 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.addchild!
— Methodaddchild!(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)
)
mtg
MultiScaleTreeGraph.ancestors
— Methodancestors(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 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
).
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 withnothing
values for the givenkey
type::Union{Union,DataType}
: The type of the attribute. Makes the function run much
faster if provided (≈4x faster).
Note
In most cases, the type
argument should be given as a union of Nothing
and the data type of the attribute to manage missing or inexistant data, e.g. measurements made at one scale only. See examples for more details.
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) # Short to write, but slower to execute
# Fast version, note that we pass a union of Nothing and Float64 because there are some nodes
# without a `Length` attribute:
ancestors(leaf_node, :Length, type = Union{Nothing,Float64})
# Filter by scale:
ancestors(leaf_node, :XX, scale = 1, type = Float64)
ancestors(leaf_node, :Length, scale = 3, type = Float64)
# Filter by symbol:
ancestors(leaf_node, :Length, symbol = "Internode")
ancestors(leaf_node, :Length, symbol = ("Axis","Internode"))
MultiScaleTreeGraph.branching_order!
— Methodbranching_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 1
MultiScaleTreeGraph.cache_name
— Methodcache_name(vars...)
Make a unique name based on the vars names.
Examples
cache_name("test","var")
MultiScaleTreeGraph.cache_nodes!
— Methodcache_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 automatically usually for traversal then when using traverse!
or transform!
.
Examples
file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))), "test", "files", "simple_plant.mtg")
mtg = read_mtg(file, Dict)
# 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.check_filters
— Methodcheck_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.clean_cache!
— Methodclean_cache!(mtg)
Clean the cached variables in the mtg, usually added from descendants!
.
MultiScaleTreeGraph.components
— Functionsymbols(mtg)
components(mtg)
Get all the symbols names, a.k.a. components of an MTG.
MultiScaleTreeGraph.delete_node!
— Methoddeletenode!(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!
— Methoddelete_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 Strings.link = nothing
: The link with the previous node to delete. Usually a Tuple-alike of Char.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_fun
isnew_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, symbol = "Leaf")
# Delete the leaves and internodes:
delete_nodes!(mtg, symbol = ("Leaf","Internode"))
MultiScaleTreeGraph.descendants
— Functiondescendants(node::Node,key,<keyword arguments>)
descendants(node::Node,<keyword arguments>)
descendants!(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!
). It also only works for trees with attributes of subtype of AbstractDict
.
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 aSymbol
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 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
).
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 withnothing
values for the givenkey
type::Union{Union,DataType}
: The type of the attribute. Can make the function run much
faster if provided (e.g. ≈4x faster).
Tips
To get the values of the leaves use isleaf
as the filtering function, e.g.: descendants(mtg, :Width; filter_fun = isleaf)
.
Note
In most cases, the type
argument should be given as a union of Nothing
and the data type of the attribute to manage missing or inexistant data, e.g. measurements made at one scale only. See examples for more details.
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) # Short to write, but slower to execute
# Fast version, note that we pass a union of Nothing and Float64 because there are some nodes
# without a `Length` attribute:
descendants(mtg, :Length, type = Union{Nothing,Float64})
# Filter by scale:
descendants(mtg, :XEuler, scale = 3, type = Union{Nothing, Float64})
descendants(mtg, :Length, scale = 3, type = Float64) # No `nothing` value here, no need of a union type
# 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!
— Functiondescendants(node::Node,key,<keyword arguments>)
descendants(node::Node,<keyword arguments>)
descendants!(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!
). It also only works for trees with attributes of subtype of AbstractDict
.
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 aSymbol
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 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
).
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 withnothing
values for the givenkey
type::Union{Union,DataType}
: The type of the attribute. Can make the function run much
faster if provided (e.g. ≈4x faster).
Tips
To get the values of the leaves use isleaf
as the filtering function, e.g.: descendants(mtg, :Width; filter_fun = isleaf)
.
Note
In most cases, the type
argument should be given as a union of Nothing
and the data type of the attribute to manage missing or inexistant data, e.g. measurements made at one scale only. See examples for more details.
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) # Short to write, but slower to execute
# Fast version, note that we pass a union of Nothing and Float64 because there are some nodes
# without a `Length` attribute:
descendants(mtg, :Length, type = Union{Nothing,Float64})
# Filter by scale:
descendants(mtg, :XEuler, scale = 3, type = Union{Nothing, Float64})
descendants(mtg, :Length, scale = 3, type = Float64) # No `nothing` value here, no need of a union type
# 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_!
— MethodFast version of descendants_ that mutates the mtg nodes to cache the information.
MultiScaleTreeGraph.expand_node!
— MethodExpand 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
— Methodfilter_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
— Methodget_attributes(mtg)
Get all attributes names available on the mtg and its children.
MultiScaleTreeGraph.get_classes
— Methodget_classes(mtg)
Compute the mtg classes based on its content. Usefull after having mutating the mtg nodes.
MultiScaleTreeGraph.get_description
— Methodget_description(mtg)
Returns nothing
, because we can't really predict the description section from an mtg.
MultiScaleTreeGraph.get_features
— Methodget_features(mtg)
Compute the mtg features section based on its attributes. Usefull after having computed new attributes in the mtg.
MultiScaleTreeGraph.get_node
— Methodget_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!
— Functionget_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
ref
MultiScaleTreeGraph.get_printing
— Methodget_printing(node::Node; leading::AbstractString = "")
Format the printing of the tree according to link: follow or branching
MultiScaleTreeGraph.get_root
— MethodFind the root node of a tree, given any node in the tree.
MultiScaleTreeGraph.index!
— Methodindex(node::Node)
Get the index from the MTG encoding of the node.
MultiScaleTreeGraph.index
— Methodindex(node::Node)
Get the index from the MTG encoding of the node.
MultiScaleTreeGraph.insert_child!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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 Strings.link = nothing
: The link with at which to insert. Usually a Tuple-alike of Char.all::Bool = true
: Continue after the first insertion (true
), or stop.filter_fun = nothing
: Any function taking a node as input, e.g.isleaf
to 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)
mtg
MultiScaleTreeGraph.insert_generation!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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 Strings.link = nothing
: The link with at which to insert. Usually a Tuple-alike of Char.all::Bool = true
: Continue after the first insertion (true
), or stop.filter_fun = nothing
: Any function taking a node as input, e.g.isleaf
to 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)
mtg
MultiScaleTreeGraph.insert_nodes!
— MethodActual workhorse of insertparents!, insertgenerations!, insertchildren!, insertsiblings!
MultiScaleTreeGraph.insert_parent!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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 Strings.link = nothing
: The link with at which to insert. Usually a Tuple-alike of Char.all::Bool = true
: Continue after the first insertion (true
), or stop.filter_fun = nothing
: Any function taking a node as input, e.g.isleaf
to 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)
mtg
MultiScaleTreeGraph.insert_sibling!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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!
— Functioninsert_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
NodeMTG
orMutableNodeMTG
used 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 Strings.link = nothing
: The link with at which to insert. Usually a Tuple-alike of Char.all::Bool = true
: Continue after the first insertion (true
), or stop.filter_fun = nothing
: Any function taking a node as input, e.g.isleaf
to 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)
mtg
MultiScaleTreeGraph.is_filtered
— Methodis_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!
— Methodis_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
— Methodisleaf(node::Node)
Test whether a node is a leaf or not.
MultiScaleTreeGraph.isroot
— Methodisroot(node::Node)
Return true
if node
is the root node (meaning, it has no parent).
MultiScaleTreeGraph.issection
— Methodissection(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
— Methodissection(string)
Is a section
Is a string part of an MTG section ? Returns true
if it does, false
otherwise.
issection("CODE :")
MultiScaleTreeGraph.lastchild
— Methodlastchild(node::Node)
Get the last child of node
, or nothing
if the node is a leaf.
MultiScaleTreeGraph.lastsibling
— Methodlastsibling(node::Node)
Return the last sibling of node
(or nothing
if non-existant).
MultiScaleTreeGraph.link!
— Methodlink(node::Node)
Get the link from the MTG encoding of the node.
MultiScaleTreeGraph.link
— Methodlink(node::Node)
Get the link from the MTG encoding of the node.
MultiScaleTreeGraph.list_nodes
— Methodlist_nodes(mtg)
List all nodes IDs in the subtree of mtg
.
MultiScaleTreeGraph.max_id
— Methodmax_id(mtg)
Returns the maximum id of the mtg
MultiScaleTreeGraph.new_child_link
— Methodnew_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
— Methodnew_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
— Methodnew_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!
— Methodnext_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
— Functionnleaves(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!
— Functionnleaves(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!
— Methodnleaves_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.node_attributes!
— Methodnode_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
— Methodnode_attributes(node::Node)
Get the attributes of a node.
MultiScaleTreeGraph.node_id
— Methodnode_id(node::Node)
Get the unique id of the node in the MTG.
MultiScaleTreeGraph.node_mtg
— Methodnode_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.node_traversal_cache
— Methodnode_traversal_cache(node::Node)
Get the traversal cache of the node if any.
MultiScaleTreeGraph.parse_MTG_node
— MethodParse 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
— MethodParse MTG node attributes names, values and type
Arguments
node_data::String
: A splitted mtg node data (attributes)attr_type::DataType
: the type of the structure used to hold the attributesfeatures::DataFrame
: The features data.frameattr_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!
— Methodparse_line_to_node!(tree_dict, l, line, attr_column_start, node_id, attr_type, 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
— Methodparse_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!
— MethodParse 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_node_attributes
— MethodInstantiate a attr_type
struct with node_attr
keys and values
Arguments
attr_type::DataType
: the type of the structure used to hold the attributesnode_attr::String
: The node attributes as aDict
MultiScaleTreeGraph.parse_section!
— MethodParse 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!
— Methodpipe_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_name
has 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!
— Methodpipe_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!
— Methodprune!(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))
mtg
MultiScaleTreeGraph.read_mtg
— Functionread_mtg(file, attr_type = Dict, mtg_type = MutableNodeMTG; sheet_name = nothing)
Read an MTG file
Arguments
file::String
: The path to the MTG file.attr_type::DataType = Dict
: the type used to hold the attribute values for each node.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 anxlsx
orxlsm
file. It
reads the first sheet if nothing
(default behavior).
Details
attr_type
should be:
NamedTuple
if you don't plan to modify the attributes of the mtg, e.g. to use them for
plotting or computing statistics...
MutableNamedTuple
if you plan to modify the attributes values but not adding new attributes
very often, e.g. recompute an attribute value...
Dict
or similar (e.g.OrderedDict
) if you plan to heavily modify the attributes, e.g.
adding/removing attributes a lot
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)
# Or using another `MutableNamedTuple` for the attributes to be able to add one if needed:
mtg = read_mtg(file,Dict);
# 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!
— Methodrechildren!(node::Node{T,A}, chnodes::Vector{Node{T,A}}) where {T,A}
Set the children of the node.
MultiScaleTreeGraph.reparent!
— Methodreparent!(node::N, p::N) where N<:Node{T,A}
Set the parent of the node.
MultiScaleTreeGraph.rewrite_expr!
— Methodrewrite_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!
— Methodscale(node::Node)
Get the scale from the MTG encoding of the node.
MultiScaleTreeGraph.scale
— Methodscale(node::Node)
Get the scale from the MTG encoding of the node.
MultiScaleTreeGraph.scales
— Methodscales(mtg)
Get all the scales of an MTG.
MultiScaleTreeGraph.siblings
— Methodsiblings(node::Node)
Return the siblings of node
as a vector of nodes (or nothing
if non-existant).
MultiScaleTreeGraph.split_MTG_elements
— Methodsplit_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
— FunctionStrip 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!
— Methodsymbol!(node::Node, symbol)
Set the symbol of the MTG encoding node.
MultiScaleTreeGraph.symbol
— Methodsymbol(node::Node)
Get the symbol from the MTG encoding of the node.
MultiScaleTreeGraph.symbols
— Functionsymbols(mtg)
components(mtg)
Get all the symbols names, a.k.a. components of an MTG.
MultiScaleTreeGraph.traverse
— Functiontraverse!(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 Strings.link = nothing
: The link with the previous node to filter-in. Usually a Tuple-alike of Char.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
end
MultiScaleTreeGraph.traverse!
— Functiontraverse!(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 Strings.link = nothing
: The link with the previous node to filter-in. Usually a Tuple-alike of Char.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
end
MultiScaleTreeGraph.unsafe_getindex
— MethodIndexing 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
— Methodwrite_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))