如何在Python/Numpy中矢量化简单的for循环

如何在Python/Numpy中矢量化简单的for循环,python,numpy,scipy,vectorization,Python,Numpy,Scipy,Vectorization,我在Python/NumPy中找到了几十个如何对循环进行矢量化的示例。不幸的是,我不知道如何使用矢量化形式来减少简单for循环的计算时间。在这种情况下可能吗 time = np.zeros(185000) lat1 = np.array(([48.78,47.45],[38.56,39.53],...)) # ~ 200000 rows lat2 = np.array(([7.78,5.45],[7.56,5.53],...)) # same number of rows as time for

我在Python/NumPy中找到了几十个如何对循环进行矢量化的示例。不幸的是,我不知道如何使用矢量化形式来减少简单for循环的计算时间。在这种情况下可能吗

time = np.zeros(185000)
lat1 = np.array(([48.78,47.45],[38.56,39.53],...)) # ~ 200000 rows
lat2 = np.array(([7.78,5.45],[7.56,5.53],...)) # same number of rows as time
for ii in np.arange(len(time)):
    pos = np.argwhere( (lat1[:,0]==lat2[ii,0]) and \
                       (lat1[:,1]==lat2[ii,1]) )
    if pos.size:
        pos = int(pos)
        time[ii] = dtime[pos]

这里有一个解决方案。我不确定是否有可能将其矢量化。如果您想使其抵抗“浮点比较错误”,您应该修改
小于
大于
。 整个算法只是一个二进制搜索

import numpy as np

#lexicographicaly compare two points - a and b

def is_less(a, b):
    i = 0
    while i<len(a):
        if a[i]<b[i]:
            return True
        else:
            if a[i]>b[i]:
                return False
        i+=1
    return False

def is_greater(a, b):
    i = 0
    while i<len(a):
        if a[i]>b[i]:
            return True
        else:
            if a[i]<b[i]:
                return False
        i+=1
    return False


def binary_search(a, x, lo=0, hi=None):
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        midval = a[mid]
        if is_less(midval, x):
            lo = mid+1
        elif is_greater(midval, x):
            hi = mid
        else:
            return mid
    return -1

def lex_sort(v): #sort by 1 and 2 column respectively
    #return v[np.lexsort((v[:,2],v[:,1]))]
    order = range(1, v.shape[1])
    return v[np.lexsort(tuple(v[:,i] for i in order[::-1]))]

def sort_and_index(arr):
    ind = np.indices((len(arr),)).reshape((len(arr), 1))
    arr = np.hstack([ind, arr]) # add an index column as first column
    arr = lex_sort(arr)
    arr_cut = arr[:,1:] # an array to do binary search in
    arr_ind = arr[:,:1] # shuffled indices
    return arr_ind, arr_cut


#lat1 = np.array(([1,2,3], [3,4,5], [5,6,7], [7,8,9])) # ~ 200000 rows
lat1 = np.arange(1,800001,1).reshape((200000,4))
#lat2 = np.array(([3,4,5], [5,6,7], [7,8,9], [1,2,3])) # same number of rows as time
lat2 = np.arange(101,800101,1).reshape((200000,4))

lat1_ind, lat1_cut = sort_and_index(lat1)

time_arr = np.zeros(200000)
import time
start = time.time()

for ii, elem in enumerate(lat2):
    pos = binary_search(lat1_cut, elem)
    if pos == -1:
        #Not found
        continue
    pos = lat1_ind[pos][0]
    #print "element in lat2 with index",ii,"has position",pos,"in lat1"
print time.time()-start
将numpy导入为np
#按字典顺序比较两点——a和b
def小于(a,b):
i=0

而查找所有匹配项的最快方法可能是对两个数组进行排序并一起遍历,如下面的工作示例:

import numpy as np

def is_less(a, b):
    # this ugliness is needed because we want to compare lexicographically same as np.lexsort(), from the last column backward
    for i in range(len(a)-1, -1, -1):
        if a[i]<b[i]: return True
        elif a[i]>b[i]: return False
    return False

def is_equal(a, b):
    for i in range(len(a)):
        if a[i] != b[i]: return False
    return True

# lat1 = np.array(([48.78,47.45],[38.56,39.53]))
# lat2 = np.array(([7.78,5.45],[48.78,47.45],[7.56,5.53]))
lat1 = np.load('arr.npy')
lat2 = np.load('refarr.npy')

idx1 = np.lexsort( lat1.transpose() )
idx2 = np.lexsort( lat2.transpose() )
ii = 0
jj = 0
while ii < len(idx1) and jj < len(idx2):
    a = lat1[ idx1[ii] , : ]
    b = lat2[ idx2[jj] , : ]
    if is_equal( a, b ):
        # do stuff with match
        print "match found: lat1=%s lat2=%s %d and %d" % ( repr(a), repr(b), idx1[ii], idx2[jj] )
        ii += 1
        jj += 1
    elif is_less( a, b ):
        ii += 1
    else:
        jj += 1
将numpy导入为np
def小于(a,b):
#之所以需要这种难看,是因为我们想从上一列向后比较与np.lexsort()相同的词典
对于范围内的i(len(a)-1,-1,-1):
如果a[i]b[i]:返回False
返回错误
def等于(a,b):
对于范围内的i(len(a)):
如果a[i]!=b[i]:返回False
返回真值
#lat1=np.数组([48.78,47.45],[38.56,39.53]))
#lat2=np.数组([7.78,5.45],[48.78,47.45],[7.56,5.53]))
lat1=np.load('arr.npy')
lat2=np.load('refarr.npy')
idx1=np.lexsort(lat1.transpose())
idx2=np.lexsort(lat2.transpose())
ii=0
jj=0
而ii

这可能不是完美的pythonic(也许有人能想到一个更好的使用生成器或itertools的实现),但很难想象有任何方法依赖于一次搜索一个点的速度超过这个速度。

什么是
lat
lon
时间
?特别是,它们的形状是什么?我更新了上面的样本值。你能解释一下
pos=np.argwhere((lat1[:,0]==lat2[ii,0])和(lat1[:,1]==lat2[ii,1])
?那么,你想在lat2中找到这样一行,它等于lat1?您不怕浮点舍入错误吗?如果是这样,您可以在lat2上使用二进制搜索(在其排序副本中搜索)。我正在查找lat1和lat2在两列中相等的行。在这种情况下,我需要lat1和lat2的行号。现在“ii”和“pos”给了我,它就起作用了。我在两个数组上都使用了np.around(XX,小数=2)来避免舍入错误。因此,如果
lat1=[[1,2],[3,4],[5,6],[7,8],[7,8],[7,8],[7,2]
,那么算法的结果应该是
[1,2,3,0]
(lat2的0-st元素在lat1上的第一位,lat2的1元素在2上,2在3上,3在0上的第三位)这就是你想要的吗?事实上,这是难以置信的快!然而,我得到了奇怪的结果。我正在检查原因。如果lat1和lat2有4列要比较,我需要更改什么?有可能直接使用它们吗?此外,我根本不知道二进制搜索中发生了什么,也不知道为什么这比我开始文章中的简单循环快得多。。你有关于它的文献吗?好吧,假设你有一个排序数组
[1,2,6,17,25,29,37]
,你想找到
25
元素的索引。一种方法是迭代整个数组,并将每个元素与所搜索的元素进行比较。但是我们的数组是经过排序的,所以通过查看任何索引中的元素,我们可以判断所查找的元素是位于该元素的右侧还是左侧。例如,让我们从数组的中心开始:当前元素是
17
,索引是
3
(从0开始计数)。我们正在寻找
25
25>17
,因此在左侧部分(索引[0,3])寻找我们的元素没有意义。因此,我们可以不迭代整个数组,而是执行以下智能过程:1)选择输入数组的中心2)将当前元素与搜索的元素进行比较。3) 如果等于,那么我们发现有两种情况:被寻求的更小,被寻求的更大。如果较小,则设置输入数组=输入数组的左半部分,然后转到1)。如果较大,则相同,但为右半部分。该算法允许我们以O(log2(N))代替O(N)时间来查找索引。比较:N=200000 log2(N)=17.609640474436812。这大致意味着使用二进制搜索,您将在最多17个操作中找到元素,而不是使用朴素搜索算法进行200000个操作。这是一个很好的解释,不幸的是,这不适用于我的数据:和。在您的代码中,arr应该是lat1,refarr lat2。@超级立方体,请尝试上面的最新代码。在这里工作,0.4秒处理示例数据(包括np.load()s)。我忘记了(明显的)索引解引用lat1[idx1[ii],…]等等;它正好与我使用的数据一起工作。工作得很好!在我的机器上完成完整的数据集需要5.2秒,而alex_Jordan的解决方案需要10.3秒!O(N)而不是我的O(N*log(N))。我怎么没想到呢。