【问题标题】:Numpy remove duplicate column valuesNumpy 删除重复的列值
【发布时间】:2018-07-08 22:39:54
【问题描述】:

我有一个如下的numpy数组

array([[ 6,  5],
   [ 6,  9],
   [ 7,  5],
   [ 7,  9],
   [ 8, 10],
   [ 9, 10],
   [ 9, 11],
   [10, 10]])

我想选择 y 坐标唯一的元素。如果两个 y 坐标相同,我想选择 x 坐标较小的元素。

预期输出

array([[ 6,  5],
   [ 6,  9],
   [ 8, 10],
   [ 9, 11]])

解释

选择[6,5] 而不是[7,5]

选择[8,10] 而不是[9,10][10,10]

选择[9, 11]

谢谢

【问题讨论】:

  • 请发布您已经尝试过的内容。
  • 我做了一个天真的循环。玩弄独特的没有得到任何地方
  • 数组是否总是按照示例中给出的 x 坐标排序?
  • @gaganso 不一定。为了方便起见,我可以对它们进行排序
  • 使用unique之前先排序

标签: python arrays numpy


【解决方案1】:

首先,按第一列排序:

a = a[a[:, 0].argsort()]

使用带有 return_index 标志的 np.unique 返回唯一索引:

a[np.unique(a[:, 1], return_index=True)[1]]

array([[ 6,  5],
       [ 6,  9],
       [ 8, 10],
       [ 9, 11]])

一些时间安排:

a = np.random.randint(1, 10, 10000).reshape(-1, 2)

In [45]: %timeit rows_by_unique_y(a)
3.83 ms ± 137 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [46]: %timeit argsort_unique(a)
370 µs ± 8.26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

是的,我的方法使用初始排序,但在 Python 中的 numpy beat 迭代中使用矢量化操作。

【讨论】:

  • 对于这个问题,排序是渐近次优的。 O(n log n) 与 O(n)
  • 为什么不对我们两种方法都计时,使用 numpy 时循环不好
  • +1,干杯。你的常数确实低得多!我经常低估 Python 解释器的速度。
  • @AlexReinking 是的,在使用 numpy 时,这是一件违反直觉的事情。在像this 这样的答案中,我实际上在删除重复项时对某些东西进行了两次排序,它仍然很容易击败任何普通的 python 方法。
  • 玩一些输入数组大小,我实际上可以看到线性与 n-log-n 缩放,但交叉点约为 2500 万行
【解决方案2】:

如果您愿意使用其他库,我建议您使用 numpy_indexed 以获得高效且紧凑的解决方案

import numpy as np
import numpy_indexed as npi

a = np.array([[6, 5], [6, 9], [7, 5], [7, 9], [8, 10], [9, 10], [9, 11], [10, 10]])

column_to_groupby = 1
groups, reduced = npi.group_by(a[:,column_to_groupby]).min(a)
print(reduced)

它给出以下输出

[[ 6  5]
 [ 6  9]
 [ 8 10]
 [ 9 11]]

这是timeit结果

In [5]: a = np.random.randint(1, 10, 10000).reshape(-1, 2)

In [6]: %timeit npi.group_by(a[:,1]).min(a)
354 µs ± 2.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

【讨论】:

    【解决方案3】:

    一种方法循环遍历数组并记下您见过的最佳值,然后在最后重建数组:

    import numpy as np
    
    def rows_by_unique_y(arr):
      best_for_y = defaultdict(lambda: float('inf'))
      for i, row in enumerate(arr):
        x,y = row[0], row[1]
        best_for_y[y] = min(x, best_for_y[y])
      return np.array([[x,y] for y, x in best_for_y.items()])
    
    arr = np.array([[6,  5], [6,  9], [7,  5], [7,  9], [8, 10], [9, 10], [9, 11], [10, 10]])
    print(rows_by_unique_y(arr))
    

    无需排序,只需跟踪最小值即可。这输出:

    [[ 6  5]
     [ 6  9]
     [ 8 10]
     [ 9 11]]
    

    虽然这个答案越来越快,但user3483203's answer 在实践中要好得多。这是因为它调用优化的 C 代码,而不是停留在 Python 出奇的慢解释器中。但是,如果您的数组是 巨大的(几千兆字节),那么 O(n log n) 行为将开始输给这个。

    同时,如果您的数组那么大,您可能应该改用 Spark 等 MapReduce 框架。我上面给出的算法很容易并行化。


    如果您不需要最小的 x 值,那么以下使用 np.unique 的单行代码可以:

    arr[np.unique(arr[:,1], return_index=True)[1]]
    

    但这会返回

    array([[ 6,  5],
           [ 6,  9],
           [10, 10],
           [ 9, 11]])
    

    如果您切换810

    【讨论】:

    • 谢谢亚历克斯。我试图避免循环。尝试使用一些 np.unique(d[:,1]) 并玩弄它
    • 很难将np.unique 的行为与保持最小x 值的需求保持一致。如果您发现需要排序,我会针对这个解决方案彻底测试该解决方案,因为排序将使算法 O(n log n) 而我的代码以摊销(对于哈希表)O(n) 运行。跨度>
    • @user009122 - 我添加了如果您没有 x 值约束,您将如何使用 np.unique
    • 您对复杂性的看法是正确的。我的用例我正在寻找一个简洁的代码,我不太在意复杂性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 2015-03-01
    • 2017-08-14
    • 2021-09-08
    • 2018-11-27
    相关资源
    最近更新 更多