Expanding a list of numbers into a matrix (list with n values to multiply to a n x n matrix)

Viewed 69

I have a set of numbers, which I want to expand into a matrix. There are 4 values in the list which I want to expand into a 4x4 matrix. Here is some example data

freq <- c(627,449,813,111)  

I want to expand this into a matrix of so that it's like this. Apologies I have just copied and pasted data, thus it's not an R output, but hope it helps to get the idea across.

    1   2   3   4   Total
1   197 141 255 35  627
2   141 101 183 25  449
3   255 183 330 45  813
4   35  25  45  6   111
    627 449 813 111 2000

The cells are multiplication of the (row total)x(column total)/(table total). The value in 1,1 = (627 x 627)/2000 = 197. The value in 2,1 = (627 x 449)/2000 = 141, and so on.

Is there a function that will create this matrix? I will try to do it via a loop but was hoping there is a function or matrix calculation trick that can do this more efficiently? Apologies if I didn't articulate the above too well, any help is greatly appreciated. Thanks

2 Answers
freq <- c(627,449,813,111) 
round(outer(freq, freq)/sum(freq))
#>      [,1] [,2] [,3] [,4]
#> [1,]  197  141  255   35
#> [2,]  141  101  183   25
#> [3,]  255  183  330   45
#> [4,]   35   25   45    6

It doesn't really matter here, but it is good practice to avoid constructions like outer(x, x) / sum(x) in favour of ones like tcrossprod(x / sqrt(sum(x))):

round(tcrossprod(freq / sqrt(sum(freq))))
##      [,1] [,2] [,3] [,4]
## [1,]  197  141  255   35
## [2,]  141  101  183   25
## [3,]  255  183  330   45
## [4,]   35   25   45    6

There are a few issues with the outer approach:

  • outer(x, x) evaluates tcrossprod(as.vector(x), as.vector(x)) internally. The as.vector calls and everything else that happens inside of outer are completely redundant if x is already a vector. The as.vector calls are actually worse than redundant: if x has any attributes, then as.vector(x) requires a deep copy of x.
  • Naively doing A <- outer(x, x); A / sum(x) requires R to allocate memory for two n-by-n matrices. For large enough n, that can be quite wasteful, if not impossible. R is clever enough to avoid the second allocation if you compute outer(x, x) / sum(x) directly. However, such optimizations are low level, come with a number of gotchas, and are not even documented in ?Arithmetic, so it can be unsafe to rely on them.
  • outer(x, x) can result in underflow or overflow if the elements of x are very (very) small or large.

tcrossprod(x / sqrt(sum(x))) avoids all of these issues by scaling x before computing an outer product and cutting out all of the redundancies of outer.

Related