2

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 0s 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 0s 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.
3

Your example works even if it is not safe because funky returns NULL implicitly. The inconsistent behavior with 0s instead of NAs 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_)
}
2
  • 1
    Yes, 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 how grid-metrics handled it. I suppose it is a useful clarification.
    – K_D
    Jul 28 '20 at 12:52
  • 1
    This 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 case funcky has an undefined behavior for x >= 22. Hopefully R handle such case natively with NULL and the behavior of grid_metrics is made in such a way that it works. But you should avoid undefined behaviors.
    – JRR
    Jul 28 '20 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.