【问题标题】:Filter Numpy array for negative values excluding equal values but with opposite sign过滤 Numpy 数组的负值,不包括相等的值但符号相反
【发布时间】:2021-10-27 09:05:14
【问题描述】:

我有

x = np.array([20000, 700, 1000, -5000, -250, 30, -1000, 50, -30, 75, -999])

我想从x 中排除值-1000-30,因为它们前面有对应的值100030。我想得到

y = np.array([-5000, -250, -999])

【问题讨论】:

  • 你能展示以下测试用例吗:[-5, 5 -5]?
  • @MadPhysicist [-5]
  • 好的。我的答案是唯一给出的答案。
  • 顺便说一句,您应该养成接受答案的习惯,这样您的问题就不会永远在未回答的队列中徘徊。点赞也不错。

标签: python numpy


【解决方案1】:

有一个快速的O(n log n) 有效且矢量化的 Numpy 实现

这个想法是在x(和np.unique)中找到唯一值,并为每个唯一值定位其第一个位置。然后你可以在x中选择值v,如果之前找到-vv < 0。要查找它之前是否找到,您可以在已排序的唯一值(使用np.searchsorted)中执行二分法,以查找当前索引是否大于找到的索引(在唯一值中)。

这是生成的代码:

xUnique, xFirstPos = np.unique(x, return_index=True)
xIsNeg = x < 0
xNeg = -x
xNegUniquePos = np.searchsorted(xUnique, xNeg)
xNegIsFound = xUnique[xNegUniquePos] == xNeg
xHasNegBefore = np.logical_and(xNegIsFound, xFirstPos[xNegUniquePos] < np.arange(len(x)))
result = x[np.logical_and(xIsNeg, np.logical_not(xHasNegBefore))]
print(result)

以下是一些示例的结果:

x = np.array([20000, 700, 1000, -5000, -250, 30, -1000, 50, -30, 75, -999])
result = np.array([-5000,  -250,  -999])

x = np.array([-5, 5, -5])
result = np.array([-5])

以下是大小为 100_000 的随机数组的时序(33% 的负值在 -1_000_000 到 2_000_000 范围内):

Mad Physicist's Numpy implementation:         38900.0 ms
Emi OB's implementation:                       1360.0 ms (incorrect so far)
Mad Physicist's pure Python implementation:      40.0 ms
This implementation:                             14.1 ms

到目前为止,这个实现比其他实现快得多。对于这个输入大小,Mad Physicist 的 Numpy 实现占用 几个 GiB 的内存,而其他解决方案(包括这个)占用不超过 10 MiB,这毫无价值。

【讨论】:

    【解决方案2】:

    你可能不会用纯 numpy 获得 O(n) 的实现,但你可以使用 dict:

    lookup = {v: i for i, v in enumerate(x) if v > 0}
    result = [v for i, v in enumerate(x) if v < 0 and lookup.get(-v, x.size) > i]
    

    这在实践中会有点慢,但有很好的时间复杂度。一个更实际的解决方案是在整个过程中使用 numpy:

    # identify the negative numbers
    idx = np.flatnonzero(x < 0)
    # get the corresponding negative values
    neg = x[idx]
    # find the index of the first corresponding positive
    # will contain false zero for non-matching
    p = (-neg == x[:, None]).argmax(0)
    # set non-matching to large number
    p[x[p] != -neg] = x.size
    # return only elements that have smaller index than corresponding positive
    result = x[idx[p > idx]]
    

    由于argmax,这是 O(n^2),但可能比您在实践中可能遇到的数组的纯 python 实现更快。

    【讨论】:

      【解决方案3】:

      根据新信息进行编辑

      您可以使用列表推导和枚举。遍历 x 中的每个元素,仅当它小于 0(负)并且乘以 -1 的值不在 x 中直到该位置时才保留它(即,如果它在列表中位于它之前)。

      y = np.array([i for pos,i in enumerate(x) if i&lt;0 and i*-1 not in x[:pos])

      【讨论】:

      • 这就是我要找的@EmiOB。我想知道当 numpy 数组很大时,列表理解是否是最好的方法。
      • @user270199。这没有通过 cmets 中的测试用例
      • 是的,我注意到了。感谢您指出@MadPhysicist
      • @MadPhysicist 我误解了 OP 想要实现的目标,并在他们的评论进一步解释他们的目标之前写了这个。鉴于原始问题中提供的信息有限,这在当时是正确的
      • 我能够根据我认为非常明确的信息正确解释原始问题。我在评论中要求澄清,因为我不明白为什么每个人都误读了这个问题。
      猜你喜欢
      • 2021-10-05
      • 2020-10-23
      • 2021-05-16
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-07
      相关资源
      最近更新 更多