Python 如何计算最近的半正定矩阵?

Python 如何计算最近的半正定矩阵?,python,matrix,numpy,Python,Matrix,Numpy,我将从R开始学习Python,并尝试重现我在R中使用Python所做的许多事情。R的矩阵库有一个非常漂亮的函数,名为nearPD(),它可以找到与给定矩阵最近的半正定(PSD)矩阵。虽然我可以编写一些东西,但作为Python/Numpy的新手,如果已经有了一些东西,我对重新发明轮子并不感到太兴奋。关于Python中现有实现的任何提示?我认为没有一个库可以返回您想要的矩阵,但这里有一个Higham(2000)提供的“只是为了好玩”的近正定半正定矩阵算法编码 当对论文中的示例进行测试时,它会返回正确

我将从R开始学习Python,并尝试重现我在R中使用Python所做的许多事情。R的矩阵库有一个非常漂亮的函数,名为
nearPD()
,它可以找到与给定矩阵最近的半正定(PSD)矩阵。虽然我可以编写一些东西,但作为Python/Numpy的新手,如果已经有了一些东西,我对重新发明轮子并不感到太兴奋。关于Python中现有实现的任何提示?

我认为没有一个库可以返回您想要的矩阵,但这里有一个Higham(2000)提供的“只是为了好玩”的近正定半正定矩阵算法编码

当对论文中的示例进行测试时,它会返回正确答案

print nearPD(np.matrix([[2,-1,0,0],[-1,2,-1,0],[0,-1,2,-1],[0,0,-1,2]]),nit=10)
[[ 1.         -0.80842467  0.19157533  0.10677227]
 [-0.80842467  1.         -0.65626745  0.19157533]
 [ 0.19157533 -0.65626745  1.         -0.80842467]
 [ 0.10677227  0.19157533 -0.80842467  1.        ]]

我将提交一个非迭代方法。这是从(第7-9页)稍微修改的。迭代方法可能需要很长时间来处理几百个变量以上的矩阵

import numpy as np

def nearPSD(A,epsilon=0):
   n = A.shape[0]
   eigval, eigvec = np.linalg.eig(A)
   val = np.matrix(np.maximum(eigval,epsilon))
   vec = np.matrix(eigvec)
   T = 1/(np.multiply(vec,vec) * val.T)
   T = np.matrix(np.sqrt(np.diag(np.array(T).reshape((n)) )))
   B = T * vec * np.diag(np.array(np.sqrt(val)).reshape((n)))
   out = B*B.T
   return(out)

代码是在R.

< P>中围绕非PD/PSD矩阵讨论的,这可能是对DouPaZZ的一个愚蠢的扩展,它考虑了相关矩阵和协方差矩阵。如果处理大量矩阵,它也会提前终止

def near_psd(x, epsilon=0):
    '''
    Calculates the nearest postive semi-definite matrix for a correlation/covariance matrix

    Parameters
    ----------
    x : array_like
      Covariance/correlation matrix
    epsilon : float
      Eigenvalue limit (usually set to zero to ensure positive definiteness)

    Returns
    -------
    near_cov : array_like
      closest positive definite covariance/correlation matrix

    Notes
    -----
    Document source
    http://www.quarchome.org/correlationmatrix.pdf

    '''

    if min(np.linalg.eigvals(x)) > epsilon:
        return x

    # Removing scaling factor of covariance matrix
    n = x.shape[0]
    var_list = np.array([np.sqrt(x[i,i]) for i in xrange(n)])
    y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in xrange(n)] for j in xrange(n)])

    # getting the nearest correlation matrix
    eigval, eigvec = np.linalg.eig(y)
    val = np.matrix(np.maximum(eigval, epsilon))
    vec = np.matrix(eigvec)
    T = 1/(np.multiply(vec, vec) * val.T)
    T = np.matrix(np.sqrt(np.diag(np.array(T).reshape((n)) )))
    B = T * vec * np.diag(np.array(np.sqrt(val)).reshape((n)))
    near_corr = B*B.T    

    # returning the scaling factors
    near_cov = np.array([[near_corr[i, j]*(var_list[i]*var_list[j]) for i in xrange(n)] for j in xrange(n)])
    return near_cov

我知道这个线程很旧,但是这里提供的解决方案对于我的协方差矩阵来说并不令人满意:变换后的矩阵看起来总是与原始矩阵有很大的不同(至少对于我测试的情况)。因此,我在这里留下一个非常简单的答案,基于中提供的解决方案:

将numpy导入为np
def get_近屏蔽门(A):
C=(A+A.T)/2
eigval,eigvec=np.linalg.eig(C)
eigval[eigval<0]=0
返回eigvec.dot(np.diag(eigval)).dot(eigvec.T)
想法很简单:我计算对称矩阵,然后进行特征值分解,得到特征值和特征向量。我把所有负特征值归零,然后构造出矩阵,现在它是半正定的

为了完整性,我留下了一个简单的代码,用numpy检查矩阵是否为半正定矩阵(基本上检查所有特征值是否为非负):

def是_pos_semidef(x):
返回np.all(np.linalg.eigvals(x)>=0)

您是否查看了以下问题:答案显示numpy的PSD数学。确认。。。我正在搜索python标记,但它没有。。。我会看,如果多余,我会拉我的问题,并添加Python标记到那个问题。我看了那个问题,他们只是强迫矩阵进入PSD,没有考虑“最近的”。相比之下,以下是nearPD()所基于的Higham论文:scipy函数不做必要的转换吗?@Dirria这是对这个问题的一个很好的回答,您应该将其作为一个答案,而不是一个注释。最初没有人回答
cov_nearest
corr_nearest
的原因是这些函数直到2012年8月才添加,我在2012年6月问了这个问题。嘿,这太棒了!谢谢,伙计,我希望更多的人认为这类东西“只是为了好玩”。但说真的,这是很酷的东西。令人惊叹的。也许考虑把它提交给ScPy或者什么?你的输入矩阵(<代码> [2,-1,0]…] /代码>已经是正定的。你的输出矩阵的Frobenius范数是2.35。我不确定到底发生了什么,但我将Higham 1988年论文的Matlab实现移植到Python中,我只在这里留下参考:上面答案中的代码似乎基于Higham 2000,其目标是计算最近的相关矩阵(即,强制单位对角)。这比最近对称半正定矩阵要严格得多,它是Higham 1988的主题,@AhmedFasih的代码基于它。这可能是一个愚蠢的问题,但你能转换回,即从PSD到原始矩阵吗?不,你已经从根本上改变了矩阵。如果您需要原件,只需确保保留一份副本(当然,内存允许)。此代码似乎是错误的。仅用一个简单的正定对角矩阵a=np进行测试。diag([1,2,3])输出一个不同的矩阵,即恒等式。
def near_psd(x, epsilon=0):
    '''
    Calculates the nearest postive semi-definite matrix for a correlation/covariance matrix

    Parameters
    ----------
    x : array_like
      Covariance/correlation matrix
    epsilon : float
      Eigenvalue limit (usually set to zero to ensure positive definiteness)

    Returns
    -------
    near_cov : array_like
      closest positive definite covariance/correlation matrix

    Notes
    -----
    Document source
    http://www.quarchome.org/correlationmatrix.pdf

    '''

    if min(np.linalg.eigvals(x)) > epsilon:
        return x

    # Removing scaling factor of covariance matrix
    n = x.shape[0]
    var_list = np.array([np.sqrt(x[i,i]) for i in xrange(n)])
    y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in xrange(n)] for j in xrange(n)])

    # getting the nearest correlation matrix
    eigval, eigvec = np.linalg.eig(y)
    val = np.matrix(np.maximum(eigval, epsilon))
    vec = np.matrix(eigvec)
    T = 1/(np.multiply(vec, vec) * val.T)
    T = np.matrix(np.sqrt(np.diag(np.array(T).reshape((n)) )))
    B = T * vec * np.diag(np.array(np.sqrt(val)).reshape((n)))
    near_corr = B*B.T    

    # returning the scaling factors
    near_cov = np.array([[near_corr[i, j]*(var_list[i]*var_list[j]) for i in xrange(n)] for j in xrange(n)])
    return near_cov