将numpy数组中的点分配到一个二维正方形网格中

3

由于速度问题,我将超越我的以前的问题。我有一组点的Lat/Lon坐标数组,并且我想将它们分配到从等大小单元格的2D正方形网格派生的索引代码中。这是一个示例,说明它将如何运作。让我们称之为points,我的第一个包含六个点坐标(称之为[x y]对)的数组:

points = [[ 1.5  1.5]
 [ 1.1  1.1]
 [ 2.2  2.2]
 [ 1.3  1.3]
 [ 3.4  1.4]
 [ 2.   1.5]]

然后我有另一个数组,包含一个由两个单元格组成的网格的顶点坐标,形式为[minx,miny,maxx,maxy];让我们称其为bounds

bounds = [[ 0.  0.  2.  2.]
 [ 2.  2.  3.  3.]]

我希望找出哪些点位于哪个边界内,然后根据bounds数组的索引分配代码(在这种情况下,第一个单元格的代码为0,第二个为1,依此类推...)。由于单元格是正方形,计算每个点是否在每个单元格中的最简单方法是评估:
x > minx & x < maxx & y > miny & y < maxy

因此,生成的数组将显示为:

results = [0 0 1 0 NaN NaN]

其中NaN表示该点在单元格外。在我的实际情况中,元素数量的数量级为将10^6个点找到10^4个单元格。是否有一种使用numpy数组快速完成此类操作的方法?

编辑:为了澄清,预期的results数组意味着第一个点位于第一个单元格内(bounds数组的0索引),因此第二个点位于第二个单元格内,第一个点位于第二个单元格内,依此类推...


[0 0 1 0 NaN NaN] 是之前 boundspoints 的结果吗?你能解释一下你是如何使用 bounds 的吗? - Mazdak
是的,交叉每个点与两个单元格,并获取相应的单元格代码。 - Fabio Lamanna
如果bounds是一个[minx miny maxx maxy]值的数组,则问题是实现函数x > minx & x < maxx & y > miny & y < maxy,以确定例如第一个点是否在bounds数组的第一个单元格中。希望这有所帮助。 - Fabio Lamanna
我明白了,但是bounds有两个项目!!!你是如何使用这两个项目的? - Mazdak
只需对它们进行迭代。我的意思是,这里的问题是在边界数组上实现搜索函数,该数组有两个项目,因为它包含两个单元格。第一个项目具有索引0,第二个项目具有索引1,因此我想将它们分配给每个点。 - Fabio Lamanna
3个回答

3

这里是一个针对你问题的矢量化方法。它可以显著地加快速度。

import numpy as np
def findCells(points, bounds):
    # make sure points is n by 2 (pool.map might send us 1D arrays)
    points = points.reshape((-1,2))

    # check for each point if all coordinates are in bounds
    # dimension 0 is bound
    # dimension 1 is is point
    allInBounds = (points[:,0] > bounds[:,None,0])
    allInBounds &= (points[:,1] > bounds[:,None,1])
    allInBounds &= (points[:,0] < bounds[:,None,2])
    allInBounds &= (points[:,1] < bounds[:,None,3])


    # now find out the positions of all nonzero (i.e. true) values
    # nz[0] contains the indices along dim 0 (bound)
    # nz[1] contains the indices along dim 1 (point)
    nz = np.nonzero(allInBounds)

    # initialize the result with all nan
    r = np.full(points.shape[0], np.nan)
    # now use nz[1] to index point position and nz[0] to tell which cell the
    # point belongs to
    r[nz[1]] = nz[0]
    return r

def findCellsParallel(points, bounds, chunksize=100):
    import multiprocessing as mp
    from functools import partial

    func = partial(findCells, bounds=bounds)

    # using python3 you could also do 'with mp.Pool() as p:'  
    p = mp.Pool()
    try:
        return np.hstack(p.map(func, points, chunksize))
    finally:
        p.close()

def main():
    nPoints = 1e6
    nBounds = 1e4

    # points = np.array([[ 1.5, 1.5],
    #                    [ 1.1, 1.1],
    #                    [ 2.2, 2.2],
    #                    [ 1.3, 1.3],
    #                    [ 3.4, 1.4],
    #                    [ 2. , 1.5]])

    points = np.random.random([nPoints, 2])

    # bounds = np.array([[0,0,2,2],
    #                    [2,2,3,3]])

    # bounds = np.array([[0,0,1.4,1.4],
    #                    [1.4,1.4,2,2],
    #                    [2,2,3,3]])

    bounds = np.sort(np.random.random([nBounds, 2, 2]), 1).reshape(nBounds, 4)

    r = findCellsParallel(points, bounds)
    print(points[:10])
    for bIdx in np.unique(r[:10]):
        if np.isnan(bIdx):
            continue
        print("{}: {}".format(bIdx, bounds[bIdx]))
    print(r[:10])

if __name__ == "__main__":
    main()
编辑:
我使用你提供的数据量时出现了MemoryError。如果你使用multiprocessing.Pool和它的map函数,可以避免这种情况并加快速度,详见更新后的代码。

结果:

>time python test.py
[[ 0.69083585  0.19840985]
 [ 0.31732711  0.80462512]
 [ 0.30542996  0.08569184]
 [ 0.72582609  0.46687164]
 [ 0.50534322  0.35530554]
 [ 0.93581095  0.36375539]
 [ 0.66226118  0.62573407]
 [ 0.08941219  0.05944215]
 [ 0.43015872  0.95306899]
 [ 0.43171644  0.74393729]]
9935.0: [ 0.31584562  0.18404152  0.98215445  0.83625487]
9963.0: [ 0.00526106  0.017255    0.33177741  0.9894455 ]
9989.0: [ 0.17328876  0.08181912  0.33170444  0.23493507]
9992.0: [ 0.34548987  0.15906761  0.92277442  0.9972481 ]
9993.0: [ 0.12448765  0.5404578   0.33981119  0.906822  ]
9996.0: [ 0.41198261  0.50958195  0.62843379  0.82677092]
9999.0: [ 0.437169    0.17833114  0.91096133  0.70713434]
[ 9999.  9993.  9989.  9999.  9999.  9935.  9999.  9963.  9992.  9996.]

real 0m 24.352s
user 3m  4.919s
sys  0m  1.464s

谢谢!实际上,一个点只能在一个单一的边界内...所以它应该可以正常工作! - Fabio Lamanna
1
@Fiabetto 你可能需要再看一下我的答案。我现在在我的机器上使用1e6个点和1e4个边界,时间已经缩短到24秒了。 - swenzel

2
您可以使用嵌套循环来检查条件并将结果作为生成器产生:
points = [[ 1.5  1.5]
 [ 1.1  1.1]
 [ 2.2  2.2]
 [ 1.3  1.3]
 [ 3.4  1.4]
 [ 2.   1.5]]

bounds = [[ 0.  ,0. , 2.,  2.],
 [ 2.  ,2.  ,3.,  3.]]

import numpy as np

def pos(p,b):
  for x,y in p:
    flag=False
    for index,dis in enumerate(b):
      minx,miny,maxx,maxy=dis
      if x > minx and x < maxx and y > miny and y < maxy :
        flag=True
        yield index
    if not flag:
        yield 'NaN'


print list(pos(points,bounds))

结果:

[0, 0, 1, 0, 'NaN', 'NaN']

谢谢Kasra,但是可能有一个打字错误吗?结果列表长度应该等于输入点数组的长度。 - Fabio Lamanna
@Fiabetto 欢迎。是的 :) 那只是一个打字错误! - Mazdak
1
谢谢你的帮助,我正在测试你的方法与我的旧方法在速度和时间上的比较!再次感谢! - Fabio Lamanna

1
我会这样做:
import numpy as np

points = np.random.rand(10,2)

xmin = [0.25,0.5]
ymin = [0.25,0.5]

results = np.zeros(len(points))

for i in range(len(xmin)):
     bool_index_array = np.greater(points, [xmin[i],ymin[i]])
     print "boolean index of (x,y) greater (xmin, ymin): ", bool_index_array
     indicies_of_true_true = np.where(bool_index_array[:,0]*bool_index_array[:,1]==1)[0]
     print "indices of [True,True]: ", indicies_of_true_true
     results[indicies_of_true_true] += 1

print "results: ", results

[out]: [ 1.  1.  1.  2.  0.  0.  1.  1.  1.  1.]

这将使用下限将您的点分类到以下组中:

  • 1(如果 xmin[0] < x <= xmin[1] 且 ymin[0] < y <= ymin[1])
  • 2(如果 x > xmin[1] 且 y > ymin[1])
  • 0(如果以上条件都不满足)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接