【问题标题】:Is there any way to delete the specific elements of an numpy array "In-place" in python:有没有办法在python中删除numpy数组“就地”的特定元素:
【发布时间】:2016-11-04 15:43:24
【问题描述】:

当调用“np.delete()”时,我没有兴趣为缩小后的数组定义一个新变量。我想对原始的 numpy 数组执行删除。任何想法?

>>> arr = np.array([[1,2], [5,6], [9,10]])
>>> arr
array([[ 1,  2],
       [ 5,  6],
       [ 9, 10]])
>>> np.delete(arr, 1, 0)
array([[ 1,  2],
       [ 9, 10]])
>>> arr
array([[ 1,  2],
       [ 5,  6],
       [ 9, 10]])
but I want:
>>> arr
array([[ 1,  2],
       [ 9, 10]])

【问题讨论】:

  • arr = np.delete(arr, 1, 0) 有什么问题?
  • 只做 arr = np.delete(arr, 1, 0) 有什么问题?或者您可以只调用 arr 而不使用方括号?

标签: python arrays numpy


【解决方案1】:

NumPy 数组是固定大小的,因此不能有 np.delete 的就地版本。任何此类函数都必须更改数组的大小。

最接近的方法是重新分配 arr 变量:

arr = numpy.delete(arr, 1, 0)

【讨论】:

    【解决方案2】:

    delete 调用不会修改原始数组,它会复制它并在删除完成后返回副本。

    >>> arr1 = np.array([[1,2], [5,6], [9,10]])
    >>> arr2 = np.delete(arr, 1, 0)
    >>> arr1
    array([[ 1,  2],
       [ 5,  6],
       [ 9, 10]])
    >>> arr2 
    array([[ 1,  2],
       [ 9, 10]])
    

    【讨论】:

      【解决方案3】:

      如果是性能问题,您可能想尝试(但我不确定)创建一个视图*,而不是使用 np.delete。您可以通过切片来实现,这应该是 inplace 操作:

      import numpy as np
      
      arr = np.array([[1,  2], [5,  6], [9, 10]])
      arr = arr[(0, 2), :]
      print(arr)
      

      导致:

      [[ 1  2]
       [ 9 10]]
      

      但是,这不会释放被排除行占用的内存。它可能会提高性能,但在内存方面,您可能会遇到相同或更糟的问题。另请注意,据我所知,没有办法通过排除进行索引(例如arr[~1] 会非常有用),这必然会使您花费资源来构建索引数组。

      对于大多数情况,我认为其他用户给出的建议是:

      arr = numpy.delete(arr, 1, 0)
      

      ,是最好的。在某些情况下,可能值得探索其他替代方案。

      编辑: *这实际上是不正确的(感谢@user2357112)。花式索引不会创建视图,而是返回一个副本,如 documentation 所示(我应该在得出结论之前检查一下,对此感到抱歉):

      Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view).
      

      所以我不确定这个花哨的索引建议是否值得作为一个实际的建议,除非它对 np.delete 方法有任何性能提升(我会在机会出现时尝试验证,见EDIT2)。

      EDIT2:我进行了一个非常简单的测试,以查看通过反对删除功能使用花式索引是否有任何性能提升。使用timeit(实际上是我第一次使用,但似乎每个 sn-p 的执行次数为 1 000 000,因此时间的最高数字):

      import numpy as np
      import timeit
      
      def test1():
          arr = np.array([[1, 2], [5, 6], [9, 10]])
          arr = arr[(0, 2), :]
      
      def test2():
          arr = np.array([[1, 2], [5, 6], [9, 10]])
          arr = np.delete(arr, 1, 0)
      
      print("Equality test: ", test1() == test2())
      
      print(timeit.timeit("test1()", setup="from __main__ import test1"))
      print(timeit.timeit("test2()", setup="from __main__ import test2"))
      

      结果如下:

      Equality test:  True
      5.43569152576767
      9.476918448174644
      

      这代表了非常可观的速度增益。不过请注意,为精美的索引构建序列需要时间。值不值当然要看解决的问题了。

      【讨论】:

      • 这实际上并没有创建视图。归类为高级索引的索引操作(例如您使用 (0, 2) 获得的操作)不会产生视图,因为它们不会产生创建视图所需的一致步幅。
      • @user2357112 是的。我应该先检查documentation。我的错,我会编辑帖子。您是否知道在性能方面这个选择是否会更快?如果不是这样,我的建议将毫无用处。
      • 我认为它可以避免numpy.delete 的一些开销。
      【解决方案4】:

      您可以实现自己的delete 版本,它在要删除的元素之后复制数据元素,然后返回一个不包括(现已过时的)最后一个元素的视图:

      import numpy as np
      
      
      # in-place delete
      def np_delete(arr, obj, axis=None):
          # this is a only simplified example
          assert (isinstance(obj, int))
          assert (axis is None)
      
          for i in range(obj + 1, arr.size):
              arr[i - 1] = arr[i]
          return arr[:-1]
      
      
      Test = 10 * np.arange(10)
      print(Test)
      
      deleteIndex = 5
      print(np.delete(Test, deleteIndex))
      print(np_delete(Test, deleteIndex))
      

      【讨论】:

      • 我在一个算法中使用它,在该算法中,每一步都会删除一行和一列。这个解决方案已经比在那里使用numpy.delete 更快了。在这个函数上使用 Numba 模块中的 @jit 装饰器可以让它更快。
      • @JensRenders 听起来您正在删除多个项目 - 我希望您一次考虑所有删除并且只复制每个元素一次?听起来我懒得尝试在这里发布的实现:) 很高兴知道它在某些情况下更快。我猜你也像我上面暗示的那样逐块复制数据而不是逐个元素?
      • 再想一想:在最坏的情况下,从一开始就删除元素,您的实现是否也更快?
      • 是的,在代码中移动块更有意义。使用 @jit 装饰器后,它不再有任何区别。在任何情况下,我当前的代码都比 numpy.delete 运行得更快,因为它不需要分配新内存。如果我计时我的代码并分配一些无用的内存,它的速度与 numpy.delete 差不多
      【解决方案5】:

      您的代码没有问题。你只需要override变量

          arr = np.array([[1,2], [5,6], [9,10]])
          arr = np.delete(arr, 1, 0)
      

      【讨论】:

      • 改变了实际的对象。您只是将arr 名称指向一个新对象。请参阅此答案的其他回复。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 2019-11-15
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多