在两个 numpy 数组之间找到绝对最小值但保留符号

Find absolute minimum between two numpy arrays but keep the sign

考虑两个不同长度的数组:

A = np.array([58, 22, 86, 37, 64])

B = np.array([105, 212,   5, 311, 253, 419, 123, 461, 256, 464])

对于 A 中的每个值,我想找到 AB 中的值之间的最小绝对差值。我使用 Pandas 因为我的实际数组是 Pandas 数据帧的子集,而且还因为 apply 方法是一种方便(尽管很慢)的方法来计算两个不同大小的数组之间的差异:

In [22]: pd.Series(A).apply(lambda x: np.min(np.abs(x-B)))
Out[22]:
0    47
1    17
2    19
3    32
4    41
dtype: int64

BUT 我也想保留符号,所以期望的输出是:

0    -47
1     17
2    -19
3     32
4    -41
dtype: int64

[更新] 我的实际数组 AB 的长度大约为 5e4 和 1e6,因此低内存解决方案是理想的。另外,我希望避免使用 Pandas 因为它在实际数组上非常慢。

np.argmin可以找到最小值的位置。因此你可以简单地这样做:

pd.Series(A).apply(lambda x: x-B[np.argmin(np.abs(x-B))])

让我们在这里使用广播减法。然后我们使用 argmin 找到绝对最小值,然后在后续步骤中提取值。

u = A[:,None] - B
idx = np.abs(u).argmin(axis=1)

u[np.arange(len(u)), idx]
# array([-47,  17, -19,  32, -41])

这使用纯 NumPy 广播,所以应该很快。

自从您标记 pandas

# compute the diff by broadcasting
diff = pd.DataFrame(A[None,:] - B[:,None])
# mininum value
min_val = diff.abs().min()

# mask with where and stack to drop na
diff.where(diff.abs().eq(min_val)).stack()

输出:

0  0   -47.0
   2   -19.0
   4   -41.0
2  1    17.0
   3    32.0
dtype: float64

理解

我情不自禁。这不是你应该做的!但是,很可爱。

[min(x - B, key=abs) for x in A]

[-47, 17, -19, 32, -41]

减少 Big-O Numpy 解决方案

如果 N = len(A)M = len(B) 那么这个解决方案应该是 O(N + M log(M)) 如果 B 已经排序,则不需要排序步骤。这变成了 O(N + M)

C          = np.sort(B)
a          = C.searchsorted(A)

# It is possible that `i` has a value equal to the length of `C`
# in which case the searched value exceeds all those found in `C`.
# In that case, we want to clip the index value to the right most index
# which is `len(C) - 1`
right      = np.minimum(a, len(C) - 1)

# For those searched values that are less than the first value in `C`
# the index value will be `0`.  When I subtract `1`, we'll end up with
# `-1` which makes no sense.  So we clip it to `0`.
left       = np.maximum(a - 1, 0)

对于裁剪值,我们最终会将一个值与其自身进行比较,因此它是安全的。

right_diff = A - C[right]
left_diff  = A - C[left ]

np.where(np.abs(right_diff) <= left_diff, right_diff, left_diff)

array([-47,  17, -19,  32, -41])