I noticed a kind of "inconsistency" in the values returned from two user-defined functions. I tested on a tile of 200mx200m to return a raster of pixel size 50m (16 pixels)
In the following function, if the condition is satisfied, the function returns ONLY one value.
funcky <- function(x)
{
if (max(x) < 22)
return(max(x))
}
grd <- grid_metrics(lasca, funcky(Z), 50)
When the condition is not satisfied, NA values are returned.
> as.array(grd[[1]])
, , 1
[,1] [,2] [,3] [,4]
[1,] 19.92 21.90 NA NA
[2,] NA 21.46 21.89 NA
[3,] NA 21.73 NA NA
[4,] NA NA NA NA
I expected the same logic to be applied when the function returns a list of values. For example,
funcky <- function(x)
{
if (max(x) < 22)
return(list(max(x), mean(x), min(x)))
}
grd <- grid_metrics(lasca, funcky(Z), 50)
But the values are not NA
, they are 0
s as shown below.
> as.array(grd[[1]])
, , 1
[,1] [,2] [,3] [,4]
[1,] 19.92 21.90 0.00 0
[2,] 0.00 21.46 21.89 0
[3,] 0.00 21.73 0.00 0
[4,] 0.00 0.00 0.00 0
, , 2
[,1] [,2] [,3] [,4]
[1,] 6.555234 7.434102 0.000000 0
[2,] 0.000000 5.038366 5.733465 0
[3,] 0.000000 8.699386 0.000000 0
[4,] 0.000000 0.000000 0.000000 0
, , 3
[,1] [,2] [,3] [,4]
[1,] -0.83 -0.04 0.00 0
[2,] 0.00 -0.07 -0.09 0
[3,] 0.00 -0.07 0.00 0
[4,] 0.00 0.00 0.00 0
If the metrics can have negative values (skewness, for example), then these 0
s could potentially be misleading, as is the case in the third layer. Is this behaviour alright?
Adding an else {return(NULL)}
didn't make any difference. And adding else {return(list(NA,NA,NA))}
generated an error:
Error: Column 1 of result for group 2 is type 'logical' but expecting type 'double'. Column types must be consistent for each group.
1 Answer 1
Your example works even if it is not safe because funky
returns NULL
implicitly. The inconsistent behavior with 0
s instead of NA
s is a bug fixed in version 3.0.2 and probably introduced in v3.0.0 20 days before 3.0.2. Update lidR
and it should work.
That being said it is not a good practice. It works because R is very permissive and allows bad coding practice. Adding an else
statement was correct but grid_metrics
uses data.table
under the hood and is thus designed to be type safe. You cannot mix logical
and numeric
it won't be type casted automatically. In R NA
is logical
. So you were almost correct but using wrong types.
funcky <- function(x)
{
if (max(x) < 22)
return(list(max(x), mean(x), min(x)))
else
return(list(NA_real_, NA_real_, NA_real_)
}
-
1Yes, updating to 3.0.2 worked. I wasn't aware of the data type NA_real_. Thanks for that! While improving some code, I have come across some 'best practices' for
if-else
and was curious about howgrid-metrics
handled it. I suppose it is a useful clarification.K_D– K_D2020年07月28日 12:52:31 +00:00Commented Jul 28, 2020 at 12:52 -
1This is not actually about best practices for
if-else
but about undefined behavior in functions. No matter the path taken in the code, a function should always explicitly return something. In your casefuncky
has an undefined behavior forx >= 22
. Hopefully R handle such case natively withNULL
and the behavior ofgrid_metrics
is made in such a way that it works. But you should avoid undefined behaviors.JRR– JRR2020年07月28日 13:36:17 +00:00Commented Jul 28, 2020 at 13:36