Search code examples
rmapply

What is the `vapply` equivalent for `mapply`?


In base R, sapply has a safer (and sometimes faster) variant called vapply. mapply is a multivariate version of sapply.

I am running into an edge case issue when using mapply (length-0 input to mapply (not to FUN) yields a list() instead of integer(0) ).

Is there an vapply equivalent of mapply that allows to specify FUN.VALUE (the expected return value type/dimension)?

If not, what is the the recommended pattern in those situations?

A toy example:

size_of_union <- function(A, B) length(union(A, B))
# normal case:
x <- list(1:3, 2, 3)
y <- list(3, 2, numeric(0)) 
mapply(size_of_union, x, y)
#> [1] 3 1 1

# edge-case:
x <- integer(0)
y <- integer(0)
mapply(size_of_union, x, y)
#> list()  # integer(0) would be desired here

A more contrived toy example:

range_of_intersect <- function(A, B) range(intersect(A, B))

x <- list(1:3, 2, 3)
y <- list(3, 2, numeric(0)) 
mapply(range_of_intersect, x, y)
#> Warning in min(x): no non-missing arguments to min; returning Inf
#> Warning in max(x): no non-missing arguments to max; returning -Inf
#>      [,1] [,2] [,3]
#> [1,]    3    2  Inf
#> [2,]    3    2 -Inf


x <- numeric(0)
y <- numeric(0)
mapply(range_of_intersect, x, y)
#> list() # structure(numeric(0), .Dim = c(2L, 0L)) would be desired

Solution

  • For your first case you might use as.integer(Map(size_of_union, x, y))

    More generally you can still use vapply() but you'll need to loop on the index rather than on parallel vectors :

    size_of_union <- function(A, B) length(union(A, B))
    x <- list(1:3, 2, 3)
    y <- list(3, 2, numeric(0)) 
    vapply(seq_along(x), function(i) size_of_union(x[[i]], y[[i]]), integer(1))
    #> [1] 3 1 1
    
    x <- integer(0)
    y <- integer(0)
    vapply(seq_along(x), function(i) size_of_union(x[[i]], y[[i]]), integer(1))
    #> integer(0)
    
    range_of_intersect <- function(A, B) range(intersect(A, B))
    x <- list(1:3, 2, 3)
    y <- list(3, 2, numeric(0)) 
    res <- vapply(seq_along(x), function(i) range_of_intersect(x[[i]], y[[i]]), numeric(2))
    #> Warning in min(x): no non-missing arguments to min; returning Inf
    #> Warning in max(x): no non-missing arguments to max; returning -Inf
    res
    #>      [,1] [,2] [,3]
    #> [1,]    3    2  Inf
    #> [2,]    3    2 -Inf
    dput(res)
    #> structure(c(3, 3, 2, 2, Inf, -Inf), dim = 2:3)
    
    x <- numeric(0)
    y <- numeric(0)
    res <- vapply(seq_along(x), function(i) range_of_intersect(x[[i]], y[[i]]), numeric(2))
    res
    #>     
    #> [1,]
    #> [2,]
    dput(res)
    #> structure(numeric(0), dim = c(2L, 0L))
    

    Created on 2023-07-04 with reprex v2.0.2