Возвращает только диагональные значения (ноль в других ячейках) из одной матрицы в другую матрицу с одинаковыми размерами

#excel #vba #matrix #covariance

#excel #vba #матрица #ковариация

Вопрос:

Я должен уменьшить значения в матрице дисперсии-ковариации в сторону дисперсии (диагональные значения в матрице) с коэффициентом сжатия (лямбда), так что: лямбда * матрица сжатия (1-лямбда) * матрица отклонения, где:

Ковариационная матрица дисперсии равна:

 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
  

это дает мне матрицу, которая выглядит, например, так:

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

Тогда у меня возникают проблемы с созданием матрицы усадки, которая должна выглядеть как:

 0.40  0.00  0.00                                      
0.00  0.17  0.00                                              
0.00  0.00  0.19 
  

т.е. возвращает ТОЛЬКО диагональные значения (= отклонения переменных) и ноль во всех остальных ячейках.

Таким образом, каким-то образом возвращая матрицу, содержащую только значения для when строка = номер столбца, т. Е. (1,1), (2,2) и (3,3) значения.

Кто-нибудь, кто может помочь с этим?

Комментарии:

1. не проще ли, если внутри вашего цикла у вас будет if, указывающий, что if i = <> j then .value = 0

2. @Jo.lass почему так сложно? Просто удалите j цикл и замените j на i .

3. @Jo.lass куда бы вы поместили это выражение if в кодировке?

4. @Pernille не делайте этого, это замедлит ваш код. Я исправил свой ответ, пожалуйста, взгляните.

Ответ №1:

Вам нужен только один цикл, который отсчитывается от i = 1 to 3 заполнения Matrix(1, 1) , Matrix(2, 2) и Matrix(3, 3) используя 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
  

Обратите внимание, что я изменил Matrix размеры Matrix(1 To MatrixDimension, 1 To MatrixDimension) , чтобы начать с 1 и не 0 , чтобы вы могли легко использовать это для записи в ячейки:

 Sub test()

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

End Sub
  

Комментарии:

1. Спасибо за вашу помощь! У меня все еще возникают проблемы с этим (извините, я совершенно новичок в VBA и не до конца понимаю все кодирование) У меня есть эта область, скажем, A3: F27, которая возвращает значения для разных переменных (6 переменных с 24 наблюдениями в каждой). Функция, которую вы только что написали, не работает как: VarCovar(A3: F27), поскольку она приходит с сообщением «не симметричная матрица». Затем я попытался переименовать вашу функцию в «VarCovarZeros» и попробовал: VarCovarZeros (VarCovar(A3: F27)) Где VarCovar — это «моя» исходная функция. Это возвращает #VALUE! , т. е. не работает. Как я могу это изменить?

2. Мне нужно преобразовать «Необработанные данные» в матрицу, которая дает только отклонения в диагонали и нулях в другом месте, или преобразовать «мою» матрицу VarCovar в матрицу, которая имеет (1,1), (2,2), (3,3) … и т.д. Значения в ней и нули в других местах. Имеет ли это смысл?

3. @Pernille А, кажется, я понял. Я изменил свой ответ, пожалуйста, попробуйте еще раз.