在Python中跨两个列表/数组查找最近的项

在Python中跨两个列表/数组查找最近的项,python,numpy,scipy,pandas,Python,Numpy,Scipy,Pandas,我有两个包含浮点值的numpy数组x和y。对于x中的每个值,我希望在y中找到最近的元素,而不重用y中的元素。输出应该是x元素索引到y元素索引的1-1映射。这里有一个糟糕的方法,它依赖于排序。它将从列表中删除已配对的每个元素。如果不进行排序,这将是不好的,因为配对将取决于原始输入数组的顺序 def min_i(values): min_index, min_value = min(enumerate(values), key=o

我有两个包含浮点值的numpy数组
x
y
。对于
x
中的每个值,我希望在
y
中找到最近的元素,而不重用
y
中的元素。输出应该是x元素索引到y元素索引的1-1映射。这里有一个糟糕的方法,它依赖于排序。它将从列表中删除已配对的每个元素。如果不进行排序,这将是不好的,因为配对将取决于原始输入数组的顺序

def min_i(values):
    min_index, min_value = min(enumerate(values),
                               key=operator.itemgetter(1))
    return min_index, min_value

# unsorted elements
unsorted_x = randn(10)*10
unsorted_y = randn(10)*10

# sort lists
x = sort(unsorted_x)
y = sort(unsorted_y)

pairs = []
indx_to_search = range(len(y))

for x_indx, x_item in enumerate(x):
    if len(indx_to_search) == 0:
        print "ran out of items to match..."
        break
    # until match is found look for closest item
    possible_values = y[indx_to_search]
    nearest_indx, nearest_item = min_i(possible_values)
    orig_indx = indx_to_search[nearest_indx]
    # remove it
    indx_to_search.remove(orig_indx)
    pairs.append((x_indx, orig_indx))
print "paired items: "
for k,v in pairs:
    print x[k], " paired with ", y[v]
我宁愿不首先对元素进行排序,但如果对它们进行排序,那么我希望获得原始未排序列表中的索引
unsorted\u x
unsorted\u y
。在numpy/scipy/Python或使用pandas实现这一点的最佳方法是什么?谢谢

编辑:为了澄清这一点,我并不是试图找到所有元素的最佳匹配(例如,不是最小化距离之和),而是每个元素的最佳匹配,如果有时以牺牲其他元素为代价,也可以。与上述示例相反,我假设
y
通常比
x
大得多,因此
y
x
的每个值通常都有很多非常好的拟合,我只想高效地找到那个值

有人能给我们举个scipy kdtrees的例子吗?这些文件相当稀少

kdtree = scipy.spatial.cKDTree([x,y])
kdtree.query([-3]*10) # ?? unsure about what query takes as arg

EDIT 2如果您可以选择多个邻居来保证数组中的每个项目都有一个唯一的邻居,那么使用
KDTree
的解决方案可以执行得非常好。使用以下代码:

def nearest_neighbors_kd_tree(x, y, k) :
    x, y = map(np.asarray, (x, y))
    tree =scipy.spatial.cKDTree(y[:, None])    
    ordered_neighbors = tree.query(x[:, None], k)[1]
    nearest_neighbor = np.empty((len(x),), dtype=np.intp)
    nearest_neighbor.fill(-1)
    used_y = set()
    for j, neigh_j in enumerate(ordered_neighbors) :
        for k in neigh_j :
            if k not in used_y :
                nearest_neighbor[j] = k
                used_y.add(k)
                break
    return nearest_neighbor
一个
n=1000
点的样本,我得到:

In [9]: np.any(nearest_neighbors_kd_tree(x, y, 12) == -1)
Out[9]: True

In [10]: np.any(nearest_neighbors_kd_tree(x, y, 13) == -1)
Out[10]: False
因此,最佳值为
k=13
,然后计时为:

In [11]: %timeit nearest_neighbors_kd_tree(x, y, 13)
100 loops, best of 3: 9.26 ms per loop
但在最坏的情况下,您可能需要
k=1000
,然后:

In [12]: %timeit nearest_neighbors_kd_tree(x, y, 1000)
1 loops, best of 3: 424 ms per loop
这比其他选项慢:

In [13]: %timeit nearest_neighbors(x, y)
10 loops, best of 3: 60 ms per loop

In [14]: %timeit nearest_neighbors_sorted(x, y)
10 loops, best of 3: 47.4 ms per loop

编辑在搜索超过1000项的数组之前对数组进行排序:

def nearest_neighbors_sorted(x, y) :
    x, y = map(np.asarray, (x, y))
    y_idx = np.argsort(y)
    y = y[y_idx]
    nearest_neighbor = np.empty((len(x),), dtype=np.intp)
    for j, xj in enumerate(x) :
        idx = np.searchsorted(y, xj)
        if idx == len(y) or idx != 0 and y[idx] - xj > xj - y[idx-1] :
            idx -= 1
        nearest_neighbor[j] = y_idx[idx]
        y = np.delete(y, idx)
        y_idx = np.delete(y_idx, idx)
    return nearest_neighbor
对于10000个元素长的数组:

In [2]: %timeit nearest_neighbors_sorted(x, y)
1 loops, best of 3: 557 ms per loop

In [3]: %timeit nearest_neighbors(x, y)
1 loops, best of 3: 1.53 s per loop
对于较小的阵列,它的性能稍差


如果只是为了丢弃重复项,则必须循环所有项以实现最近邻算法。考虑到这一点,这是我能想到的最快的方法:

def nearest_neighbors(x, y) :
    x, y = map(np.asarray, (x, y))
    y = y.copy()
    y_idx = np.arange(len(y))
    nearest_neighbor = np.empty((len(x),), dtype=np.intp)
    for j, xj in enumerate(x) :
        idx = np.argmin(np.abs(y - xj))
        nearest_neighbor[j] = y_idx[idx]
        y = np.delete(y, idx)
        y_idx = np.delete(y_idx, idx)

    return nearest_neighbor
现在是:

n = 1000
x = np.random.rand(n)
y = np.random.rand(2*n)
我得到:

In [11]: %timeit nearest_neighbors(x, y)
10 loops, best of 3: 52.4 ms per loop

我认为用二进制搜索的排序来查找索引可能是你最好的选择。@mgilton:scipy/numpy中有内置的二进制搜索算法吗?是的:在我看来,你需要一个组合
np.sort
np.argsort
np.searchsorted
。@Jaime,不确定你的意思,您可以为使用它查询的集合之外的点获取k近邻<代码>树=KDTree(x[:,无]);tree.query(y[:,None],k=1)为所有
y
查找最近的
x
(基于二次范数,您可以更改)。谢谢。使用
cKDTree
,有没有一种不重复的方法?即使性能受到轻微影响?另一个问题是:有没有办法确保
p.argmin(np.abs(y-xj))
忽略像NaN这样的缺失值?有没有过这样的情况,它会选择这些?这是您想要的。这种方法也可以用于多维点吗?因为我总是得到错误:缓冲区在“tree=scipy.spatial.cKDTree(y[:,None])行中的维度数错误(预期为2,得到3)