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

Find closest float in array for all floats in another array

我在根据另一个数组中找到的最近的浮点值"过滤"一个数组时遇到性能问题。

这是一个MWE的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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]少,这是我比较它的子列表)

此块按预期工作,但效率极低。我确信答案在于广播和numpy阵列的正确应用,但我仍然没有足够的把握将其应用到我的问题上。

因为我是在提高这段代码的性能之后,任何解决方案都可以做到(即:我不受一定要使用广播的答案的约束)

添加

作为参考,在不久前我提出的这个类似问题中,快速加权的欧几里得数组中点之间的距离,用户Ali_m使用广播来实现惊人的性能改进。

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

Ali_m应用于该问题的广播解决方案不能应用于此吗?

添加2

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

1
2
3
4
5
6
7
8
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的范围,因此给出的答案不能否定ilist2[i] > max(list1[2])list2[i] < min(list1[2])所指的点。

是否可以修改答案以考虑这种可能性?我很抱歉更改了这样的原始代码,它真的被我偷走了。


在这里,kd树真的是太过分了,你需要做的就是对数组进行排序,然后使用二进制搜索在排序后的数组中找到最接近的值。前一段时间我写了一个关于如何使用searchsorted查找数组中目标的最接近值的答案。您可以在这里使用相同的想法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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)


如果你有坐骨神经痛,一个scipy.spatial.cKDTree可以做这个工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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树是为多维数据设计的,所以这可能不是最快的解决方案,但与您拥有的相比,它应该是相当快的。k-d树期望以点的二维数组的形式输入,其中data[i]是表示ith点的一维数组,因此使用newaxis的切片表达式将数据放入该格式。如果你需要更快的速度,你可以用numpy.sortnumpy.searchsorted来做一些事情。

如果需要拒绝来自list2的数据,这些数据不在list1[2]给出的值范围内,可以通过预处理步骤完成:

1
2
3
4
5
lowbound = array1[2].min()
highbound = array1[2].max()

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