Skip to contents

Rounding matrices while preserving totals

A typical problem in official statistics is that of rounding a matrix whose columns are quarterly components of a total. In rounding, the rounded sum of the components for each quarter must be preserved and also the annual sum of each component.

Remark: To be able to round the matrix in this fashion, the following things must be equal:

  1. the sum of the differences between the row totals and the rounded row totals
  2. the sum of the differences between the column totals and the rounded row totals

Rounding a simple matrix

Consider the example matrix

example_matrix
#>           [,1]       [,2]        [,3]      [,4]      [,5]
#> [1,]  2.696060 17.2719552 -13.0920430 -10.48397  6.532067
#> [2,] -6.299854  0.2418764   7.3862193  17.27851 -3.685665
#> [3,]  8.686598  3.6802518   0.4487299 -11.78600 -5.995546

The round_matrix() function returns the following matrix.

rounded_matrix <- round_matrix(example_matrix)
rounded_matrix
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    3   17  -13  -11    7
#> [2,]   -6    0    7   18   -4
#> [3,]    8    4    1  -12   -6

And we may check that the rounded totals are preserved.

colSums(rounded_matrix) - round(colSums(example_matrix))
#> [1] 0 0 0 0 0
rowSums(rounded_matrix) - round(rowSums(example_matrix))
#> [1] 0 0 0

Sometimes we don’t need to preserve the rounded sum in both directions. The argument MARGIN works like that of the apply() function. If we just need to round the matrix and preserve the rounded sums of each column independently, we may do it by setting MARGIN = 2.

rounded_matrix <- round_matrix(example_matrix, MARGIN = 2)
rounded_matrix
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    3   17  -13  -10    7
#> [2,]   -6    0    7   17   -4
#> [3,]    8    4    1  -12   -6
colSums(rounded_matrix) - round(colSums(example_matrix))
#> [1] 0 0 0 0 0
rowSums(rounded_matrix) - round(rowSums(example_matrix))
#> [1]  1 -1  0

Rounding a matrix by blocks

Going back to the example of time series in official statistics, let us consider now a longer series, encompassing several years. We wish to apply round_matrix() to each year separately. This may be seen as applying the function to blocks

The round_by_blocks() function applies round_matrix() to each block. We have to indicate that the blocks are distributed vertically (layout = 2) and also that they 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).

The choice of the direction in which the rounded sums are preserved in each block is done with the argument MARGING_BLOCK, which is then passed to MARGIN in when round_matrix() is applied to the block.

example_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
X <- round_by_blocks(example_block_matrix, 2, 4)
U <- example_block_matrix[5:8,] |> round_matrix()
X[5:8,] - U
#>      [,1] [,2]
#> [1,]    0    0
#> [2,]    0    0
#> [3,]    0    0
#> [4,]    0    0

The function solves the problem independently for each block. The blocks can be distributed horizontally and analogous considerations apply.