NumPy数组的构造方式多种多样,其中最一般的方法是通过array()来构造,下面的例子是把一个普通列表转换为NumPy数组:
In [27]: import numpy as np
np.array([1,2,3])
Out[27]: array([1, 2, 3])
除了掌握上述利用显式构造列表生成NumPy数组的方法,更重要的是掌握一些特殊的数组生成方法,包括等差数列的生成、特殊矩阵的生成与随机数组的生成等方法。
1.等差数列
在日常生活中,等差数列随处可见,例如一组连续的奇数是等差数列,又如居民住宅楼每层所在的高度的数值大多呈等差数列。在NumPy中,生成数值均匀间隔数组的函数为np.linspace()和np.arange(),前者的3个参数代表了起始点、终止点(包含)与样本个数,后者的3个参数代表了起始点、终止点(不包含)与步长。
In [28]: np.linspace(1, 5, 5)
Out[28]: array([1., 2., 3., 4., 5.])
In [29]: np.arange(1, 5, 1) # 与range()一样不含末端点
Out[29]: array([1, 2, 3, 4])
2.特殊矩阵
NumPy提供了若干特殊的数组生成函数,这里介绍np.zeros()、np.ones()、np.eye()和np.full()这4种函数。其中,np.zeros()和np.ones()传入的参数为元组,其含义是数组每一个维度的大小。这里需要指出,NumPy数组可以是任意维度的,很多函数都会含有参数dim,指代这个函数操作作用于数组的哪一个或哪几个维度,后文会更加具体地说明其中的计算方法。下面构造一个2×3×4的全零数组:
In [30]: # 传入元组表示各维度大小,此例中外层二维,中层三维,内层四维
np.zeros((2, 3, 4))
Out[30]: array([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
In [31]: np.ones((2, 1, 2))
Out[31]: array([[[1., 1.]],
[[1., 1.]]])
全零函数np.zeros()是填充函数np.full()的特例,后者最简单的用法即在第一个参数中传入元组代表维数,在第二个参数中传入单个数值代表填充元素。
In [32]: # 传入元组表示各维度大小,10表示填充数值
np.full((2,3), 10)
Out[32]: array([[10, 10, 10],
[10, 10, 10]])
一般地,若np.full()的第一个参数为元组(d 0 ,…,d k −t ,…,d k −1 ),且第二个参数为维度大小为d k −t ×…×d k −1 的数组np_input,则np.full()函数返回的数组为将d 0 ×…×d k −t−1 个np_input按照d 0 ×…×d k −t−1 的布局进行拼接的结果,返回数组的维度大小为d 0 ×…×d k −t ×…×d k −1 。下面举一个具体的例子来说明。
In [33]: np_input = [[1, 2], [3, 4], [5, 6]]
np.full((2, 2, 3, 2), np_input)
out[33]: array([[[[1, 2],
[3, 4],
[5, 6]],
[[1, 2],
[3, 4],
[5, 6]]],
[[[1, 2],
[3, 4],
[5, 6]],
[[1, 2],
[3, 4],
[5, 6]]]])
在上面的这个例子中,数组np_input的大小为d 2 ×d 3 =3×2,布局维度为d 0 ×d 1 =2×2,从而最终得到的数组维度大小为2×2×3×2。
在某些情况下,用户希望生成和给定数组相同大小的全零矩阵、全一矩阵和填充矩阵,可以使用np.zeros_like()、np.ones_like()和np.full_like()来完成,用法如下:
In [34]: arr = [[1, 2], [3, 4]] # 给定的矩阵
np.zeros_like(arr)
Out[34]: array([[0, 0],
[0, 0]])
In [35]: np.ones_like(arr)
Out[35]: array([[1, 1],
[1, 1]])
In [36]: np.full_like(arr, [100, 200])
Out[36]: array([[100, 200],
[100, 200]])
除了重复元素的填充矩阵,单位矩阵也是一类常用的特殊矩阵,它可以通过np.eye()来获取,其中包含参数n,表示返回n×n的单位矩阵。
In [37]: np.eye(3) # 3×3的单位矩阵
Out[37]: array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
值得注意的是,np.eye()中的参数k表示主对角线的偏移距离,当k≥1时,主对角线上的元素1向上移动k个单位。
In [38]: np.eye(3, k=1)
Out[38]: array([[0., 1., 0.],
[0., 0., 1.],
[0., 0., 0.]])
注解
在某些时候,用户希望生成没有初始化的空数组,作为之后填充的容器,此时可以使用empty(shape,dtype)或empty_like(arr, dtype)来实现,其中shape为整数或元组,dtype为空数组的数据类型(即填入元素后希望得到的类型)。空数组将在第13章中被使用。
3.随机数组
随机数组广泛出现在数据建模或模型构造的过程中,例如神经网络中的Xavier初始化策略,贝叶斯统计学习中的蒙特卡洛模拟等。最常用的随机生成函数为uniform()、normal()、randint()和choice(),它们都来自np.random模块,分别表示均匀分布的随机数组、正态分布的随机数组、随机整数数组和随机列表抽样。
使用uniform(a,b,size)能够生成服从U[a,b]且数组维度为size的均匀分布的数组:
In [39]: np.random.uniform(-1, 2, 3) # 一维
Out[39]: array([ 0.59014468, -0.86907229, -0.21060346])
In [40]: np.random.uniform(-1, 2, (3, 3)) # 二维
Out[40]: array([[ 0.21562771, 1.79108142, -0.6553781 ],
[-0.36814731, -0.12093076, 0.51697754],
[-0.00473059, -0.18388791, -0.50126406]])
特别地,能够使用rand(d 1 ,d 2 ,...,d i )来生成服从U [0,1]的均匀分布的数组,其中d i 是相应维度的维数。
In [41]: np.random.rand(3) # 一维
Out[41]: array([0.47851726, 0.18083504, 0.75219483])
In [42]: np.random.rand(3, 3) # 二维
Out[42]: array([[0.04386193, 0.58884209, 0.33904954],
[0.33108071, 0.71849998, 0.03185896],
[0.1269714 , 0.1664075 , 0.26441228]])
使用normal()能够生成服从N[µ,σ]的正态分布的数组:
In [43]: mu, sigma = 3, 2.5
np.random.normal(mu, sigma, 3) # 一维
Out[43]: array([3.73778133, 8.41300871, 5.04849452])
In [44]: np.random.normal(mu, sigma, (3, 3)) # 二维
Out[44]: array([[5.42932796, 2.78689662, -1.45657554],
[6.06735553, -0.675319 , 4.84657806],
[6.54138637, 0.21483743, 3.04411302]])
特别地,能够使用randn(d 1 ,d 2 ,…)来生成服从N[0,1]的标准正态分布的数组:
In [45]: np.random.randn(3) # 一维
Out[45]: array([-1.38498657, 0.70879419, -0.31567701])
In [46]: np.random.randn(3, 3) # 二维
Out[46]: array([[-1.24285359, 0.08074262, -0.127839 ],
[-1.16307485, -0.80570142, -0.66898915],
[1.12926891 , -0.684949 , 0.97122595]])
使用randint()可以生成在给定整数范围内的呈离散均匀分布的数组,其中参数high对应的整数值不包含在内:
In [47]: low, high, size = 5, 15, (3, 3) # 生成5到14的随机整数
In [48]: np.random.randint(low, high, size)
Out[48]: array([[ 5, 6, 13],
[10, 9, 7],
[14, 9, 5]])
使用choice()可以从给定的列表中,以一定概率和方式抽取元素,当不指定概率时为等概率抽样。默认抽样方式为有放回抽样,此时replace=True,即同一个元素可能会被重复抽取:
In [49]: my_list = ['a', 'b', 'c', 'd']
In [50]: np.random.choice(
my_list,
3,
replace=False,
p=[0.1,0.7,0.1,0.1]
)# 结果一定不同
# <U1类型指不超过一个字符长度的字符串
Out[50]: array(['b', 'c', 'd'], dtype='<U1')
In [51]: # 此时,指定replace=False报错
np.random.choice(my_list, (3, 3))
Out[51]: array([['d', 'a', 'c'],
['b', 'c', 'a'],
['b', 'd', 'c']],dtype='<U1')
当返回的元素个数与原列表的元素个数相同时,不放回抽样等价于使用permutation()函数,即打散原列表:
In [52]: np.random.permutation(my_list)
Out[52]: array(['b', 'd', 'c', 'a'], dtype='<U1')
最后,需要提到的是随机种子,它能够固定随机数的输出结果。传入固定随机种子的做法在一些具有随机性的算法模型中是常见的。
In [53]: np.random.seed(0)
np.random.rand()
Out[53]: 0.5488135039273248
In [54]: np.random.seed(0)
np.random.rand()
Out[54]: 0.5488135039273248