【问题标题】:Which numpy index is copy and which is view?哪个 numpy 索引是副本,哪个是视图?
【发布时间】:2020-12-23 07:25:41
【问题描述】:

问题

关于 Numpy Indexing 可以返回副本或查看,请确认我的理解是否正确。如果不是,请提供解释并提供相关规范的指针。

问:基本切片

Numpy 索引 documentationBasic Slicing and Indexing 部分。我相信本节只讨论Indexing 中提到的三种方式之一的基本切片

共有三种可用的索引:字段访问、基本切片、高级索引。发生哪一个取决于 obj。

它只使用Python slice object 并且只返回视图。即使切片在元组中,可能属于下面的部分,它仍然返回视图。

如果向索引提供一个元组,则该元组将被解释为索引列表。

此代码将返回一个视图。

>>> indices = (1,1,1,slice(0,2)) # same as [1,1,1,0:2]
>>> z[indices]
array([39, 40])

这些都是正确的理解吗?

问:结合基本切片和高级索引

文档中有一段:

当索引中至少有一个切片 (:)、省略号 (...) 或 newaxis(或数组的维度多于高级索引)时,行为可能会更复杂。

我相信,部分标题中的basic indexing意味着basic slicing。没有这样的basic indexing基本切片不同,因为Indexing 表示只有三种方式,field access, basic slicing, advanced indexing

当高级索引和基本索引结合时,它会返回副本。

这些理解正确吗?


背景

Numpy 索引有很多选项,有的返回视图,有的返回副本。有一个清晰的心理分类,哪个操作可能返回引用/视图或复制是相当混乱的。

共有三种可用的索引:字段访问、基本切片、高级索引。发生哪一个取决于 obj。

当 obj 是切片对象(由括号内的 start:stop:step 表示法构造)、整数或切片对象和整数的元组时,就会发生基本切片。 Ellipsis 和 newaxis 对象也可以穿插其中。

NumPy 切片创建一个视图,而不是像字符串、元组和列表等内置 Python 序列的情况下的副本。

在选择对象,obj是a的时候触发高级索引 非元组序列对象,一个 ndarray(数据类型为整数或布尔值), 或具有至少一个序列对象或 ndarray 的元组(数据类型 整数或布尔值)。有两种类型的高级索引:整数 和布尔值。

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

如果向索引提供一个元组,则该元组将被解释为索引列表。

z = np.arange(81).reshape(3,3,3,3)
>>> indices = (1,1,1,1)
>>> z[indices]
40

如果 ndarray 对象是结构化数组,则可以通过使用字符串索引数组来访问数组的字段,类似于字典。索引 x['field-name'] 将新视图返回到数组


注意

【问题讨论】:

  • @ hpaulj 是的,你是对的,它创建了一个视图,它仅作为“高级”用于奇怪的结果形状规则。我记得我遇到了那个怪癖,但很明显,这一切都够令人困惑的了。
  • 当切片夹在列表之间时会发生令人困惑的情况。然后切片维度移动到末尾。子案例的形式为(标量、切片、列表)。
  • 对于心智模型,需要很好地理解这些 numpy ndarray 属性:缓冲区、偏移量、步幅和形状。然后更容易理解为什么基本切片总是能够返回一个视图(使用相同的缓冲区)。还有为什么有些操作一般不能返回一个视图并且总是返回一个副本。

标签: python arrays numpy


【解决方案1】:

确实,为了更好地掌握返回视图和返回副本的内容,您需要彻底阅读文档(有时也没有真正提及)。我无法为您提供一整套操作及其输出类型(查看或复制),但也许这可以帮助您完成任务。

您可以使用np.shares_memory() 来检查函数是返回视图还是原始数组的副本。

x = np.array([1, 2, 3, 4])
x1 = x
x2 = np.sqrt(x)
x3 = x[1:2]
x4 = x[1::2]
x5 = x.reshape(-1,2)
x6 = x[:,None]
x7 = x[None,:]
x8 = x6+x7
x9 = x5[1,0:2]
x10 = x5[[0,1],0:2]


print(np.shares_memory(x, x1))
print(np.shares_memory(x, x2))
print(np.shares_memory(x, x3))
print(np.shares_memory(x, x4))
print(np.shares_memory(x, x5))
print(np.shares_memory(x, x6))
print(np.shares_memory(x, x7))
print(np.shares_memory(x, x8))
print(np.shares_memory(x, x9))
print(np.shares_memory(x, x10))
True
False
True
True
True
True
True
False
True
False

请注意最后 2 个高级+基本索引示例。一个是视图,另一个是副本。文档中提到的对这种差异的解释(还提供了有关如何实现这些差异的见解)是 -

当索引中至少有一个slice (:)ellipsis (...)newaxis 时(或者数组的维数多于高级索引),则行为可能会更复杂。这就像连接每个高级索引元素的索引结果

【讨论】:

    【解决方案2】:

    From Python to Numpy. Nicolas P. Rougier : Views and copies.

    首先,我们必须区分索引和花式索引。第一个将始终返回一个视图,而第二个将返回一个副本。这种差异很重要,因为在第一种情况下,修改视图会修改基本数组,而在第二种情况下则不然:

    如果您不确定索引的结果是视图还是副本,您可以检查结果的基础是什么。如果它是无,那么你的结果是一个副本:

    >>> Z = np.random.uniform(0,1,(5,5))
    >>> Z1 = Z[:3,:]
    >>> Z2 = Z[[0,1,2], :]
    >>> print(np.allclose(Z1,Z2))
    True
    >>> print(Z1.base is Z)
    True
    >>> print(Z2.base is Z)
    False
    >>> print(Z2.base is None)
    True
    

    如果基础引用源,则它不是无,因此是视图。

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 2016-12-10
      • 2021-03-12
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 2013-01-12
      • 2017-11-10
      • 1970-01-01
      相关资源
      最近更新 更多