根据元素距离交错两个numpy数组(python)

dei*_*aur 2 python sorting numpy

我有两个数组,看起来像这样:

X = np.array([ 157,  262,  368,  472,  577,  682,  786,  891,  996, 1100, 1204,
       1310, 1415, 1520, 1625, 1731, 1879])

Y = np.array([  30,  135,  240,  345,  450,  555,  660,  765,  870,  975, 1080,
       1185, 1290, 1395, 1500, 1605])
Run Code Online (Sandbox Code Playgroud)

阵列将:

  • 从开始按升序排序值.
  • 有时长度不等.

我想Z基于以下内容将这两个插入到一个新数组中:

  • 每个元素只能使用一次
  • 不需要使用所有元素
  • 如果存在元素,使得没有其他元素的值差小于且并且没有元素值,则距离to 小于的元素Xi可以仅包括Z元素.(同样的规则适用于中的元素.)YjYYabs(Xi - Yj)XYjabs(Xi - Yj)Y

我看到我可以通过一堆嵌套的for循环来做到这一点,但是我想知道是否有一些更聪明,更整洁的方法呢?

(我意识到,我提出这个问题的方式,听起来像是从教科书中剪下来的.它不是.但也许这是一个经典的排序功能,谁知道,但对我来说,作为一名生物学家......我只能说是我不知道如何以高效,整洁的方式解决它.)

编辑:不是那么漂亮的例子

new_list = list()
for i in X:
    delta_i = np.abs(Y - i)
    delta_reciprocal = np.abs(X - Y[delta_i.argmin()])
    if delta_i.min() == delta_reciprocal.min():
        new_list += sorted([Y[delta_i.argmin()],
        X[delta_reciprocal.argmin()]])
Z = np.array(new_list)
Run Code Online (Sandbox Code Playgroud)

我甚至都不确定它是否符合所有标准,但是当重写旧代码时,我只需要一个循环...仍然必须有一些更好的方法!

unu*_*tbu 5

让我们试着找出这个例子的解决方案:

In [1]: import numpy as np

In [5]: X = np.array([1879, 1731])

In [6]: Y = np.array([1481, 1691, 1586, 1796])
Run Code Online (Sandbox Code Playgroud)

我们可以像这样计算值X和值之间的所有距离Y:

In [7]: dist = np.abs(np.subtract.outer(X,Y))

In [8]: dist
Out[8]: 
array([[398, 188, 293,  83],
       [250,  40, 145,  65]])
Run Code Online (Sandbox Code Playgroud)

行对应于X值,列对应于Y值.

为了找到X最接近某个元素的值Y,我们正在寻找与 矩阵中的X最小值相对应的值.每列对应一个特定的列,因此列中的最小距离对应于某些列与特定列之间的最小距离.distYXY

从视觉上讲,我们正在寻找的是值,dist它们是它们所在行的最小值,以及它们所在的列.让我们称它们为"行列最小值".

dist上面的数组中,40是行列最小值.65是列最小值,但不是行列最小值.

对于每一列,我们可以找到以这种方式最小化列的X索引:

In [6]: idx1 = np.argmin(dist, axis = 0)

In [7]: idx1
Out[7]: array([1, 1, 1, 1])
Run Code Online (Sandbox Code Playgroud)

同样,对于每一行,我们都可以通过这种方式找到Y-index:

In [8]: idx2 = np.argmin(dist, axis = 1)

In [9]: idx2
Out[9]: array([3, 1])
Run Code Online (Sandbox Code Playgroud)

现在,让我们暂时忘掉这个例子,假设idx1看起来像这样:

        0,1,2,3,4,5   # the index value 
idx1 = (_,_,_,_,_,2,...)
Run Code Online (Sandbox Code Playgroud)

这就是说在第5列中,第2行具有最小值.

然后,如果第2行,第5列对应于行列最小值,那么idx2 必须如下所示:

        0,1,2        # index value
idx2 = (_,_,5,...)
Run Code Online (Sandbox Code Playgroud)

我们可以在NumPy中表达这种关系

idx1[idx2] == np.arange(len(X))
idx2[idx1] == np.arange(len(Y))    
Run Code Online (Sandbox Code Playgroud)

因此,对应于行列最小值的X,Y值是

X[idx1[idx2] == np.arange(len(X))]
Run Code Online (Sandbox Code Playgroud)

Y[idx2[idx1] == np.arange(len(Y))]
Run Code Online (Sandbox Code Playgroud)
import numpy as np
tests = [
    (np.array([1879, 1731]),
     np.array([1481, 1691, 1586, 1806])), 
    (np.array([1879, 1731]),
     np.array([1481, 1691, 1586, 1796])),
    (np.array([ 157,  262,  368,  472,  577,  682,  786,  891,  996, 1100, 1204]),
     np.array([  30,  135,  240,  345,  450,  555,  660,  765,  870,  975])),
    (np.array([ 157, 262, 368, 472, 577, 682, 786, 891, 996, 1100, 1204, 1310,
                1415, 1520, 1625, 1731, 1879]),
     np.array([ 221, 326, 431, 536, 641, 746, 851, 956, 1061, 1166, 1271, 1376,
                1481, 1586, 1691, 1796]))]

def find_close(X,Y):
    new_list = list()
    for i in X:
        delta_i = np.abs(Y - i)
        # print(delta_i)
        delta_reciprocal = np.abs(X - Y[delta_i.argmin()])
        if delta_i.min() == delta_reciprocal.min():
            new_list += sorted([Y[delta_i.argmin()],
                                X[delta_reciprocal.argmin()]])
    Z = np.array(new_list)
    return Z

def alt_find_close(X,Y):
    dist = np.abs(np.subtract.outer(X,Y))
    idx1 = np.argmin(dist, axis = 0)
    idx2 = np.argmin(dist, axis = 1)
    Z = np.r_[X[idx1[idx2] == np.arange(len(X))], Y[idx2[idx1] == np.arange(len(Y))]]
    return Z

for X, Y in tests:
    assert np.allclose(sorted(find_close(X,Y)), sorted(alt_find_close(X,Y)))
Run Code Online (Sandbox Code Playgroud)

时间结果:

% python -mtimeit -s'import test' 'test.find_close(test.X, test.Y)'
1000 loops, best of 3: 454 usec per loop
% python -mtimeit -s'import test' 'test.alt_find_close(test.X, test.Y)'
10000 loops, best of 3: 40.6 usec per loop
Run Code Online (Sandbox Code Playgroud)

所以alt_find_close明显快于find_close.