Numpy reshape()函数何时复制数组
在Numpy中,reshape()函数是常用的一种数组重塑工具。它可以改变数组的维度和形状,但有时候它会操作原始数组,有时则会创建一个新的数组。那么,什么时候reshape()函数会改变原始数组,什么时候会创建新的数组呢?这篇文章将从以下几个方面进行讨论。
阅读更多:Numpy 教程
Numpy数组的内存模型
在理解reshape()函数如何复制数组之前,我们需要先了解一下Numpy数组的内存模型。在Numpy中,数组是由连续的内存块构成的,其中每个元素都是同一类型的。数组的某个元素可以通过其索引来访问,其索引是一个整数元组,对应于各个维度的下标。
下面是一个一维数组的示例:
数组的每个元素都在内存中占用一定的空间,它们之间是连续的。这个数组是由4个整数元素构成的,每个整数元素占用4个字节的内存,因此数组总共占用16个字节的内存空间。
对于多维数组,它们在内存中也是连续的,但不同维度之间的元素并不一定在物理上相邻。假设有一个2×3的二维数组,其内存模型可能是这样的:
这个数组总共有6个元素,每个元素占用4个字节的内存,因此数组总共占用24个字节的内存空间。虽然第1行和第2行的元素并不在物理上相邻,但这些元素在逻辑上是按照行优先的顺序存储的。
reshape()函数的参数
在介绍reshape()函数何时会复制数组之前,我们需要先了解一下reshape()函数的参数。该函数的语法如下:
其中,参数a是要进行重塑的数组,newshape是一个整数元组,它指定了新的形状。例如,如果a是一个长度为12的一维数组,我们可以将它重塑为一个3×4的二维数组,如下所示:
如果newshape参数中包含了0或-1,则该维度的长度将自动计算,例如:
注意,newshape参数指定的形状必须与原数组中元素的数量一致,否则将会抛出ValueError异常。另外,reshape()函数还有一个可选参数order,它指定了重塑后数组中元素的存储顺序。如果order等于’C’,则使用C语言的默认顺序,也就是按照行优先存储(即数组中相邻的元素在同一行上),如果order等于’F’,则使用Fortran语言的默认顺序,也就是按照列优先存储(即相邻元素在同一列上)。
何时原地修改数组
当我们使用reshape()函数创建一个新数组时,新数组的内存将被分配,并将原数组的数据复制到新数组中。这种情况下,原数组和新数组之间没有任何共享的内存块。例如:
在上面的代码中,我们创建了一个2×2的二维数组a,并使用reshape()函数将其重塑为一个一维数组b。当我们修改a中的值时,b中的值没有改变,是因为a和b内存中的数据块是相互独立的。
事实上,只有当重塑后的数组在内存上与原始数组共享同一个数据块时,才会发生原地修改。我们可以通过数组的flags属性来判断一个数组是否拥有共享内存块的能力。如果数组的flags属性中的OWNDATA标志位为True,则表示该数组拥有独立的数据块。例如:
在上面的代码中,我们使用order参数将重塑后的数组按照列优先存储。此外,我们还使用flags属性检查了a和b是否共享内存块。由于order=’F’,所以b和a的存储顺序相同,但是b的元素顺序不同。当我们修改a中的值时,b中的值没有发生变化,是因为a和b没有共享内存块。
另外,如果新形状与原数组中的元素数量相同,且没有指定order参数,则默认使用C语言的行优先顺序,并在原数组中原地修改。例如:
在上面的代码中,我们重塑了一个与a中元素数量相同的一维数组b,但是没有指定order参数。由于默认使用C语言的行优先顺序,并且新形状与原数组相同,所以b和a共享同一个内存块,并且当我们修改a中的值时,b中的值也会随之改变。
何时创建新的数组
我们已经知道,当重塑后的数组与原始数组没有共享内存块时,将创建一个新的数组。那么,什么情况下重塑后的数组会与原始数组没有共享内存块呢?
一种情况是newshape中包含了-1或者0。这种情况下,新数组的形状会自动计算。例如:
在上面的代码中,我们重塑了一个新的形状为(1,-1)的数组b。由于newshape中包含了-1,因此Numpy会自动计算出b的形状为(1,4)。由于b的形状和元素数量与a不同,因此b会被创建为一个新的数组。
另一种情况是当newshape所需内存空间超过了原始数组可以提供的内存空间时,也会创建一个新的数组。例如:
在上面的代码中,我们试图将a重塑为一个新的形状分别为(5,)和(1,100)的数组。由于这些形状需要比a中更多的内存空间,因此Numpy会创建一个新的数组,也就是说,newshape和原数组元素数量不同,不会进行原地修改。
总结
本文从Numpy数组的内存模型、reshape()函数的参数及操作等方面阐述了Numpy何时会复制数组。我们知道,新数组的创建与否取决于新形状和原数组元素数量是否相同,以及重塑后的数组是否与原始数组共享内存块。如果新形状不同或者newshape所需内存空间超过了原始数组可以提供的内存空间,将会创建一个新的数组;如果要进行原地修改,则重塑后的数组必须与原数组共享内存块。了解这些特点可以更好地解决一些操作Numpy数组时的问题。