【问题标题】:Better method to calculate half of symmetrical numpy matrix?计算对称numpy矩阵一半的更好方法?
【发布时间】:2015-08-03 07:50:30
【问题描述】:

我的矩阵的每个单元格都需要是由一个昂贵的函数计算的分数。矩阵是对称的,这是我能想到的填充每个单元格的最佳方法。

num_cases = len(case_dictionary.keys())  # num_cases = 10
SmallMatrix = np.zeros((num_cases,num_cases))

for CasesX in range(0,num_cases):
    for CasesY in range(CasesX,num_cases):
        SmallMatrix[CasesX,CasesY] = 1

返回:

array([[ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

很简单...

但是,当 Matrix 较大且计算量较大时: 嵌套的 for 循环是最有效的解决方案吗?

num_cases = len(case_dictionary.keys())  # 100000
BigMatrix = np.zeros((num_cases,num_cases))

for CasesX in range(0,num_cases):
    for CasesY in range(CasesX,num_cases):
        BigMatrix[CasesX,CasesY] = ExpensiveFunction()

慢...由于我的功能或循环?

编辑

不断地处理成对数据,所以我回去尝试使用@hpaulj 解决方案。我没有足够的知识来理解为什么 testUpper() 更快?

def testUpper(func):
    num_cases = 100
    BigMatrix = np.zeros((num_cases,num_cases))

    upper = np.triu_indices_from(BigMatrix)

    BigMatrix[upper] = ExpensiveFunction()

从下面对@unutbu test 函数进行基准测试,对比 numpy 版本:

In [8]: %timeit test(ExpensiveFunction)
        1 loops, best of 3: 11.1 s per loop

In [9]: %timeit testUpper(ExpensiveFunction)
        1000 loops, best of 3: 2.03 ms per loop

【问题讨论】:

  • 很难说没有更多细节是什么让代码变慢了。 Python 循环很慢,但它可能不是瓶颈。话虽如此,slow 对你来说是什么?毫秒、秒、分钟……?你如何评价ExpensiveFunction()?它是否有任何参数,或者任何条目都是相同的函数调用?
  • 你的功能是什么?
  • ExpensiveFunction 有 5 个参数(3 个在循环之外定义,2 个以 CasesX 和 CasesY 为条件),它通过评估 R 代码(使用 python 包 rpy2)来计算相似度分数。慢,是相对的,同意,问题更多是关于嵌套 for 循环的效率,作为消除对称矩阵中一半计算的最佳方法。不知道 pandas 或 numpy 是否有一个巧妙的电话可以为您处理这个问题。
  • 在 NumPy 中,您会尝试将 ExpensiveFunction 向量化并只调用一次。在实践中,这通常比通过每次调用一次函数来避免一半调用要快,因为迭代和索引在 Python 中很慢,但在本机代码中却很快(即矢量化代码在引擎盖下迭代)。您也许可以在 R 中进行矢量化。
  • ExpensiveFunction(x) 是否仅取决于值x,还是也取决于[i,j] 坐标。例如,它能否应用于阵列的扁平化版本?有一些工具可以索引数组的三角形部分。

标签: python numpy optimization matrix


【解决方案1】:

这是一个简单的实验,表明瓶颈更有可能是ExpensiveFunction

import time

def SimpleFunction():
    return 1

def ExpensiveFunction():
    time.sleep(0.001)
    return 1

def test(func):
    num_cases = 100
    BigMatrix = np.zeros((num_cases,num_cases))

    for CasesX in range(0,num_cases):
        for CasesY in range(CasesX,num_cases):
            BigMatrix[CasesX,CasesY] = func()

In [84]: %timeit test(ExpensiveFunction)
1 loops, best of 3: 5.48 s per loop

In [85]: %timeit test(SimpleFunction)
1000 loops, best of 3: 890 µs per loop

除了被调用的函数之外,两次 timeit 运行是相同的。 当funcSimpleFunction 时,填充BigMatrix 需要不到1ms。 但是当funcExpensiveFunction 时,填充BigMatrix 需要5s。

所以双重for-loop 可能不是瓶颈; ExpensiveFunction 是。您可以使用您的实际代码进行尝试以确保。如果确实证明ExpensiveFunction 是瓶颈,那么您无需费心优化双循环,因为即使有更快的方法来填充BigMatrix - 即使您可以将时间成本降低到零-- 您(在上述情况下)最多只能保存890 us,而整个程序仍然需要 5 秒以上。

【讨论】:

  • 谢谢,这确实说明循环不是问题。函数调用是瓶颈,让我相信@MrE 可能通过矢量化我的 R 代码获得迄今为止最好的解决方案。我会尽快投票(n00b,sry)
【解决方案2】:

我建议在矩阵的一半上应用您的“昂贵”计算,而不是使用 symmetrize() 函数使您的 numpy 数组对称,这应该具有最小的时间成本

def symmetrize(a):
    return a + a.T - numpy.diag(a.diagonal())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-03
    • 1970-01-01
    • 1970-01-01
    • 2013-07-29
    • 2016-06-27
    • 2011-05-21
    • 2021-11-08
    相关资源
    最近更新 更多