Balancing a matrix (original) (raw)

This section is concerned with the problem of slightly adjusting a matrix so that its row and column totals add up to given vector usingbalance_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\times4\) matrix is composed of 4 vertical \(4\times 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 appliesbalance_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(), bothcol_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.