Python矢量化嵌套for循环

时间:2016-09-23 18:10:01

标签: python numpy for-loop optimization vectorization

我非常感谢在查找和理解嵌套for循环中优化以下数组操作的pythonic方法方面的一些帮助:

roi.shape

其中ndarray(182,218,200)和radius(3,)都是int类型; namespace :admin do resources :invoices, only: [:index, :new] end new_admin_invoice GET /admin/invoices/new(.:format) admin/invoices#new

2 个答案:

答案 0 :(得分:54)

方法#1

这是一种矢量化方法 -

m,n,r = volume.shape
x,y,z = np.mgrid[0:m,0:n,0:r]
X = x - roi[0]
Y = y - roi[1]
Z = z - roi[2]
mask = X**2 + Y**2 + Z**2 < radius**2

可能的改进:我们可以用numexpr模块加速最后一步 -

import numexpr as ne

mask = ne.evaluate('X**2 + Y**2 + Z**2 < radius**2')

方法#2

我们还可以逐渐构建与形状参数相对应的三个范围,并在运行时对roi的三个元素执行减法,而不像np.mgrid之前那样实际创建网格。为了提高效率,使用broadcasting会使这一点受益。实现看起来像这样 -

m,n,r = volume.shape
vals = ((np.arange(m)-roi[0])**2)[:,None,None] + \
       ((np.arange(n)-roi[1])**2)[:,None] + ((np.arange(r)-roi[2])**2)
mask = vals < radius**2

简化版:感谢@Bi Rico建议改进,因为我们可以使用np.ogrid以更简洁的方式执行这些操作,如此 -

m,n,r = volume.shape    
x,y,z = np.ogrid[0:m,0:n,0:r]-roi
mask = (x**2+y**2+z**2) < radius**2

运行时测试

功能定义 -

def vectorized_app1(volume, roi, radius):
    m,n,r = volume.shape
    x,y,z = np.mgrid[0:m,0:n,0:r]
    X = x - roi[0]
    Y = y - roi[1]
    Z = z - roi[2]
    return X**2 + Y**2 + Z**2 < radius**2

def vectorized_app1_improved(volume, roi, radius):
    m,n,r = volume.shape
    x,y,z = np.mgrid[0:m,0:n,0:r]
    X = x - roi[0]
    Y = y - roi[1]
    Z = z - roi[2]
    return ne.evaluate('X**2 + Y**2 + Z**2 < radius**2')

def vectorized_app2(volume, roi, radius):
    m,n,r = volume.shape
    vals = ((np.arange(m)-roi[0])**2)[:,None,None] + \
           ((np.arange(n)-roi[1])**2)[:,None] + ((np.arange(r)-roi[2])**2)
    return vals < radius**2

def vectorized_app2_simplified(volume, roi, radius):
    m,n,r = volume.shape    
    x,y,z = np.ogrid[0:m,0:n,0:r]-roi
    return (x**2+y**2+z**2) < radius**2

计时 -

In [106]: # Setup input arrays  
     ...: volume = np.random.rand(90,110,100) # Half of original input sizes 
     ...: roi = np.random.rand(3)
     ...: radius = 3.4
     ...: 

In [107]: %timeit _make_mask(volume, roi, radius)
1 loops, best of 3: 41.4 s per loop

In [108]: %timeit vectorized_app1(volume, roi, radius)
10 loops, best of 3: 62.3 ms per loop

In [109]: %timeit vectorized_app1_improved(volume, roi, radius)
10 loops, best of 3: 47 ms per loop

In [110]: %timeit vectorized_app2(volume, roi, radius)
100 loops, best of 3: 4.26 ms per loop

In [139]: %timeit vectorized_app2_simplified(volume, roi, radius)
100 loops, best of 3: 4.36 ms per loop

所以,像往常一样broadcasting显示其对原始代码几乎 10,000x 疯狂的魔力并超过 10x 比通过使用动态广播操作创建网格更好!

答案 1 :(得分:7)

假设您首先构建一个SELECT ... FOR UPDATE数组:

xyzy

现在,使用numpy.linalg.norm

import itertools

xyz = [np.array(p) for p in itertools.product(range(volume.shape[0]), range(volume.shape[1]), range(volume.shape[2]))]

检查np.linalg.norm(xyz - roi, axis=1) < radius 中每个元组的距离是否小于半径。

最后,只需roi结果即可获得所需尺寸。