-
Notifications
You must be signed in to change notification settings - Fork 1
/
mtg_getters_shortcuts.R
209 lines (190 loc) · 8.94 KB
/
mtg_getters_shortcuts.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#' Get parent value
#'
#' @description Get the value of one or several variables from the parent node. This is a helper
#' function that is usually used as input to [mutate_mtg()] to get the values of the parent node
#' for all nodes.
#'
#' @param attribute Any node attribute (as a character, an expression or a node callm see details)
#' @param node The node (do not put something when used from [mutate_mtg()])
#' @param symbol A character vector for filtering the names of the MTG symbol required for the parent (i.e. the SYMBOL
#' column from the MTG classes).
#' @param scale An integer vector for filtering the MTG scale of the parent (i.e. the SCALE
#' column from the MTG classes).
#' @param link A character vector for filtering the `.link` with the descendant
#' @param filter_fun Any filtering function taking a node as input, e.g. [data.tree::isLeaf()]
#'
#' @param continue If a parent is not of the right `scale`, continue until the `scale`
#' required is met if `TRUE`, or returns `NA` if `FALSE`.
#'
#' @details The `attribute` argument can be given as a string (*e.g.* attribute = "Length"), an
#' expression (*e.g.* attribute = Length) or a call to a node (*e.g.* attribute = node$Length).
#' All are equivalent because they evaluate the node given by the user or by the calling function.
#' This behavior is set to homogenize the grammar used in the calls of `mutate_mtg()`.
#'
#' @note This function returns the values of any attribute of the parent of a node. It is
#' mainly intended to be used in a call to [mutate_mtg()] (see [mutate_mtg()] doc for examples).
#'
#' @return The attribute values from the parent of a node
#'
#' @export
#'
#' @examples
#' filepath= system.file("extdata", "simple_plant.mtg", package = "XploRer")
#' MTG = read_mtg(filepath)
#' parent(Length, node = extract_node(MTG, "node_5"))
parent = function(attribute, node = NULL, scale = NULL, symbol = NULL,
link = NULL, filter_fun = NULL, continue = TRUE) {
attribute_expr = rlang::enexpr(attribute)
# If the node is not given, use the one from the parent environment.
# This is done to make it work from mutate_mtg without the need of
# explicitly giving node = node as argument
if(is.null(node)){
if(!environmentName(env = parent.frame()) == "R_GlobalEnv"){
node = eval(quote(node), parent.frame())
}else{
stop("node should be given when 'parent()' is used interactively")
}
}
check_filters(node = node, scale = scale, symbol = symbol, link = link)
ancestors(!!attribute_expr, node = node, scale = scale, symbol = symbol,
link = link, filter_fun = filter_fun, self = FALSE,
continue = continue, recursivity_level = 1)
}
#' Get children values
#'
#' @description Get attribute values from the children of a node.
#'
#' @param attribute Any node attribute (as a character)
#' @param node The MTG node
#' @param symbol A character vector for filtering the children by the name of their `.symbol` (i.e. the SYMBOL
#' column from the MTG classes).
#' @param scale An integer vector for filtering the `.scale` of the children (i.e. the SCALE
#' column from the MTG classes).
#' @param link A character vector for filtering the `.link` with the children
#' @param filter_fun Any filtering function taking a node as input, e.g. [data.tree::isLeaf()]
#' @param continue If a child is not of the right `scale`, continue until the `scale`
#' required is met if `TRUE`, or returns `NA` if `FALSE`.
#'
#' @details This function is mainly intended to be used with [mutate_mtg()]. In this case,
#' the `node` argument can be left empty (or you can put `node = node` equivalently).
#'
#' @return The attribute values from the children of the node
#'
#' @export
#'
#' @examples
#' filepath= system.file("extdata", "simple_plant.mtg", package = "XploRer")
#' MTG = read_mtg(filepath)
#'
#' # node_6 has one child:
#' children("Length", node = extract_node(MTG, "node_6"))
#'
#' # Using node 4 as reference now:
#' node_4 = extract_node(MTG, "node_4")
#' # node_4 has two children, returns two values:
#' children("Length", node = node_4)
#' # To get the names of those children:
#' children("name", node = node_4)
#'
#' # The width is not available for one child ("node_6"):
#' children("Width", node = node_4)
#'
#' # We can filter by scale if we need to return the values for some scales only:
#' children("Width", node = node_4, symbol = "Leaf")
#' # Here we get the value of node_7 also, because its parent "node_6" is not of scale
#' # "Leaf", so it was filtered out. It you need the values for one scale, but not
#' # making a recursive search from one scale to another until finding the required scale,
#' # you can put the `continue` argument to `FALSE`:
#'
#' children("Width", node = node_4, symbol = "Leaf", continue = FALSE)
#'
#' # To get the values of the children of each node:
#' mutate_mtg(MTG, children_width = children("Width"))
#' print(MTG, "Width", "children_width")
#'
#' # And using only the bodes with symbol "Leaf":
#' mutate_mtg(MTG, children_width2 = children("Width", symbol = "Leaf", continue = FALSE))
#' print(MTG, "Width", "children_width2")
children = function(attribute, node = NULL, scale = NULL, symbol = NULL,
link = NULL, filter_fun = NULL,
continue = TRUE) {
attribute_expr = rlang::enexpr(attribute)
# If the node is not given, use the one from the parent environment.
# This is done to make it work from mutate_mtg without the need of
# explicitly giving node = node as argument
if(is.null(node)){
if(!environmentName(env = parent.frame()) == "R_GlobalEnv"){
node = eval(quote(node), parent.frame())
}else{
stop("node should be given when 'children()' is used interactively")
}
}
check_filters(node = node, scale = scale, symbol = symbol, link = link)
descendants(attribute = !!attribute_expr, node = node, scale = scale, symbol = symbol,
link = link, filter_fun = filter_fun,
continue = continue, recursivity_level = 1)
}
#' Get leaves values
#'
#' @description Get attribute values from the terminal nodes of the descendants sub-tree of a
#' node (i.e. the "leaves" in computer science terms).
#'
#' @param attribute Any node attribute (as a character)
#' @param node The MTG node
#' @param symbol A character vector for filtering the children by the name of their `.symbol` (i.e. the SYMBOL
#' column from the MTG classes).
#' @param scale An integer vector for filtering the `.scale` of the children (i.e. the SCALE
#' column from the MTG classes).
#' @param link A character vector for filtering the `.link` with the children
#' @param filter_fun Any filtering function taking a node as input.
#'
#' @note This function is mainly intended to be used with [mutate_mtg()]. In this case,
#' the `node` argument can be left empty (or you can put `node = node` equivalently).
#'
#' @return The attribute values from the children of the node
#'
#' @export
#'
#' @examples
#' filepath= system.file("extdata", "simple_plant.mtg", package = "XploRer")
#' MTG = read_mtg(filepath)
#'
#' # The MTG has two leaves (node_5 and node_7):
#' leaves("Length", node = MTG)
#' # We can check the function worked well by printing the symbols of each nodes:
#' print(MTG, ".symbol")
#'
#' # If we need the terminal Internodes:
#' leaves("Length", node = MTG, symbol = "Internode")
#'
#' # Or the leaves at a given scale:
#' print(MTG, ".scale")
#' leaves("Length", node = MTG, scale = 2)
#'
leaves = function(attribute, node = NULL, scale = NULL, symbol = NULL,
link = NULL, filter_fun = NULL){
attribute_expr = rlang::enexpr(attribute)
# If the node is not given, use the one from the parent environment.
# This is done to make it work from mutate_mtg without the need of
# explicitly giving node = node as argument
if(is.null(node)){
if(!environmentName(env = parent.frame()) == "R_GlobalEnv"){
node = eval(quote(node), parent.frame())
}else{
stop("node should be given when 'leaves()' is used interactively")
}
}
# Filter the leaves. The complexity here rise from the scales. The terminal node
# at one scale can have children at another scale. So a leaf is a node that has no
# children of the required scale/symbols/links...
filt = rlang::new_function(rlang::pairlist2(node= , scale = scale, symbol = symbol,
link = link, filter_fun = filter_fun),
body = quote(length(descendants("name", node = node,
scale = scale, symbol = symbol,
link = link,
filter_fun = filter_fun)) == 0))
check_filters(node = node, scale = scale, symbol = symbol, link = link)
descendants(attribute = !!attribute_expr, node = node, scale = scale, symbol = symbol,
link = link, filter_fun = filt,
continue = TRUE, recursivity_level = NULL)
}