在数组中查找另一个数组中所有浮点数的最近浮点数

时间:2014-01-27 17:51:24

标签: python arrays performance algorithm numpy

我根据另一个数组中最近的浮点数“过滤”数组时出现了性能问题。

这是问题的MWE

import numpy as np

def random_data(N):
    # Generate some random data.
    return np.random.uniform(0., 10., N).tolist()

# Data lists.
N1 = 1500
list1 = [random_data(N1), random_data(N1), random_data(N1)]
list2 = random_data(1000)

# Define list1's range.
min_1, max_1 = min(list1[2]), max(list1[2])

# This list will contain the "filtered" list1.
list4 = [[], [], []]

# Go through each element in list2.
for elem2 in list2:

    # If it is located within the list1 range.
    if min_1 <= elem2 <= max_1:

        # Find the closest float in sub-list list1[2] to this float
        # in list2.
        indx, elem1 = min(enumerate(list1[2]), key=lambda x:abs(x[1]-elem2))

        # Store the values in list1 that are associated with the closest float
        # found above.
        list4[0].append(list1[0][indx])
        list4[1].append(list1[1][indx])
        list4[2].append(elem1)

(请注意,list2包含的元素少于list1[2],这是我将其与之比较的子列表。

此块按预期工作,但效率非常低。我确定答案在于broadcastingnumpy数组的正确应用,但我仍然无法将前者的问题充分应用于我的问题。

由于我在增强此代码的性能之后,任何解决方案都会做(即:我不受必须使用广播的答案的约束)


添加

作为参考,在我前一段时间提出的类似问题Fast weighted euclidean distance between points in arrays中,用户ali_m使用广播来实现性能的惊人提升。

问题不完全相同(那里的欧几里德距离不是绝对值,而且问题中的距离也必须加权)但这个问题在我看来更简单。

无法将应用于该问题的广播解决方案ali_m应用于此问题吗?


添加2

user2357112给出的答案与Eelco Hoogendoorn的更正对我最初定义的代码非常有用。我刚刚意识到我过度简化了它,在我的实际代码中,列表list1[2]list2 不一定在同一范围内定义。这将更准确地表示(这应该取代上面MWE中的第一行):

def random_data(N, xi, xf):
    # Generate some random data.
    return np.random.uniform(xi, xf, N).tolist()

# Data lists.
N1 = 1500
list1 = [random_data(N1, 13., 20.), random_data(N1, -1., 4.), random_data(N1, 2., 7.)]
list2 = random_data(1000, 0., 10.)

现在list1[2]的范围不等于list2的范围,因此给出的答案无法拒绝i或{{1}的那些点list2[i] > max(list1[2]) }}

可以修改答案以考虑这种可能性吗?我很抱歉改变这样的原始代码,它真的让我失望了。

2 个答案:

答案 0 :(得分:5)

Kd-tree在这里真的太过分了,你需要做的就是对数组进行排序并使用二进制搜索来找到排序数组中最接近的值。我前后写了一篇关于如何使用searchsorted来查找数组中目标的壁橱值的answer。你可以在这里使用相同的想法:

import numpy as np

def find_closest(A, target):
    #A must be sorted
    idx = A.searchsorted(target)
    idx = np.clip(idx, 1, len(A)-1)
    left = A[idx-1]
    right = A[idx]
    idx -= target - left < right - target
    return idx

def random_data(shape):
    # Generate some random data.
    return np.random.uniform(0., 10., shape)

def main(data, target):
    order = data[2, :].argsort()
    key = data[2, order]
    target = target[(target >= key[0]) & (target <= key[-1])]
    closest = find_closest(key, target)
    return data[:, order[closest]]

N1 = 1500
array1 = random_data((3, N1))
array2 = random_data(1000)
array2[[10, 20]] = [-1., 100]

array4 = main(array1, array2)

答案 1 :(得分:3)

如果您有SciPy,scipy.spatial.cKDTree可以完成这项任务:

import numpy
import scipy.spatial

array1 = numpy.array(list1)
array2 = numpy.array(list2)

# A tree optimized for nearest-neighbor lookup
tree = scipy.spatial.cKDTree(array1[2, ..., numpy.newaxis])

# The distances from the elements of array2 to their nearest neighbors in
# array1, and the indices of those neighbors.
distances, indices = tree.query(array2[..., numpy.newaxis])

array4 = array1[:, indices]

k-d树是为多维数据设计的,所以这可能不是最快的解决方案,但与你拥有的相比,它应该非常快。 kd树期望以2D数组点的形式输入,其中data[i]是表示i点的1D数组,因此使用newaxis的切片表达式来放置数据到那种格式。如果您需要它更快,您可以使用numpy.sortnumpy.searchsorted执行某些操作。

如果您需要拒绝list2中超出list1[2]给出的值范围的数据,可以通过预处理步骤完成:

lowbound = array1[2].min()
highbound = array1[2].max()

querypoints = array2[(array2 >= lowbound) & (array2 <= highbound)]
distances, indices = tree.query(querypoints[..., numpy.newaxis])