NumPy 数组重塑形状和调整大小

Jinku Hu 2023年1月30日 2018年7月14日
  1. numpy.reshape()
  2. ndarray.reshape()
  3. reshape() 函数/方法内存
  4. numpy.resize()
NumPy 数组重塑形状和调整大小

NumPy 中有两个跟形状转换相关的函数(及方法) reshape 以及 resize,它们都能方便的改变矩阵的形状,但是它们之间又有一个显著的差别,我们会着重的来讲。

numpy.reshape()

我们先来看看会在各种数据计算中经常用到的改变数组形状的函数 reshape()

import numpy as np

arrayA = np.arange(8)
# arrayA = array([0, 1, 2, 3, 4, 5, 6, 7])

np.reshape(arrayA, (2, 4))
#array([[0, 1, 2, 3],
#       [4, 5, 6, 7]])

这里它把一个有 8 个元素的向量,转换成了形状为 (4, 2) 的一个矩阵。因为转换前后的元素数目一样,所以能够成功的进行转换,假如前后数目不一样的话,就会有错误 ValueError 报出。

In [1]: np.reshape(arrayA, (3, 4))
    ---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: cannot reshape array of size 8 into shape (3,4)

我们仔细看转换后的数据,第一行是 arrayA 的前四个数据,第二行是 arrayA 的后四个数据,也就是它是按行来填充数据的,有些时候我们需要把数据填充的顺序改成按列来填充,那我们需要改变函数中的另外一个输入参数 order=

In [1]: np.reshape(arrayA, (2, 4), order='F')
Out[1]: array([[0, 2, 4, 6],
        	  [1, 3, 5, 7]])

order 默认的参数是 C,也就是按行填充,当参数变为 F 时,就变成按列填充。

说明
这里用按行或者按列来填充,是为了便于理解,其实具体的不同是由于函数是按照类似 C 的索引顺序还是按照类似 Fortan 的索引顺序来读取输入矩阵的内容,具体可以参照 Numpy reshape 的官方说明文档

ndarray.reshape()

除了用 NumPy 的 reshape 函数外,你还可以用数据 ndarray 对象里面的 reshape 方法来进行矩阵形状变化。它的语法跟 numpy.reshape() 类似,区别在于不用输入矩阵作为参数了。

In [1]: arrayB = arrayA.reshape((2, 4))
    
In [2]: arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [1]: arrayA
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7])    

可以看得出,用法跟 reshape 函数类似。这里想强调的一点是 ndarray.reshape() 方法不会改变原矩阵的数据、形状等,而只是返回一个新的矩阵。

reshape() 函数/方法内存

reshape 函数或者方法生成的新数组和原始数组是共用一个内存的,有点类似于 Python 里面的 shallow copy,当你改变一个数组的元素,另外一个数组的元素也相应的改变了。

In [1]: arrayA = np.arange(8)
    	arrayB = arrayA.reshape((2, 4))
        arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [2]: arrayA[0] = 10
    	arrayA
Out[2]: array([10, 1, 2, 3, 4, 5, 6, 7]) 
In [3]: arrayB    
Out[3]:	array([[10, 1, 2, 3],
       		[4, 5, 6, 7]])    

numpy.resize()

numpy.resize()reshape 类似,可以改变矩阵的形状,但它有几点不同,

  1. 没有 order 参数了,它只有跟 reshape 里面 order='C'的方式。
  2. 假如要转换成的矩阵形状中的元素数量跟原矩阵不同,它会强制进行转换,而不报错。

我们具体来看一下第二点

In [1]: arrayA = np.arange(8)
        arrayB = np.resize(arrayA, (2, 4))
Out[1]: array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

这是尺寸大小正常情况,跟 reshape 的结果是一样的。

In [1]: arrayC = np.resize(arrayA, (3, 4))
    	arrayC
Out[1]: array([[0, 1, 2, 3],
       [4, 5, 6, 7],
       [0, 1, 2, 3]])
In [2]: arrayD = np.resize(arrayA, (4, 4))
    	arrayD
Out[2]: array([[0, 1, 2, 3],
       [4, 5, 6, 7],
       [0, 1, 2, 3],
       [4, 5, 6, 7]])

当新形状行数超出的话,它会开始重复填充原始矩阵的内容,实现形状大小的自动调整而不报错。

In [1]: arrayE = np.resize(arrayA, (2, 2))
		arrayE
Out[1]: array([[0, 1],
       [2, 3]])    
In [2]: np.resize(arrayA, (1,4))
Out[2]: array([[0, 1, 2, 3]])

当新形状比原形状所需要的数据小的时候,它会从原矩阵读读取出所需要个数的数据,然后按照先按行填充的方式来对新矩阵元素赋值。

注意
当新矩阵的形状在原矩阵形状内时,比如 (2, 2)(2, 4) 的情形,新矩阵的元素不是按照子集来获取的。比如上例中,np.resize(arrayA, (2, 2)) 不是 array([[0, 1], [4, 5])。假如你需要这样的截取方法,我们会在后续的索引和切片操作中介绍到的。
In [1]: np.resize(arrayA, (3, 5))
Out[1]: array([[0, 1, 2, 3, 4],
       [5, 6, 7, 0, 1],
       [2, 3, 4, 5, 6]])    

当新形状比原形状要大时,它会先按行去填充旧矩阵的元素,并且在元素被用光后,再重复的填充这些元素,直到新矩阵的最后一元素。

resize 函数/方法内存

reshape 不一样的是,resize 函数/方法生成的新数组跟原数组并不共用一个内存,所以彼此元素的改变不会影响到对方。

In [1]: arrayA = np.arange(8)
    	arrayB = arrayA.reshape((2, 4))
        arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [2]: arrayA[0] = 10
    	arrayA
Out[2]: array([10, 1, 2, 3, 4, 5, 6, 7]) 
In [3]: arrayB    
Out[3]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])    
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn