Balancing a matrix

Balancing matrices

This section is concerned with the problem of slightly adjusting a matrix so that its row and column totals add up to given vector using balance_matrix().

Remark: In order for the adjustment to be possible, the sum of those two vectors must be the same. This only makes sense, because both must equal the sum of the whole resulting matrix.

Balancing a simple matrix

Consider the example matrix

example_matrix
#>        [,1]   [,2]   [,3]   [,4]   [,5]
#> [1,] -0.897 -1.130  0.708 -0.139 -0.393
#> [2,]  0.185 -0.080 -0.240  0.418 -1.040
#> [3,]  1.588  0.132  1.984  0.982  1.782

and the desired row totals

row_totals
#> [1] -1.851  0.243  6.468

and column totals,

col_totals
#> [1]  0.87 -1.07  3.45  0.26  1.35

which are mildly different from those of the matrix.

colSums(example_matrix) - col_totals
#> [1]  0.006 -0.008 -0.998  1.001 -1.001
rowSums(example_matrix) - row_totals
#> [1]  0 -1  0

Let’s use our function to solve this problem.

tallied_matrix <- balance_matrix(example_matrix, col_totals, row_totals)
tallied_matrix - example_matrix
#>             [,1]   [,2]  [,3]       [,4]  [,5]
#> [1,] -0.06866667 -0.064 0.266 -0.4003333 0.267
#> [2,]  0.13133333  0.136 0.466 -0.2003333 0.467
#> [3,] -0.06866667 -0.064 0.266 -0.4003333 0.267
(rowSums(tallied_matrix) - row_totals) |> round(7)
#> [1] 0 0 0
(colSums(tallied_matrix) - col_totals) |> round(7)
#> [1] 0 0 0 0 0

We don’t need to provide both the row and column totals. If only the column totals (or rows) are provided, the tallying is done to match only those.

tallied_matrix <- balance_matrix(example_matrix, col_totals)
tallied_matrix - example_matrix
#>        [,1]       [,2]      [,3]       [,4]      [,5]
#> [1,] -0.002 0.00266667 0.3326667 -0.3336667 0.3336667
#> [2,] -0.002 0.00266667 0.3326667 -0.3336667 0.3336667
#> [3,] -0.002 0.00266667 0.3326667 -0.3336667 0.3336667
(rowSums(tallied_matrix) - row_totals) |> round(7)
#> [1]  0.3333333 -0.6666667  0.3333333
(colSums(tallied_matrix) - col_totals) |> round(7)
#> [1] 0 0 0 0 0

Balancing a matrix by blocks

Sometimes one may need to balance a matrix that is made up of blocks. For example, suppose that the following 16 × 4 matrix is composed of 4 vertical 4 × 2 blocks.

block_matrix
#>          [,1]    [,2]
#>  [1,]   0.187  -9.549
#>  [2,]  -1.843  -1.952
#>  [3,] -13.713   9.255
#>  [4,]  -5.992   4.830
#>  [5,]   2.945  -5.963
#>  [6,]   3.898 -21.853
#>  [7,] -12.081  -6.749
#>  [8,]  -3.637 -21.191
#>  [9,] -16.267 -12.652
#> [10,]  -2.565  -3.737
#> [11,]  11.018  -6.876
#> [12,]   7.558  -8.722
#> [13,]  -2.382  -1.018
#> [14,]   9.874  -2.538
#> [15,]   7.414 -18.537
#> [16,]   0.893  -0.779

And we have the following matrix whose rows are the desired column totals for each of the blocks.

block_col_totals
#>      [,1] [,2]
#> [1,]  -21    3
#> [2,]   -9  -56
#> [3,]    0  -32
#> [4,]   16  -23

The balance_by_blocks() function applies balance_matrix() to each block using the totals given by the argument col_totals. When the blocks are distributed vertically (layout = 2), this argument must be a matrix as wide as the matrix to be balanced (Y), and with a row for each block. We have to indicate also that the bloks are 4 rows long (L = 4). Blocks are assumed to be as wide as the matrix (or as tall as the matrix if distributed horizontally).

X <- balance_by_blocks(block_matrix, col_totals = block_col_totals, 
                       layout = 2, L = 4)
X[9:12,] - balance_matrix(block_matrix[9:12,], block_col_totals[3,])
#>      [,1] [,2]
#> [1,]    0    0
#> [2,]    0    0
#> [3,]    0    0
#> [4,]    0    0

Just as with balance_matrix(), both col_totals and row_totals can be provided. In the case of vertically distributed blocks, row_totals is a vector with an entry for each row of the Y matrix. The function solves the problem independently for each block.

The blocks can be distributed horizontally and analogous considerations apply.