在两个 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
中的每个值,我想找到 A
和 B
中的值之间的最小绝对差值。我使用 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
[更新] 我的实际数组 A
和 B
的长度大约为 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])
考虑两个不同长度的数组:
A = np.array([58, 22, 86, 37, 64])
B = np.array([105, 212, 5, 311, 253, 419, 123, 461, 256, 464])
对于 A
中的每个值,我想找到 A
和 B
中的值之间的最小绝对差值。我使用 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
[更新] 我的实际数组 A
和 B
的长度大约为 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])