Binding Explained

 

This page explains some details on broadcasting that are specific to the bind_array() function.

 

The bind_array() function binds 2 or more arrays together along a dimension, and fully supports broadcasting. While it’s relatively easy to reason about broadcasting when it involves only 2 arrays, reasoning about broadcasting when it involves more than 2 arrays becomes a bit more difficult. Therefore, bind_array() comes with the ndim2bc argument (an abbreviation of ” maximum number of dimensions to broadcast”), which allows users to specify the maximum number of dimensions that are allowed to be broadcasted while binding arrays. This way, users won’t get unpleasant surprises.

This should be fairly obvious, but the dimension specified in along is never broadcasted.

 

By default, ndim2bc is set to 1. This means that, by default, bind_array() will broadcast no more than 1 dimension for each array in the input list, when necessary.

Consider the following arrays:


x <- array(1:20, c(4, 5))
y <- array(1:5*10, c(1, 5))
print(x)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    5    9   13   17
#> [2,]    2    6   10   14   18
#> [3,]    3    7   11   15   19
#> [4,]    4    8   12   16   20
print(y)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]   10   20   30   40   50

Binding them together with abind() won’t work:

abind::abind(x, y, along = 2)
Error in abind::abind(x, y, along = 2) : 
  arg 'X2' has dims=1, 5; but need dims=4, X

To bind x and y together along columns, y needs its single row to be recycled (broadcasted) 4 times.

This can be done in a highly efficient way using bind_array(), like so:

bind_array(list(x, y), 2L)
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    1    5    9   13   17   10   20   30   40    50
#> [2,]    2    6   10   14   18   10   20   30   40    50
#> [3,]    3    7   11   15   19   10   20   30   40    50
#> [4,]    4    8   12   16   20   10   20   30   40    50

 

But what if broadcasting is explicitly not desired? What if one actually wants this to produce an error, like abind()? Fret not, for that’s what the ndim2bc argument is for. Setting it to 0 will disable broadcasting altogether:

bind_array(list(x, y), 2L, ndim2bc = 0)
Error in bind_array(list(x, y), 2L, ndim2bc = 0) : 
  maximum number of dimensions to be broadcasted (1) exceeds `ndim2bc` (0)

 

Let’s replace x with a 3 dimensional array:

x <- array(1:20, c(4, 5, 3))
print(x)
#> , , 1
#> 
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    5    9   13   17
#> [2,]    2    6   10   14   18
#> [3,]    3    7   11   15   19
#> [4,]    4    8   12   16   20
#> 
#> , , 2
#> 
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    5    9   13   17
#> [2,]    2    6   10   14   18
#> [3,]    3    7   11   15   19
#> [4,]    4    8   12   16   20
#> 
#> , , 3
#> 
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    5    9   13   17
#> [2,]    2    6   10   14   18
#> [3,]    3    7   11   15   19
#> [4,]    4    8   12   16   20

Trying to bind x with y now will produce an error even with bind_array(), to protect the user from unintended broadcasting:

bind_array(list(x, y), 2L)
Error in bind_array(list(x, y), 2L) : 
  maximum number of dimensions to be broadcasted (2) exceeds `ndim2bc` (1)

If you know you actually do want to broadcast multiple dimensions, simply increase ndim2bc:

bind_array(list(x, y), 2L, ndim2bc = 3L)
#> , , 1
#> 
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    1    5    9   13   17   10   20   30   40    50
#> [2,]    2    6   10   14   18   10   20   30   40    50
#> [3,]    3    7   11   15   19   10   20   30   40    50
#> [4,]    4    8   12   16   20   10   20   30   40    50
#> 
#> , , 2
#> 
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    1    5    9   13   17   10   20   30   40    50
#> [2,]    2    6   10   14   18   10   20   30   40    50
#> [3,]    3    7   11   15   19   10   20   30   40    50
#> [4,]    4    8   12   16   20   10   20   30   40    50
#> 
#> , , 3
#> 
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    1    5    9   13   17   10   20   30   40    50
#> [2,]    2    6   10   14   18   10   20   30   40    50
#> [3,]    3    7   11   15   19   10   20   30   40    50
#> [4,]    4    8   12   16   20   10   20   30   40    50