Search code examples
excelvbamatrixcovariance

Return only diagonal values (zero in the other cells) in one matrix to another matrix with same dimensions


I have to shrink values in the variance-covariance matrix towards the variance (diagonal values in matrix) by a shrinkage factor (lambda), so: lambda*shrinkagematrix+(1-lambda)*variancecovariancematrix, where:

Variance covariance matrix is:

Function VarCovar(rng As Range) As Variant
    Dim i As Integer
    Dim j As Integer
    Dim numcols As Integer

    numcols = rng.Columns.Count
    numrows = rng.Rows.Count

    Dim matrix() As Double
    ReDim matrix(numcols - 1, numcols - 1)

    For i = 1 To numcols
        For j = 1 To numcols
            matrix(i - 1, j - 1) = Application.WorksheetFunction.Covar(rng.Columns(i), rng.Columns(j)) * numrows / (numrows - 1)
        Next j
    Next i
    VarCovar = matrix

, this gives me a matrix that looks for example like this:

 0.40  -0.10  0.11                                                 
-0.10   0.17 -0.03                                              
 0.11  -0.03  0.19 

Then I have trouble creating the Shrinkage matrix which should look like:

0.40  0.00  0.00                                      
0.00  0.17  0.00                                              
0.00  0.00  0.19 

i.e returning ONLY diagonal values (= variances of the variables) and zero in all other cells.

So in some kind of way, making it return a matrix containing only the values for when row=column number, i.e. (1,1), (2,2) and (3,3) values.

Anyone that can help with this?


Solution

  • You need only one loop that counts from i = 1 to 3 to fill Matrix(1, 1), Matrix(2, 2) and Matrix(3, 3) using Matrix(i, i)

    Function VarCovar(InputMatix As Range) As Variant
        Dim MatrixColumns  As Long
        MatrixColumns = InputMatix.Columns.Count
    
        Dim MatrixRows  As Long
        MatrixRows = InputMatix.Rows.Count
    
        Dim Matrix() As Double
        ReDim Matrix(1 To MatrixColumns, 1 To MatrixColumns)
    
        Dim i As Long
        For i = 1 To MatrixColumns
            Matrix(i, i) = Application.WorksheetFunction.Covar(InputMatix.Columns(i), InputMatix.Columns(i)) * MatrixRows / (MatrixRows - 1)
        Next i
    
        VarCovar = Matrix
    End Function
    

    Note that I changed the Matrix dimenstions Matrix(1 To MatrixDimension, 1 To MatrixDimension) to start with 1 and not 0 so you can easily use it to write it to cells:

    Sub test()
    
        Range("A5:C7").Value = VarCovar(Range("A1:C3"))
    
    End Sub