搜索numpy数组((x,y,z)...),z匹配最近的x,y

时间:2014-05-20 20:17:25

标签: python numpy scipy interpolation

我有一个非常大的数组,类似于格式的高程数据:

triplets = ((x0, y0, z0), 
            (x1, y1, z1), 
            ... ,
            (xn, yn, zn))

其中x,y,z都是以米为单位的浮点数。您可以使用以下命令创建与此格式匹配的合适测试数据:

x = np.arange(20, 40, dtype=np.float64)
y = np.arange(30, 50, dtype=np.float64)
z = np.random.random(20) * 25.0
triplets = np.hstack((x, y, z)).reshape((len(x),3))

我希望能够有效地找到给定(x,y)对的相应z值。到目前为止,我的研究引出了更多问题。这就是我所拥有的:

  1. 遍历所有三元组:

    query = (a, b) # where a, b are the x and y coordinates we're looking for
    for i in triplets:
      if i[0] == query[0] and i[1] == query[1]:
        result = i[2]
    

    缺点:缓慢; a, b必须存在,这是比较浮点数的问题。

  2. 使用scipy.spatial.cKDTree查找最近的内容:

    points = triplets[:,0:2] # drops the z column
    tree = cKDTree(points)
    idx = tree.query((a, b))[1] # this returns a tuple, we want the index
    query = tree.data[idx]
    result = triplets[idx, 2]
    

    缺点:返回最近点而不是插值。

  3. 根据评论使用interp2d

    f = interp2d(x, y, z)
    result = f(a, b)
    

    缺点:不适用于大型数据集。在真实数据上运行时,我得到OverflowError: Too many data points to interpolate。 (我的实际数据大约是1100万点。)

  4. 所以问题是:是否有任何直截了当的做法让我忽视?有没有办法减少上述的弊端?

3 个答案:

答案 0 :(得分:4)

如果你想插入结果,而不是只找到最近邻居的z值,我会考虑做以下的事情:

  1. 使用k-d树根据(x,y)坐标对数据点进行分区
  2. 对于给定的(xi,yi)插值点,找到它的 k 最近邻居
  3. 取其 z 值的平均值,根据与(xi,yi)
  4. 的距离加权

    代码可能如下所示:

    import numpy as np
    from scipy.spatial import cKDTree
    
    # some fake (x, y, z) data
    XY = np.random.rand(10000, 2) - 0.5
    Z = np.exp(-((XY ** 2).sum(1) / 0.1) ** 2)
    
    # construct a k-d tree from the (x, y) coordinates
    tree = cKDTree(XY)
    
    # a random point to query
    xy = np.random.rand(2) - 0.5
    
    # find the k nearest neighbours (say, k=3)
    distances, indices = tree.query(xy, k=3)
    
    # the z-values for the k nearest neighbours of xy
    z_vals = Z[indices]
    
    # take the average of these z-values, weighted by 1 / distance from xy
    dw_avg = np.average(z_vals, weights=(1. / distances))
    

    值得玩一下 k 的值,这是最接近的邻居的平均数。这基本上是kernel density estimation的粗略形式,其中 k 的值控制着你对z值的基础分布强加的'平滑度'。较大的 k 会导致更平滑。

    同样,您可能想要根据与(xi,yi)的距离来衡量点的贡献,具体取决于您对 z的相似性的看法随着 x,y 距离的增加而减小。例如,您可能希望按(1 / distances ** 2)而不是(1 / distances)加权。

    在绩效方面,constructing and searching k-d trees are both very efficient。请记住,您只需要为数据集构建一次树,如果需要,您可以通过将(N,2)数组传递给tree.query()来一次查询多个点。 / p>

    近似最近邻搜索的工具(例如FLANN)可能会更快,但在数据维数非常高的情况下,这些工具通常会更有用。

答案 1 :(得分:3)

我不了解你的cKDTree代码,你得到idx,为什么再次循环for?您可以通过result = triplets[idx, 2]获得结果。

from scipy.spatial import cKDTree

x = np.arange(20, 40, dtype=np.float64)
y = np.arange(30, 50, dtype=np.float64)
z = np.random.random(20) * 25.0
triplets = np.hstack((x, y, z)).reshape((len(x),3))

a = 30.1
b = 40.5

points = triplets[:,0:2] # drops the z column
tree = cKDTree(points)
idx = tree.query((a, b))[1] # this returns a tuple, we want the index
result = triplets[idx, 2]

答案 2 :(得分:0)

您可以创建稀疏矩阵并使用简单的索引。

In [1]: import numpy as np
In [2]: x = np.arange(20, 40, dtype=np.float64)
In [3]: y = np.arange(30, 50, dtype=np.float64)
In [4]: z = np.random.random(20) * 25.0
In [9]: from scipy.sparse import coo_matrix
In [12]: m = coo_matrix((z, (x, y))).tolil()
In [17]: m[25,35]
Out[17]: 17.410532044604292