【问题标题】:What does it mean "The reshape function returns its argument with a modified shape, whereas the ndarray.resize method modifies the array itself"?“reshape 函数以修改后的形状返回其参数,而 ndarray.resize 方法修改数组本身”是什么意思?
【发布时间】:2019-07-14 22:15:26
【问题描述】:

在 Numpy 中,reshape 函数以修改后的形状返回其参数,而 ndarray.resize 方法修改数组本身。但这究竟意味着什么?真正的区别是什么?

import numpy as np
a = np.arange(18)
b = a.reshape(3,6)
a.resize(3,6)

a[0] = -2
print(a)

[[-2 -2 -2 -2 -2 -2]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]]

print(b)

[[-2 -2 -2 -2 -2 -2]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]]

b[0]= -1
print(b)

[[-1 -1 -1 -1 -1 -1]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]]

print(a)

[[-1 -1 -1 -1 -1 -1]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]]

从上面的代码中,resize() 和 reshape() 对相同的数据执行相同的操作。为什么需要两个?

【问题讨论】:

  • 在对帮助中心进行交叉检查后,我很确定这个问题符合质量基准水平(特别是询问“为什么”,并给出“为什么”的答案)。在 cmets 中,在提问者(似乎是新来者)明显可见的区域中:这个问题缺少什么/错了,使它值得被否决?

标签: python numpy


【解决方案1】:
In [15]: a = np.arange(18) 
    ...: b = a.reshape(3,6)  

我喜欢检查数组的属性:

In [16]: a.__array_interface__                                                                               
Out[16]: 
{'data': (65611776, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (18,),
 'version': 3}
In [17]: b.__array_interface__                                                                               
Out[17]: 
{'data': (65611776, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (3, 6),
 'version': 3}

请注意,data 对两者都是相同的。 bviewa。它是一个新的数组对象,但它共享底层数据缓冲区。它的形状不同,但对a 的更改将显示为b 的更改(反之亦然)。

查看有关 viewcopy 的文档。这是numpy中的一个重要区别,需要好好理解。

有时reshape 必须返回一个副本,就像order 被更改时一样。意识到reshape 不能改变元素的数量也很重要。

还有一个reshape 函数,在大多数情况下与方法相同。

shape 属性也可以直接更改:

In [19]: a.shape = (6,3)                                                                                     
In [20]: a                                                                                                   
Out[20]: 
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14],
       [15, 16, 17]])
In [21]: b                                                                                                   
Out[21]: 
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17]])

该操作是就地的,修改a 本身。它比reshape 更严格一些,并且不经常使用。例如,我们可以用一行创建b

b = np.arange(18).reshape(3,6)

resize 的主要区别在于它可以更改元素的数量。它可以截断,也可以增加大小。 resize 方法和函数都这样做,但有不同的“填充”规则。而resize 方法就地运行。

在您的示例中,a.resize(3,6) 就地更改了a,更改了其shape,但没有更改其数据缓冲区或b 到该缓冲区的链接。因此a 的更改也显示为b 的更改。

我的a 在原地shape 更改后仍然有相同的data

In [22]: a.__array_interface__                                                                               
Out[22]: 
{'data': (65611776, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (6, 3),
 'version': 3}

resize 方法之后使用相同的 data

In [23]: a.resize(3,6)                                                                                       
In [24]: a.__array_interface__                                                                               
Out[24]: 
{'data': (65611776, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (3, 6),
 'version': 3}

请注意,如果我尝试以更改元素总数的方式 resize a,我会收到错误消息。那是因为b共享数据缓冲区,我们不能改变a的大小而不影响b的大小。

In [28]: a.resize(4,6)                                                                                       
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-28-f7d47a627631> in <module>
----> 1 a.resize(4,6)

ValueError: cannot resize an array that references or is referenced
by another array in this way.
Use the np.resize function or refcheck=False

我想你会发现np.reshapea.reshape 被使用了很多。 resize 在这两种形式中的使用频率几乎没有。 reshape 更可预测且更安全。 resize 可以做同样的事情,但它也会让你做危险的事情。

我将重点了解viewcopy 之间的区别,其次是创建新数组和进行就地更改之间的区别。

【讨论】:

  • 深入了解底层数据缓冲区!计算机内存始终呈线性方式,看起来调整大小和重塑只是同一线性内存的虚拟“视图”。唯一的区别似乎是:1)调整大小可以改变物理内存大小,前提是没有对它的引用。 (有什么意义?,谁在乎呢) 2)调整大小是“就地”,例如。 a.resize(),而 reshape 返回一个引用,例如b=a.reshape()。所以,a.resize() 等价于 a=a.reshape()。两者都不会创建原始数组的新副本。总而言之,两者在实际意义上没有太大区别。
【解决方案2】:

表示一个函数会修改数组,而你只有修改后的数组。

另一个将返回一个新数组,已修改,但保持原始数组不变。它将使用双倍内存,但原始数组仍可用于其他计算。

根据具体情况,您可能更喜欢其中一种。

【讨论】:

  • 答案似乎不正确。这两个函数都不会返回一个新数组。修改都会影响原始数组。例如
【解决方案3】:

我认为这最好通过例子来解释。

import numpy as np

a = np.arange(18)

b = a

print(a)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]
print(b)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]

print("Reshape - doesnt change the base array")
print(a.reshape(3,6))
#[[ 0  1  2  3  4  5]
# [ 6  7  8  9 10 11]
# [12 13 14 15 16 17]]
print(b)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]

b = a.reshape(3,6)
print(a)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]
print(b)
#[[ 0  1  2  3  4  5]
# [ 6  7  8  9 10 11]
# [12 13 14 15 16 17]]

a = np.arange(18)
b = a

print(a)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]
print(b)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]

print("resize changes the actual base array")
a.resize(3,6)
print(a)
#[[ 0  1  2  3  4  5]
# [ 6  7  8  9 10 11]
# [12 13 14 15 16 17]]
print(b)
#[[ 0  1  2  3  4  5]
# [ 6  7  8  9 10 11]
# [12 13 14 15 16 17]]

【讨论】:

  • resize() 不返回引用,而是对自身进行操作。基本上是 a.resize(3,4) == a=a.reshape(3,4)。两者的真正区别似乎是:一个不返回引用,另一个返回引用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 2020-04-29
  • 1970-01-01
  • 2014-04-21
  • 2013-03-30
相关资源
最近更新 更多