我可以矢量化这个python代码吗?

时间:2014-10-08 08:06:05

标签: python optimization numpy

我编写了这个python代码来获取标签的邻居(一组像素共享一些常见属性)。标签的邻居被定义为位于边界另一侧的其他标签(相邻标签共享边界)。所以,我写的代码有效,但速度极慢:

# segments: It is a 2-dimensional numpy array (an image really)
# where segments[x, y] = label_index. So each entry defines the
# label associated with a pixel.

# i: The label whose neighbours we want.

def get_boundaries(segments, i):
    neighbors = []
    for y in range(1, segments.shape[1]):
        for x in range(1, segments.shape[0]):
            # Check if current index has the label we want 
            if segments[x-1, y] == i:
                # Check if neighbour in the x direction has
                # a different label
                if segments[x-1, y] != segments[x, y]:
                    neighbors.append(segments[x,y])

            # Check if neighbour in the y direction has
            # a different label
            if segments[x, y-1] == i:
                if segments[x, y-1] != segments[x, y]:
                    neighbors.append(segments[x, y])

    return np.unique(np.asarray(neighbors))

你可以想象,我可能在这里完全滥用了python。我想知道是否有办法优化此代码以使其更加pythonic。

2 个答案:

答案 0 :(得分:4)

你走了:

def get_boundaries2(segments, i):
    x, y = np.where(segments == i) # where i is
    right = x + 1
    rightMask = right < segments.shape[0] # keep in bounds
    down = y + 1
    downMask = down < segments.shape[1]
    rightNeighbors = segments[right[rightMask], y[rightMask]]
    downNeighbors = segments[x[downMask], down[downMask]]
    neighbors = np.union1d(rightNeighbors, downNeighbors)
    return neighbors

如您所见,根本没有Python循环;我还试图最小化副本(第一次尝试制作了带有NAN边界的segments副本,但后来我设计了“保持边界”检查。)

请注意,我没有从这里的“邻居”中过滤掉i本身;如果你愿意,你可以在最后轻松添加。一些时间:

输入2000x3000:原始需要13秒,我需要370毫秒(35倍加速)。

输入1000x300:原始需要643毫秒,我的需要17.5毫秒(36倍加速)。

答案 1 :(得分:0)

您需要使用numpy的隐式循环替换for循环。

我不太了解您的代码以直接转换它,但我可以举个例子。

假设你有一个包含100000个随机整数的数组,你需要得到每个元素的数组除以它的邻居。

import random, numpy as np 
a = np.fromiter((random.randint(1, 100) for i in range(100000)), int)

这样做的一种方法是:

[a[i] / a[i+1] for i in range(len(a)-1)]

或者这个,速度要快得多:

a / np.roll(a, -1)

Timeit:

initcode = 'import random, numpy as np; a = np.fromiter((random.randint(1, 100) for i in range(100000)), int)'
timeit.timeit('[a[i] / a[i+1] for i in range(len(a)-1)]', initcode, number=100)
5.822079309000401
timeit.timeit('(a / np.roll(a, -1))', initcode, number=100)
0.1392055350006558