【问题标题】:numpy: copying value defaults on integer indexing vs boolean indexingnumpy:在整数索引与布尔索引上复制值默认值
【发布时间】:2014-04-24 10:30:46
【问题描述】:

我最近开始研究 McKinney 的 Python 数据分析。这让我在书中绊倒了:

数组切片是原始数组的视图。这意味着数据不 复制并且对视图的任何修改都将反映在 源数组...由于 NumPy 是针对大数据用例设计的 记住,如果 NumPy,你可以想象性能和内存问题 坚持从左到右复制数据。

很好。似乎是一个明智的设计选择。但是两页后它说:

通过布尔索引从数组中选择数据总是会创建一个副本 的数据,即使返回的数组没有改变。

等等,什么?还有,

您甚至可以将布尔数组与切片进行混合和匹配...例如data[names == 'Bob', 2:]

现在会返回什么?数据副本的视图?为什么这种行为是这样的?来自 R,我看到布尔索引和基于位置的索引同样常用的技术。如果 NumPy 的设计目的是避免复制内存,那么是什么驱动了这种设计选择?

谢谢。

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    让我们假设一个一维数组。内存中的数据如下所示:

    10 | 11 | 12 | 13 | 14 | 15 | 16
    

    通过索引访问元素是微不足道的。只取第一个元素的位置,跳n 步。所以,对于arr[2]

    10 | 11 | 12 | 13 | 14 | 15 | 16
               ^
    

    我可以通过一次乘法得到内存中的位置。快速简单。

    我可以切片,然后说“只取arr2 = arr[2:-1]”:

    10 | 11 | 12 | 13 | 14 | 15 | 16
               ^----^----^----^
    

    现在,内存布局非常相似。获取一个元素是从一个新起点的乘法。 arr2[1]:

    10 | 11 | 12 | 13 | 14 | 15 | 16
      (ignore) -----^----------
    

    你可以做一个更花哨的把戏,比如arr3 = arr[::2],让所有元素一个一个地跳跃。

    10 | 11 | 12 | 13 | 14 | 15 | 16
     ^---------^---------^---------^
    

    同样,获取arr3 的索引非常简单:只需进行乘法运算,但现在大小更大了。这就是 strides 的用途,它们告诉您块的大小以及如何通过索引获取元素。跨度在更多维度上更加强大。顺便说一句,这是我们可以将内存 (1D) 转换为矩阵 (2D) 的方式。

    现在,我们来看看布尔数组。如果我的掩码是:T F T T F F T,并且我要求您提供第三个元素,则您需要遍历掩码,找到第三个为真,然后获取其索引;因此,非常缓慢。因此,当采用布尔掩码时,我们必须复制数据。有一些掩码无法用步幅表示,但不是一般情况下,因此为了保持一致性,请始终使用副本。

    附带说明一下,有时制作副本的成本在性能方面是值得的。如果要进行多次读取“数组的每五个元素”的操作,内存中的数据将不会对齐,因此CPU每次都必须等待它被读取。然后制作单个副本(将是连续的)并使用它会更快。

    【讨论】:

    • 我想说,对于布尔索引,除了你提到的速度问题外,还有一个内存问题:你需要将原始数据和布尔向量保持在同一空间。无法像跨步内存视图那样共享数据,因此您也可以复制一份(并丢弃索引向量)。
    • @Davidmh:这是否意味着,如果我采用数组的不规则子集而不是切片,也会导致复制?例如:x=np.arange(10); y = x[[0, 3, 4, 6]] 应该导致将值复制到 y?
    • 没有什么比测试来确保:y[0] -> 0; y[0] = 1;x[0] -> 0
    • 当然,即使您采用常规子集也是如此。 y = x[[0, 3, 6]]
    【解决方案2】:

    需要一个副本,因为对于任意布尔索引,您无法保证结果可以表示为 ndarray。

    见:http://scipy-lectures.github.io/advanced/advanced_numpy/#indexing-scheme-strides

    切片返回视图

    一切都可以通过仅更改shapestrides 以及可能调整data 指针来表示!

    【讨论】:

    • 感谢您的链接。非常有用。
    猜你喜欢
    • 2021-01-14
    • 2017-08-06
    • 2016-06-02
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 2020-03-25
    相关资源
    最近更新 更多