Hello,
I'm writing a program that takes a tree in input (nested lists) and
returns a copy of it replacing the leaves with something else (eg: a
computation done on the original leaves).
In the example below, the tree is composed by countries and cities,
and the leaves (children of the cities) are vectors of numbers. The
program takes this tree and replaces the vectors at the bottom by
their mean.
I use a recursive function and lapply, and I give names to the
returned list after I get them from lapply. I was happy until I
realised that when I have some anomaly in the data (in the example at
Spain/Madrid I have NULL instead of a vector -- I simulate an error
artificially with stop()) I have no way to trace where in the original
tree is the problem, as the names are lost when I do lapply. I was
thinking to use tryCatch() and do something smart at error/warning,
but I'm afraid the information is all lost.
Is there any obvious way I am missing that would give me the debug
information I need? In the example above ideally I'd like to know that
the program stopped at the path Spain/Madrid.
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
data <- list("France" = list("Paris" = c(21, 34, 42),
"Marseille" = c(23, 51, 64),
"Toulouse" = c(78, 43, 25)),
"Germany" = list("Berlin" = c(43, 20, 87),
"Munich" = c(89, 45, 34),
"Hamburg" = c(98, 78, 32)),
"Spain" = list("Barcelona" = c(43, 49, 72),
"Madrid" = NULL,
"Valencia" = c(23, 23, 76)))
leaf.func <- function(data) {
if(is.null(data)) stop()
return(mean(data))
}
visit.level <- function(data, depth) {
if (depth == 2) {
return(leaf.func(data))
} else {
res <- lapply(data, visit.level, depth + 1)
names(res) <- names(data)
return(res)
}
}
new.data <- visit.level(data, 0)
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Thanks,
Giovanni
You could replace your 'depth' argument with one that shows where in the
original data you are at:
leaf.func <-
function(data, where) {
if(is.null(data)) stop("Null data at ", deparse(where))
return(mean(data))
}
visit.level <-
function(data, where = integer()) {
if (length(where) == 2) {
return(leaf.func(data, where))
} else {
res <- lapply(seq_along(data), function(i)
visit.level(data[[i]], where = c(where, i)))
names(res) <- names(data)
return(res)
}
}
E.g.,> new.data <- visit.level(data, integer())
Error in leaf.func(data, where) : Null data at c(3L, 2L)> data[[3]][2]
$Madrid
NULL
Bill Dunlap
TIBCO Software
wdunlap tibco.com
On Mon, Aug 14, 2017 at 11:43 AM, Giovanni Gherdovich <
g.gherdovich at gmail.com> wrote:
> Hello,
>
> I'm writing a program that takes a tree in input (nested lists) and
> returns a copy of it replacing the leaves with something else (eg: a
> computation done on the original leaves).
> In the example below, the tree is composed by countries and cities,
> and the leaves (children of the cities) are vectors of numbers. The
> program takes this tree and replaces the vectors at the bottom by
> their mean.
>
> I use a recursive function and lapply, and I give names to the
> returned list after I get them from lapply. I was happy until I
> realised that when I have some anomaly in the data (in the example at
> Spain/Madrid I have NULL instead of a vector -- I simulate an error
> artificially with stop()) I have no way to trace where in the original
> tree is the problem, as the names are lost when I do lapply. I was
> thinking to use tryCatch() and do something smart at error/warning,
> but I'm afraid the information is all lost.
>
> Is there any obvious way I am missing that would give me the debug
> information I need? In the example above ideally I'd like to know that
> the program stopped at the path Spain/Madrid.
>
>
> # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
> data <- list("France" = list("Paris" = c(21, 34,
42),
> "Marseille" = c(23, 51, 64),
> "Toulouse" = c(78, 43, 25)),
> "Germany" = list("Berlin" = c(43, 20, 87),
> "Munich" = c(89, 45, 34),
> "Hamburg" = c(98, 78, 32)),
> "Spain" = list("Barcelona" = c(43, 49,
72),
> "Madrid" = NULL,
> "Valencia" = c(23, 23, 76)))
>
> leaf.func <- function(data) {
> if(is.null(data)) stop()
> return(mean(data))
> }
>
> visit.level <- function(data, depth) {
> if (depth == 2) {
> return(leaf.func(data))
> } else {
> res <- lapply(data, visit.level, depth + 1)
> names(res) <- names(data)
> return(res)
> }
> }
>
> new.data <- visit.level(data, 0)
> # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
>
>
> Thanks,
> Giovanni
>
> ______________________________________________
> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/
> posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>
[[alternative HTML version deleted]]
Hello William, that's exactly what I needed. I didn't consider lapply'ing over seq_along(data) instead of data, very useful. Thanks a lot! Giovanni On Mon, Aug 14, 2017 at 10:02 PM, William Dunlap <wdunlap at tibco.com> wrote:> You could replace your 'depth' argument with one that shows where in the > original data you are at: > > leaf.func <- > function(data, where) { > if(is.null(data)) stop("Null data at ", deparse(where)) > return(mean(data)) > } > visit.level <- > function(data, where = integer()) { > if (length(where) == 2) { > return(leaf.func(data, where)) > } else { > res <- lapply(seq_along(data), function(i) > visit.level(data[[i]], where = c(where, i))) > names(res) <- names(data) > return(res) > } > } > > E.g., >> new.data <- visit.level(data, integer()) > Error in leaf.func(data, where) : Null data at c(3L, 2L) >> data[[3]][2] > $Madrid > NULL