Writing Custom Functions for matsbyname

Matthew Kuperus Heun

2024-02-12

Introduction

The matsbyname package provides many useful functions for "by name" manipulation of matrices, lists of matrices, and matrices in columns of data frames. However, the built-in functions may not cover all possible needs. matsbyname provides three functions for these situations:

Function Purpose
unaryapply_byname apply a unary function to a single matrix, a list of matrices, or a column of matrices in a data frame
elementapply_byname apply a unary function to a single element of a matrix or a column of matrices in a data frame
binaryapply_byname apply a binary function to two matrices or Map a binary function across two lists of matrices or two columns of matrices in a data frame
cumapply_byname apply a binary function cumulatively to a single list of matrices or a column of matrices in a data frame

How the *apply_byname functions work

The *apply_byname functions have several arguments.

Argument Description
FUN a unary function in the case of unaryapply_byname, a binary function (that may also accept a single argument) in the case of binaryapply_byname, and a binary function (that must also accept only a single argument) in the case of cumapply_byname
a a matrix, a list or matrices, or a column of matrices in a data frame
b a matrix, a list or matrices, or a column of matrices in a data frame
.FUNdots a named list of arguments to be passed to FUN
rowcoltypes tells what to do with row and column types
match_type tells how row and column types of a and b arguments must be matched
.organize tells whether to automatically complete a and b relative to each other and sort the rows and columns of the completed matrices

FUN is mapped as expected over a (in the case of unaryapply_byname, elementapply_byname, and cumapply_byname) or over a and b (in the case of binaryapply_byname). FUN should assume that its a and/or b arguments are single numbers or matrices; *apply_byname handles all mapping across lists. The following sections describe each *apply_byname function.

unaryapply_byname

unaryapply_byname applies FUN to a single matrix, a list of matrices, or (if used with dplyr::mutate()) a column in a data frame that contains matrices. The rowcoltypes argument must be one of the following:

rowcoltypes value Behaviour
"all" transfer both row and column types of directly to output (the default)
"transpose" rowtype of becomes coltype of output; coltype of becomes rowtype of output
"row" rowtype of becomes both rowtype and coltype of output
"col" coltype of becomes both rowtype and coltype of output
"none" rowtype and coltype not set by this function; will set rowtype and coltype

A simple example follows.

U <- matrix(1:4, ncol = 2, dimnames = list(c("p1", "p2"), c("i1", "i2"))) %>%
  setrowtype("Products") %>% setcoltype("Industries")
U
##    i1 i2
## p1  1  3
## p2  2  4
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"
difference_byname(0, U)
##    i1 i2
## p1 -1 -3
## p2 -2 -4
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"
unaryapply_byname(`-`, U)
##    i1 i2
## p1 -1 -3
## p2 -2 -4
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"

elementapply_byname

elementapply_byname applies FUN to a single matrix, a list of matrices, or (if used with dplyr::mutate()) a column in a data frame that contains matrices.

A simple example follows.

divide <- function(x, divisor){
  x/divisor
}
m <- matrix(c(1:4), nrow = 2, ncol = 2, dimnames = list(c("r1", "r2"), c("c1", "c2"))) %>% 
  setrowtype("row") %>% setcoltype("col")
m
##    c1 c2
## r1  1  3
## r2  2  4
## attr(,"rowtype")
## [1] "row"
## attr(,"coltype")
## [1] "col"
elementapply_byname(divide, a = m, row = 1, col = 1, .FUNdots = list(divisor = 2))
##     c1 c2
## r1 0.5  3
## r2 2.0  4
## attr(,"rowtype")
## [1] "row"
## attr(,"coltype")
## [1] "col"

binaryapply_byname

binaryapply_byname applies FUN to a pair of matrices, a pair of lists of matrices, or (if used with dplyr::mutate) a pair of columns in a data frame that contains matrices.

match_type must be one of "all", "matmult", or "none".

match_type value Behaviour
"all" rowtypes of a must match rowtypes of b and coltypes of a must match coltypes of b (the default)
"matmult" coltypes of a must match rowtypes of b
"none" neither coltypes nor rowtypes are checked

The rowcoltypes argument (a boolean) tells whether to apply row and column types from a and b to the output.

The .organize argument (a boolean) tells whether to automatically complete a and b relative to each other and sort the rows and columns of the completed matrices. Normally, this should be TRUE (the default).

A simple example follows.

U <- matrix(1:4, ncol = 2, dimnames = list(c("p1", "p2"), c("i1", "i2"))) %>%
  setrowtype("Products") %>% setcoltype("Industries")
U
##    i1 i2
## p1  1  3
## p2  2  4
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"
Y <- matrix(1:4, ncol = 2, dimnames = list(c("p2", "p1"), c("i2", "i1"))) %>%
  setrowtype("Products") %>% setcoltype("Industries")
Y
##    i2 i1
## p2  1  3
## p1  2  4
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"
sum_byname(U, Y)
##    i1 i2
## p1  5  5
## p2  5  5
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"
binaryapply_byname(`+`, U, Y)
##    i1 i2
## p1  5  5
## p2  5  5
## attr(,"rowtype")
## [1] "Products"
## attr(,"coltype")
## [1] "Industries"

cumapply_byname

cumapply_byname applies FUN cumulatively to a list of numbers, a list of matrices, or (if used with dplyr::mutate) a column of a data frame. FUN must be a binary function that also allows a single argument. The result is a list with first element FUN(m[[1]]). For i >= 2, elements of the resulting list are FUN(m[[i]], out[[i-1]]), where out is the result list.

Simple examples follow.

cumapply_byname(sum_byname, list(1, 2, 3, 4))
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 3
## 
## [[3]]
## [1] 6
## 
## [[4]]
## [1] 10
cumapply_byname(hadamardproduct_byname, list(1, 2, 3, 4))
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] 6
## 
## [[4]]
## [1] 24

Summary

The various *apply_byname functions allow users to extend the functionality of the matsbyname package as needed for any problem domain. The functions are used extensively in matsbyname itself. In fact, all matsbyname functions utilize the *apply_byname functions, so they are ready for prime time!