Balancing a matrix
balance-matrix.Rmd
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\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 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.