用矢量化双重求和的Pythonic方法

时间:2019-03-28 16:38:58

标签: python numpy matrix vectorization

我正在尝试将双重求和公式转换为代码,但是无法找出正确的矩阵/矢量表示形式。

第一个求和是i到n,第二个求和是j> i到n。

我猜想有一种更高效,更Python化的方式来编写此代码?

我求助于嵌套循环以使其正常运行,但是,正如预期的那样,它在大型数据集下运行非常缓慢:

def wapc_denom(weights, vols):
    x = []
    y = []

    for i, wi in enumerate(weights):
        for j, wj in enumerate(weights):
            if j > i:
                x.append(wi * wj * vols[i] * vols[j])
        y.append(np.sum(x))

    return np.sum(y)

编辑:

使用smci答案中的指导,我认为我有一个潜在的解决方案:

def wapc_denom2(weights, vols):
    return np.sum(np.tril(np.outer(weights, vols.T)**2, k=-1))

3 个答案:

答案 0 :(得分:3)

假设您只希望对每一项进行计数(为此您必须将x = []移到外循环中),那么一种便宜的计算总和的方法就是

创建模拟数据

weights = np.random.random(10)
vols = np.random.random(10)

进行计算

wv = weights * vols
result = (wv.sum()**2 - wv@wv) / 2

检查是否相同

def wapc_denom(weights, vols):
    y = []

    for i, wi in enumerate(weights):
        x = []
        for j, wj in enumerate(weights):
            if j > i:
                x.append(wi * wj * vols[i] * vols[j])
        y.append(np.sum(x))

    return np.sum(y)

assert np.allclose(result, wapc_denom(weights, vols))

为什么起作用?

我们正在做的是计算完整矩阵的总和,减去对角线并除以2。这很便宜,因为很容易验证外部乘积的总和是否是总和的乘积。

答案 1 :(得分:1)

  • wi * wj * vols[i] * vols[j]是一个故事。 vols是另一个向量,因此首先要计算向量wv = w * vols
  • 然后(wj * vols[j]) * (wi * vols[i]) = wv^T * wv是您的(矩阵外部乘积)表达式;那是列向量*行向量。但是实际上,您只想要总和。因此,我认为无需构造向量y.append(np.sum(x)),您只需要对向量np.sum(y)进行求和
  • if j > i部分也意味着您只需要下三角部分的总和,而不包括对角线。
    • 编辑:结果完全由wv决定,我认为我们不需要矩阵来获得总和,也不需要对角线; @PaulPanzer找到了最紧凑的表达方式。

答案 2 :(得分:0)

您可以在numpy中使用三角剖分,请选中np.triunp.meshgrid。做:

np.product(np.triu(np.meshgrid(weights,weights), 1) * np.triu(np.meshgrid(vols,vols), 1),0).sum(1).cumsum().sum()

示例:

w = np.arange(4) +1
v = np.array([1,3,2,2])
print(np.triu(np.meshgrid(w,w), k=1))
>>array([[[0, 2, 3, 4],
        [0, 0, 3, 4],
        [0, 0, 0, 4],
        [0, 0, 0, 0]],

       [[0, 1, 1, 1],
        [0, 0, 2, 2],
        [0, 0, 0, 3],
        [0, 0, 0, 0]]])

# example of product + triu + meshgrid (your x values):
print(np.product(np.triu(np.meshgrid(w,w), 1) * np.triu(np.meshgrid(v,v), 1),0))
>>array([[ 0,  6,  6,  8],
   [ 0,  0, 36, 48],
   [ 0,  0,  0, 48],
   [ 0,  0,  0,  0]])

print(np.product(np.triu(np.meshgrid(w,w), 1) * np.triu(np.meshgrid(v,v), 1),0).sum(1).cumsum().sum())
>> 428
print(wapc_denom(w, v))
>> 428