Vectorize嵌套for循环Python

时间:2014-12-27 23:57:07

标签: python for-loop numpy nested vectorization

我有一个numpy数组,我正在迭代:

import numpy
import math
array = numpy.array([[1, 1, 2, 8, 2, 2],
               [5, 5, 4, 1, 3, 2],
               [5, 5, 4, 1, 3, 2],
               [5, 5, 4, 1, 3, 2],
               [9, 5, 8, 8, 2, 2],
               [7, 3, 6, 6, 2, 2]])


Pixels = ['U','D','R','L','UL','DL','UR','DR']

for i in range (1,array.shape[0]-1):
    for j in range (1,array.shape[1]-1):


         list = []
         while len(list) < 2:
                iToMakeList = i
                jToMakeList = j

                if iToMakeList > array.shape[0]-1 or iToMakeList < 1 or jToMakeList> array.shape[0]-1 or jToMakeList < 1:

                    break

                PixelCoord = {
            'U' : (iToMakeList-1,jToMakeList),
            'D' : (iToMakeList+1,jToMakeList),
            'R' : (iToMakeList,jToMakeList+1),
            'L' : (iToMakeList,jToMakeList-1),
            'UL' : (iToMakeList-1,jToMakeList-1),
            'DL' : (iToMakeList+1,jToMakeList-1),
            'UR' : (iToMakeList-1,jToMakeList+1),
            'DR' : (iToMakeList+1,jToMakeList+1)
                }
                Value = {
            'U' : array[iToMakeList-1][jToMakeList],
            'D' : array[iToMakeList+1][jToMakeList],
            'R' : array[iToMakeList][jToMakeList+1],
            'L' : array[iToMakeList][jToMakeList-1],
            'UL' : array[iToMakeList-1][jToMakeList-1],
            'DL' : array[iToMakeList+1][jToMakeList-1],
            'UR' : array[iToMakeList-1][jToMakeList+1],
            'DR' : array[iToMakeList+1][jToMakeList+1]
                }


                candidates = []
                for pixel in Pixels:
                    candidates.append((Value[pixel],pixel))

                Lightest = max(candidates)


                list.append(PixelCoord[Lightest[1]])

                iToMakeList = PixelCoord[Lightest[1]][0]
                jToMakeList = PixelCoord[Lightest[1]][1]

我想加快这个过程。这很慢。

假设此代码段的输出是我的最终目标,我想要做的唯一事情是加速此代码。

6 个答案:

答案 0 :(得分:2)

为了让您的问题有意义,我认为您需要移动list = []出现的位置。否则,在i=0已满之前,您永远不会达到j=1list。我无法想象它当前实现的速度很慢---列表将很快填满,然后for循环应该非常快。这就是我相信你的意图。请澄清这是否正确。

for i in range (0,array.shape[0]):
    for j in range (0,array.shape[1]):
         list = []
         while len(list) < 100:
                print "identity", i, j

                #find neighboring entry with greatest value (e.g., assume it is [i-1, j] with value 10)
                list.append((i-1,j))
                i = i-1
                j = j
         #perform operations on list

让我们做一些修改。我假设有一个函数get_max_nbr(i,j),它返回最大邻居的坐标。你的代码之一的地方之一就是它会多次调用get_max_nbr相同的坐标(在循环的每一步它都会执行100次)。下面的代码使用memoization来解决此问题(平均降至1次)。所以,如果这是你的瓶颈,那么你的速度应该接近100倍。

maxnbr = {}
for i in range(0,array.shape[0]):
    for j in range (0,array.shape[1]):
        list = []
        current_loc = (i,j)
        while len(list) < 100:
            if current_loc not in maxnbr:  #if this is our first time seeing current_loc
                maxnbr[current_loc] = get_max_nbr(*current_loc) #note func(*(i,j)) becomes func(i,j)
            current_loc = maxnbr[current_loc]
            list.append(current_loc)
        #process list here                 

这并没有成功地进行矢量化,但它确实创建了你想要的列表(我认为),它应该是一个重大的改进。可能如果我们对列表处理有了更多了解,我们可能会找到更好的方法,但目前尚不清楚。

答案 1 :(得分:1)

很简单,numpy允许对其数组进行逐元素操作,而不必遍历每个维度。

所以说你想在每个元素上应用一个简单的运算符,例如数字scalar multiplication 2,您可以执行以下任一操作:

array*2

np.multiply( array,2)

根据您在循环中所做的stuff的性质,您可以使用vectorization调整其他技术来执行元素操作。

答案 2 :(得分:1)

  • 您首先应该关注的是,您是否可以使用numpy的元素运算符来执行计算。
  • 如果这不起作用,请查看numpy中内置的通用函数(ufuncs)。

这两个都是在编译的C(或Fortran)中编码的,并且比在Python中循环更快 。此外,您的代码将更短,更容易理解。

可能提高性能的其他参数是使用哪个编译器来编译numpy以及使用哪个lineair代数库(假设您的代码使用线性代数)。例如。 ATLAS会自动调整它们所在的机器。英特尔销售的Fortran编译器和数学库应该在英特尔处理器上非常快。 IIRC,它们还可以并行化所有可用内核。

如果您的数学库不自动使用多个核心,则可以选择使用multiprocessing模块。假设问题可以并行化,这可以将运行时间(几乎)减少1 / N倍,其中N是核心数。当然,减去分配问题和收集结果所需的开销。

或者,对于可以并行化的问题,如果您有NVidia视频卡,可以使用pyCUDA numpy。

答案 3 :(得分:1)

如果你的目标是找到数组中的局部最大值,你可以使用scipy.ndimage.filters.maximum_filter和3×3窗口,然后检查是否相等:

import numpy
import scipy
import scipy.ndimage

arr = numpy.array([[1, 1, 2, 8],
                   [5, 5, 4, 1],
                   [9, 5, 8, 8],
                   [7, 3, 6, 6]])
maxima = zip(*(scipy.ndimage.filters.maximum_filter(arr, 3) == arr).nonzero())

这个速度在很大程度上取决于你是否真的只需要使用前100个以及有多少个最大值。如果是这样,提前爆发可能会更快。尽管如此,用你所做的真实内容完善你的问题将有助于我们获得更好的解决方案。

答案 4 :(得分:1)

添加已经很好的答案,这里是评论和快速版本,以获取列表中的所有内容:

import numpy as np
import scipy.ndimage as ndi

#Data generation
data=np.random.randint(100, size=(2000, 2000))
#Maximum extraction using a 3x3 kernel
b=ndi.filters.maximum_filter(data,3) 
#Getting the first 100 entries of b as a 1-D array
max_list=b.flatten()[0:99]

在我的测试中,这个代码大约需要0.2秒,包括我的Intel i7 CPU上的数据生成,大约3秒,当阵列的大小是20k * 2k时。时间似乎没有问题,因为我在执行时间明显上升之前遇到了内存问题。

尽管如此,您可以将完全相同的方法细分为较小的子阵列,以获得更大量的数据。请记住,在某些时候,数据处理将花费比计算本身更多的时间。

答案 5 :(得分:1)

所以这是我的平行方法。首先,我创建一个查找表,其中每个像素显示最近邻居的最大坐标。 对于我的intel i7双核cpu上的100 * 100矩阵,代码运行大约2秒。 到目前为止,代码没有经过优化,多处理内部的数据处理有点奇怪,可以肯定更容易。请告诉我,如果是这样,你想要什么。到目前为止,代码只会将数据点的坐标添加到列表中,如果您需要更改值,请在适当的位置进行更改,或者只解析生成的lines[]列表。

import numpy
import multiprocessing as mp
import time
start=time.time()
#Getting the number of CPUs present
num_cpu=mp.cpu_count()
#Creation of random data for testing
data=numpy.random.randint(1,30,size=(200,200))
x,y=data.shape
#Padding is introduced to cope with the border of the dataset.
#Change if you want other behaviour like wrapping, reflection etc.
def pad(data):
    '''Can be made faster, by using numpys pad function
    if present'''
    a=numpy.zeros((x+2,y+2))
    a[1:-1,1:-1]=data
    return a
data=pad(data)
#Kernel to get only the neighbours, change that if you only want diagonals or other shapes.
kernel=numpy.array([[1,1,1],[1,0,1],[1,1,1]])
result_list=[]  
#Setting up functions for Parallel Processing  
def log_result(result): 
    result_list.append(result) 
def max_getter(pixel):
    '''As this function is going to be used in a parallel processing environment,
    the data has to exist globally in order not to have to pickle it in the subprocess'''
    temp=data[pixel[0]-1:pixel[0]+2,pixel[1]-1:pixel[1]+2].copy()*kernel
    #Getting the submatrix without the central pixel
    compare=numpy.max(temp)==temp
    coords=numpy.nonzero(compare)
    if len(coords[0])!=1:
        coords=(coords[0][0],coords[1][0])
    #discards every maximum which is not the first. Change if you want.
    #Converting back to global coordinates
    return (pixel,(pixel[0]+(numpy.asscalar(coords[0])-1),pixel[1]+(numpy.asscalar(coords[1])-1)))
    #This assumes, that the maximum is unique in the subset, if this is not the case adjust here
def parallell_max():
    pool = mp.Pool() 
#You can experiment using more cores if you have hyperthreading and it's not correctly found by cpu_count
    for i in range(1,x+1):

        for j in range(1,y+1):

            pool.apply_async(max_getter, args = ((i,j),),callback=log_result) 
    pool.close()
    pool.join() 


#___________START Parallel Processing________
if __name__ == '__main__':
   # directions={}
    parallell_max()
    directions={}
    for i in result_list:
        directions[i[0]]=i[1]
    #Directions is a dictionary-like lookup-table, where every pixel gives the next pixel in the line
    lines=[]
    #The following code can also be easily parallelized as seen above.
    for i in range(1,x+1):
        for j in range(1,y+1):
            line=[]
            first,second=i,j
            for k in range(100):
                line.append((first,second))
                first,second=directions[(first,second)]
            lines.append(line)
    stop=time.time()
    print stop-start