【问题标题】:Find absolute minimum between two numpy arrays but keep the sign查找两个 numpy 数组之间的绝对最小值,但保留符号
【发布时间】:2019-10-31 11:12:45
【问题描述】:

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

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

但是我也想保留这个标志,所以想要的输出是:

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

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

【问题讨论】:

  • 如果你在处理 NumPy 数组,pandas 是从哪里来的?
  • @cs95 你是对的,不需要熊猫。我忘记了 NumPy 的广播,所以不知道如何在不使用 Pandas 和 apply 方法的情况下减去不同大小的数组。
  • 您能否有两个输出列:一个用于absdiff(然后您可以轻松地对其进行最小化),另一个用于sign?或者你只需​​要广播一个min_abs_diff_preserve_signpandas/numpy 函数吗?
  • “我使用 Pandas 是因为我的实际数组是 Pandas 数据帧的子集” 如果它们是 pandas Series 而不是 numpy arrays,请编辑标题以说明这一点。

标签: python pandas numpy difference


【解决方案1】:

理解

我忍不住了。这不是你应该做的!但是,它很可爱。

[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])

【讨论】:

  • 在我的实际数组中,left_diff = A-C[left]IndexError 而出错。也就是说,如果数组 C 的长度为 100 并且 left 返回索引 100,那么它将出错(因为索引为 0-99)。 left = np.maximum(a,0) - 1 会保留算法并修复索引问题吗?
  • @GeneBurinsky 道歉。我更新了我的帖子。应该是np.maximum(a - 1, 0) 不要使用np.maximum(a, 0) - 1,因为这会在实例中破坏最小值发生在第一个位置,因为相应的位置将是-1
【解决方案2】:

让我们在这里使用广播减法。然后我们使用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 广播,所以应该很快。

【讨论】:

  • 这是一个快速但非常占用内存的解决方案。这对较小的数组很有用,但在我的实际数据中,我遇到了MemoryError。从角度来看,我的实际A 的长度为 50e3,B 的长度为 1e6,因此广播的内存需求非常大。
【解决方案3】:

既然你标记了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

【讨论】:

  • 如果你想使用 pandas s=pd.DataFrame(A[None,:] - B[:,None]); s.lookup(s.abs().idxmin(),s.columns) Out[123]: array([-47, 17, -19, 32, -41])
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-11
    • 2021-12-09
    相关资源
    最近更新 更多