Numpy (Numerical Python)是Python 中比较重要的一个库,它提供了高效的多维数组对象和相应的操作函数,是Python 科学计算的基础库之一。同时,Pytorch 中关于 Tensor的操作与Numpy原理类似。 so,let's go。
目录
1. 数组创建
创建数组有几种方式,包括从其他Python结构转换以及利用Numpy相关方法。
- 将元组或者列表转换:
import numpy as np x = np.array([2,3,1,0]) y = np.array((1,2,3))
- 利用numpy 内置的一些函数:
- np.zeros: 创建一个全用0填充的数组;
- np.ones: 创建一个全用1填充的数组;
- np.arange: 创建一个按规律递增的数组;
- np.linspace: 创建制定数量元素的数组;
2. 数组属性
Numpy的主要对象是多维数组,也被称为ndarray,其主要属性如下:
- ndarray.ndim: 数组维度的个数;
- ndarray.shape: 数组的维度。这是一个整数的元组,表示每个维度上数组的长度。 此外,shape 元组的长度就是 ndim;
- ndarray.size: 数组元素的总数。这等于shape 的元素的乘积;
- ndarray.dtype: 描述数组中元素类型的对象;
- ndarray.itemsize: 数组中每个元素的字节大小;
- ndarray.data: 缓冲区包含数组的实际元素;
3. 数组索引和切片
数组索引是指使用方括号[]来索引数组值。
前言 ——数组的拷贝和视图
在计算和操作数组时,有时会将数据复制到新数组中,有时候不会。有以下三种情况:
- 完全不复制:
>>> a = np.arange(12) >>> b = a # no new object is created >>> b is a # a and b are two names for the same ndarray object True >>> b.shape = 3,4 # changes the shape of a >>> a.shape (3, 4)
- 视图或者浅拷贝:不同的数组对象共享相同的数据
>>> c = a.view() >>> c is a False >>> c.base is a # c is a view of the data owned by a True >>> c.flags.owndata False >>> >>> c.shape = 2,6 # a's shape doesn't change >>> a.shape (3, 4) >>> c[0,4] = 1234 # a's data changes >>> a array([[ 0, 1, 2, 3], [1234, 5, 6, 7], [ 8, 9, 10, 11]])
- 深拷贝:生成数组及其数据的完整副本
>>> d = a.copy() # a new array object with new data is created >>> d is a False >>> d.base is a # d doesn't share anything with a False >>> d[0,0] = 9999 >>> a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]])
3.1 单个元素索引
和Python 序列完全相同,从0开始,并接受从数组末尾开始索引的负索引。
import numpy as np
x = np.arange(10)
print(x[2])
print(x[-2])
与列表和元组不同的是,numpy 数组支持多维数组的多维索引,即不需要将每个维度的索引都单独用方括号括出来。
import numpy as np
x = np.array([[1,2,3], [4,5,6]])
print(x[1,1])
此外,在多维数组索引过程中,如果索引少于维度,则会获得一个子数组。
import numpy as np
x = np.arange(10).reshape(2,5)
print(x[0])
3.2 索引数组
Numpy 数组可以使用其他数组进行索引。注意,返回的是原始数据的副本,而不是切片获取的视图。
索引数组必须是整数类型,数组中每个值代表索引值(允许使用负数),返回的是与索引数组相同形状的数组。
>>> x = np.arange(10,1,-1)
>>> x
array([10, 9, 8, 7, 6, 5, 4, 3, 2])
>>> x[np.array([3, 3, 1, 8])]
array([7, 7, 9, 2])
>>> x[np.array([[1,1],[2,3]])]
array([[9, 9],
[8, 7]])
3.3 布尔或者“掩码”索引数组
- 布尔数组的形状和索引数组的形状相同,返回的是一个一维数组:
import numpy as np y = np.arange(35).reshape(5,7) b = y>20 print(y[b]) #array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])
- 索引数组的维数大于布尔数组的维度,返回的是多维数组:
>>> b[:,5] # use a 1-D boolean whose first dim agrees with the first dim of y array([False, False, False, True, True]) >>> y[b[:,5]] array([[21, 22, 23, 24, 25, 26, 27], [28, 29, 30, 31, 32, 33, 34]])
当布尔数组的维数小于被索引的数组时,相当于y[b, ...], 结果的维度包含:布尔数组中True元素的数量,以及被索引数组的其余维度。
3.4 结构索引工具
为来便于数组形状与表达式和赋值轻松匹配,可以在数组索引中使用np.newaxis 来添加大小为1的新维度。
>>> x = np.arange(5)
>>> x[:,np.newaxis] + x[np.newaxis,:]
array([[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]])
4. 数组广播
首先解释一下什么是数组广播。在对两个数组进行算术运算的时候,它们的形状必须相同,否则会报错。但是在某些情况下,我们希望对不同形状的数组进行运算,例如将一个数组的每个元素加上一个常数。而数组广播可以允许通用功能以有意义的方式处理不具有完全相同形状的输入。
在两个数组上运行时,Numpy会逐元素地比较它们的形状(从最后一个维度开始,向前发展)。具体来说,数组广播的规则如下:
- 如果两个数组的维度数不同,将维度数较小的数组用1填充,直到维度数相同;
- 如果两个数组在某个维度上的大小不同,但是在该维度上,其中一个数组的维度大小为1,则可以通过复制该数组使它们大小相同;
- 如果两个数组在某个维度上的大小不同,且都不为1,则会报错。
下图展示了数组广播的例子:
- x 和 y 的 shape 分别是 (4,) 和 (5,) ,两个只有一个维度,且都不为1,在进行相加操作的时候会报错(见红色框);
- 接下来看第二个例子(见绿框),xx和y的shape分别是(4,1)和(5,)。从后往前对比维度,xx的最后一个是1,y只有一个,是5。所以xx会在最后一个维度上进行复制,变成(4,5); 同理,由于y只有一个维度,所以y首先会增加一个维度,变成(1,5),然后在第一个维度上进行复制,变成(4,5);
- 最后是进行 x + z 的操作。x和z的shape分别是(4,) 和(3,4)。可以看到,它们最后一个维度是相同的,所以只需要对x增加一个维度,变成(1,4),然后在第一个维度上进行复制,变成(3,4),就可以完成操作。