【问题标题】:Is there a way to not make a copy when a numpy array is sliced?当一个numpy数组被切片时,有没有办法不制作副本?
【发布时间】:2020-01-18 01:09:31
【问题描述】:

我需要在我的项目中处理一些大型 numpy 数组。从磁盘加载这样的阵列后,将消耗我计算机一半以上的内存。

加载数组后,我对它做了几个切片(将选择几乎一半的数组),然后我收到错误,告诉我内存不足。

据我所知,通过做一个小实验,我收到了错误,因为当numpy 数组被切片时,将创建一个副本

import numpy as np

tmp = np.linspace(1, 100, 100)
inds = list(range(100))
tmp_slice = tmp[inds]

assert id(tmp) == id(tmp_slice)

返回AssertionError

有没有办法让numpy 数组的一部分只引用原始数组的内存地址,从而不复制数据条目?

【问题讨论】:

  • 您的id 测试只是比较了两个不同的python 对象。它不比较它们的元素存储。您可能需要阅读有关基本 numpy 数组布局的更多信息。

标签: python arrays numpy


【解决方案1】:

在 Python 中,slice 是一个定义良好的类,具有 startstopstep 值。当我们使用alist[1: 10: 2] 索引列表时使用它。这将创建一个新列表,其中包含原始指针的副本。在numpy 中,这些用于basic indexing,例如arr[:3, -3:]。这将创建原始的viewview 共享数据缓冲区,但有自己的shapestrides

但是当我们用列表、数组或布尔数组(掩码)索引数组时,它必须创建一个副本,一个具有自己数据缓冲区的数组。元素的选择过于复杂或不规则,无法用shapestrides 属性来表达。

在某些情况下,索引数组很小(与原始数组相比)并且副本也很小。但是如果我们对整个数组进行置换,那么索引数组和副本都将与原始数组一样大。

【讨论】:

    【解决方案2】:

    通读thisthisthis 我认为您的问题在于使用高级切片,并重申其中一个答案——numpy docs 明确指出

    高级索引始终返回数据的副本(与 返回视图的基本切片)。

    所以不要这样做:

    inds = list(range(100))
    tmp_slice = tmp[inds]
    

    你应该使用:

    tmp_slice = tmp[:100]
    

    这将导致view rather than a copy。您可以通过尝试发现差异:

    tmp[0] = 5

    在第一种情况下tmp_slice[0] 将返回1.0,但在第二种情况下它将返回5

    【讨论】:

    • 感谢您的回答!示例中的inds 仅用于演示,在实际项目中,我必须使用高级切片,因为掩码类似于[True, False, True, False],其中选定的inds 不连续。还有其他方法吗?
    • 我认为,一旦您使用掩码,这就是高级索引,因此需要复制数据。基本上非连续访问内存必然会产生副本。
    • 也许可以试试熊猫? stackoverflow.com/questions/33103988/…
    • 不我不能,除非我重构代码,因为所有掩码都是在切片之前生成的,所以在应用所有掩码之前我无法对原始数组做任何事情。
    • 布尔掩码与原始数组一样大。
    猜你喜欢
    • 1970-01-01
    • 2015-04-18
    • 1970-01-01
    • 2021-06-24
    • 2018-01-13
    • 1970-01-01
    • 2022-11-19
    • 1970-01-01
    • 2018-04-13
    相关资源
    最近更新 更多