【问题标题】:Python numpy indexing copyPython numpy 索引副本
【发布时间】:2017-02-26 02:08:17
【问题描述】:

我正在阅读 Python for data analysis about numpy Boolen indexing 一书,它说 Selecting data from an array by boolean indexing 总是会创建数据的副本,但为什么我可以使用 Boolen 索引更改原始数组?有人可以帮助我吗?非常感谢。 这是一个例子:

In [86]: data
Out[86]:
array([[-0.048 , 0.5433, -0.2349, 1.2792],
[-0.268 , 0.5465, 0.0939, -2.0445],
[-0.047 , -2.026 , 0.7719, 0.3103],
[ 2.1452, 0.8799, -0.0523, 0.0672],
[-1.0023, -0.1698, 1.1503, 1.7289],
[ 0.5994, 0.8174, -0.9297, -1.2564]])

In [96]: data[data < 0] = 0
In [97]: data
Out[97]:
array([[ 0. , 0.5433, 0. , 1.2792],
[ 0. , 0.5465, 0.0939, 0. ],
[ 0. , 0. , 0.7719, 0.3103],
[ 2.1452, 0.8799, 0. , 0.0672],
[ 0. , 0. , 1.1503, 1.7289],
[ 0.1913, 0.4544, 0.4519, 0.5535],
[ 0.5994, 0.8174, 0. , 0. ]])

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    布尔索引返回数据的副本,而不是原始数据的视图,就像切片一样。

    >>> b=data[data<0]; b # this is a copy of data
    array([-0.048 , -0.2349, -0.268 , -2.0445, -0.047 , -2.026 , -0.0523,
           -1.0023, -0.1698, -0.9297, -1.2564])
    

    我可以操纵b 并且data 被保留。

    >>> b[:] = 0; b
    array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])
    >>> data
    array([[-0.048 ,  0.5433, -0.2349,  1.2792],
           [-0.268 ,  0.5465,  0.0939, -2.0445],
           [-0.047 , -2.026 ,  0.7719,  0.3103],
           [ 2.1452,  0.8799, -0.0523,  0.0672],
           [-1.0023, -0.1698,  1.1503,  1.7289],
           [ 0.5994,  0.8174, -0.9297, -1.2564]])
    

    现在,切片:

    >>> a = data[0,:]; a # a is not a copy of data 
    array([-0.048 ,  0.5433, -0.2349,  1.2792])
    >>> a[:] = 0; a
    array([ 0.,  0.,  0.,  0.])
    >>> data
    array([[ 0.    ,  0.    ,  0.    ,  0.    ],
           [-0.268 ,  0.5465,  0.0939, -2.0445],
           [-0.047 , -2.026 ,  0.7719,  0.3103],
           [ 2.1452,  0.8799, -0.0523,  0.0672],
           [-1.0023, -0.1698,  1.1503,  1.7289],
           [ 0.5994,  0.8174, -0.9297, -1.2564]])
    

    但是,正如您所确定的,通过索引数组创建的assignments 始终是根据原始数据创建的。

    >>> data[data<0] = 1; data
    array([[ 1.    ,  0.5433,  1.    ,  1.2792],
           [ 1.    ,  0.5465,  0.0939,  1.    ],
           [ 1.    ,  1.    ,  0.7719,  0.3103],
           [ 2.1452,  0.8799,  1.    ,  0.0672],
           [ 1.    ,  1.    ,  1.1503,  1.7289],
           [ 0.5994,  0.8174,  1.    ,  1.    ]])
    

    【讨论】:

      【解决方案2】:

      在 fetch 或__getitem__ 中,布尔索引确实会返回一个副本。但如果在赋值前立即使用,则为__setitem__ 的情况,选择的值将被更改:

      In [196]: data = np.arange(10)
      In [197]: d1 = data[data<5]
      In [198]: d1                 # a copy
      Out[198]: array([0, 1, 2, 3, 4])
      In [199]: d1[:] = 0
      In [200]: data               # not change to the original
      Out[200]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
      

      蒙面作业:

      In [201]: data[data<5] = 0
      In [202]: data
      Out[202]: array([0, 0, 0, 0, 0, 5, 6, 7, 8, 9])   # changed data
      

      间接赋值什么都不做:

      In [204]: data[data<5][:] = 1
      In [205]: data
      Out[205]: array([0, 0, 0, 0, 0, 5, 6, 7, 8, 9])
      

      把它想象成data.__getitem__(mask).__setitem__(slice) = 1。获取项返回一个副本,设置项会更改该副本 - 但不会更改原始项。

      因此,如果您需要使用 LHS 的高级索引,请确保它位于分配之前。而且您不能在 LHS 上使用 2 个高级索引步骤。

      查看 v 复制

      通过basic 索引,可以使用原始数据缓冲区,只需更改形状和步幅等属性。例如:

      In [85]: x = np.arange(10)
      In [86]: x.shape
      Out[86]: (10,)
      In [87]: x.strides
      Out[87]: (4,)
      
      In [88]: y = x[::2]
      In [89]: y.shape
      Out[89]: (5,)
      In [90]: y.strides
      Out[90]: (8,)
      

      yx 具有相同的数据缓冲区(比较x.__array_interface__ 字典)。 x 使用所有 10 个 4bytes 元素; y 每隔一个使用一个(步长为 8 个字节而不是 4 个)。

      但是使用高级索引,您无法根据形状和步幅来表达元素选择。

      In [98]: z = x[[1,2,6,7,0]]
      In [99]: z.shape
      Out[99]: (5,)
      In [100]: z.strides
      Out[100]: (4,)
      

      原始数组中的项目可以按任意顺序重复选择。没有规律的模式。所以需要一份副本。

      【讨论】:

      • 谢谢,我只是一个初学者,从来没有写过代码,请你解释一下LHS上的2个高级索引步骤,我什至不知道LHS的含义。
      • LHS = 左侧,= 左侧的东西。我试图警告分配不起作用的情况,因为它创建了一个隐藏的副本。您的示例案例有效,因为没有中间副本。
      • 知道了,但我只是想知道为什么布尔索引会返回一个副本,因为 NumPy 是用大数据设计的,其他基本索引和切片只是原始数组的视图?有什么特殊的设计目的吗?
      • 我编辑了我的答案,试图说明基本索引和高级索引之间的区别。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-18
      • 2019-06-02
      • 1970-01-01
      • 2018-02-28
      相关资源
      最近更新 更多