NumPy 基础教程

点点圈 提交于 2020-03-14 15:37:47

注:本文是我在阅读 An introduction to Numpy and Scipy 时,一边实践一边记下的笔记。个人觉得这篇介绍 NumPy 的英文文档写的很好,因此如果可以,请停止阅读本文,转去参考此英文文档。

目录


Numpy 是在 Python 中进行科学计算所依赖的核心库,它提供高性能的多维数组对象,已经大量便于计算的方法。

可以在 Installing packages 找到安装方法,而后在 Python 中采用如下方式引入它:

import numpy as np

Arrays

NumPy 中的核心对象是多维数组 np.ndarray,它有一个别名 np.array,这个数组和 Python 中的 array.array 不同,Python 中的 array 只支持一维,而 np.array 支持多维,且提供了更多的方法。

np.array 中只能存放同种类型的数据,如下,使用 np.array 创建数组,第二个参数需指明数组中元素的类型,否则 np.array 会自动推测数据类型:

>>> a = np.array([1, 4, 5, 8], float)
>>> a
array([ 1., 4., 5., 8.])
>>> type(a)
<type 'numpy.ndarray'>

np.ndarray 含下面几个常用的属性:

  • ndarray.ndim:数组的维度
  • ndarray.shape:数组的形状
  • ndarray.size:数组中所有元素的数量
  • ndarray.dtype:数组中元素的类型
  • ndarray.itemsize:每个元素的大小
  • ndarray.data:元素数据 buffer

创建多维数组:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a[0, 0]
1

数组创建后可以使用 reshape 来调整维度,可以使用 shape 来得到数组的维度:

>>> a = np.array(range(10), float)
>>> a
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> a = a.reshape((5, 2))
>>> a
array([[ 0., 1.],
    [ 2., 3.],
    [ 4., 5.],
    [ 6., 7.],
    [ 8., 9.]])
>>> a.shape
(5, 2)

Python中 list 的切片操作,同样适用,对个维度需要使用 , 隔开:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a[1,:] # 第二行的所有列
array([ 4., 5., 6.])
>>> a[:,2]
array([ 3., 6.])
>>> a[-1:,-2:] # 最后一行的后两列
array([[ 5., 6.]])

copy 能够生成数组的一份拷贝:

>>> a = np.array([1, 2, 3], float)
>>> b = a
>>> c = a.copy()
>>> a[0] = 0
>>> a
array([0., 2., 3.])
>>> b
array([0., 2., 3.])
>>> c
array([1., 2., 3.])

fill 方法给数组统一填充某个值:

>>> a = array([1, 2, 3], float)
>>> a
array([ 1., 2., 3.])
>>> a.fill(0)
>>> a
array([ 0., 0., 0.])

transpose 方法用来得到二维数组的转置:

>>> a = np.array(range(6), float).reshape((2, 3))
>>> a
array([[ 0., 1., 2.],
 [ 3., 4., 5.]])
>>> a.transpose()
array([[ 0., 3.],
       [ 1., 4.],
       [ 2., 5.]])

多维数组能够被展平:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a
array([[ 1., 2., 3.],
 [ 4., 5., 6.]])
>>> a.flatten()
array([ 1., 2., 3., 4., 5., 6.])

多个数组可以使用 concatenate 拼接起来:

>>> a = np.array([1,2], float)
>>> b = np.array([3,4,5,6], float)
>>> c = np.array([7,8,9], float)
>>> np.concatenate((a, b, c))
array([1., 2., 3., 4., 5., 6., 7., 8., 9.])

如果有多个维度,默认(不指定 axis 时)是按第一个维度拼接的

>>> a = np.array([[1, 2], [3, 4]], float)
>>> b = np.array([[5, 6], [7,8]], float)
>>> np.concatenate((a,b))
array([[ 1., 2.],
       [ 3., 4.],
       [ 5., 6.],
       [ 7., 8.]])
>>> np.concatenate((a,b), axis=0)
array([[ 1., 2.],
       [ 3., 4.],
       [ 5., 6.],
       [ 7., 8.]])
>>> np.concatenate((a,b), axis=1)
array([[ 1., 2., 5., 6.],
       [ 3., 4., 7., 8.]])

一个数组的维度,可以借助 newaxis 来增加:

>>> a = np.array([1, 2, 3], float)
>>> a
array([1., 2., 3.])
>>> a[:,np.newaxis]
array([[ 1.],
       [ 2.],
       [ 3.]])
>>> a[:,np.newaxis].shape
(3,1)
>>> a[np.newaxis,:]
array([[ 1., 2., 3.]])
>>> a[np.newaxis,:].shape
(1,3)

其他创建数组的方法

np.arangerange 类似,只是前者返回一个数组:

>>> np.arange(5, dtype=float)
array([ 0., 1., 2., 3., 4.])
>>> np.arange(1, 6, 2, dtype=int)
array([1, 3, 5])

zerosones 用来创建指定维度的数组,并填充 0 和 1:

>>> np.ones((2,3), dtype=float)
array([[ 1., 1., 1.],
       [ 1., 1., 1.]])
>>> np.zeros(7, dtype=int)
array([0, 0, 0, 0, 0, 0, 0])

zeros_likeones_like 创建和给定数组维度相同的新数组,并填充 0 和 1:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> np.zeros_like(a)
array([[ 0., 0., 0.],
       [ 0., 0., 0.]])
>>> np.ones_like(a)
array([[ 1., 1., 1.],
       [ 1., 1., 1.]])

identity 方法用来一个指定阶数的单位矩阵:

>>> np.identity(4, dtype=float)
array([[ 1., 0., 0., 0.],
       [ 0., 1., 0., 0.],
       [ 0., 0., 1., 0.],
       [ 0., 0., 0., 1.]])

eye 方法用来创建一个指定阶数的矩阵,其指定(k)对角线上值为 1:

>>> np.eye(4, k=1, dtype=float)
array([[ 0., 1., 0., 0.],
       [ 0., 0., 1., 0.],
       [ 0., 0., 0., 1.],
       [ 0., 0., 0., 0.]])

数组的数学运算

当将标准数学运算作用于数组上时,这样运算会分别在数组的各个元素上进行,这意味着进行运算的两个数组的大小需要一致:

>>> a = np.array([1,2,3], float)
>>> b = np.array([5,2,6], float)
>>> a + b
array([6., 4., 9.])
>>> a – b
array([-4., 0., -3.])
>>> a * b
array([5., 4., 18.])
>>> b / a
array([5., 1., 2.])
>>> a % b
array([1., 0., 3.])
>>> b ** a 
array([5., 4., 216.])

对于二维数组间的乘法,依然是元素与元素相乘,而非矩阵乘法:

>>> a = np.array([[1,2], [3,4]], float)
>>> b = np.array([[2,0], [1,3]], float)
>>> a * b
array([[2., 0.], [3., 12.]])

当数组长度不匹配的时候就会出错:

>>> a = np.array([1,2,3], float)
>>> b = np.array([4,5], float)
>>> a + b
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape

但是,数组的维度不匹配时,NumPy 有一种广播机制,能够让运行进行下去,通常是将现有的数组进行重复,以完成运算:

>>> a = np.array([[1, 2], [3, 4], [5, 6]], float)
>>> b = np.array([-1, 3], float)
>>> a
array([[ 1., 2.],
       [ 3., 4.],
       [ 5., 6.]])
>>> b
array([-1., 3.])
>>> a + b
array([[ 0., 5.],
       [ 2., 7.],
       [ 4., 9.]])

这里一维数组 b 被扩展为二维,扩展后与数组 a 有相同的 shape,如此以来就可以进行运算。

在基本数学运算符的基础上,NumPy 增加了一些常见的属性数学运算函数:abs, sign, sqrt, log, log10, exp, sin, cos, tan, arcsin, arccos, arctan, sinh, cosh, tanh, arcsinh, arccosh, arctanh

>>> np.sqrt(a)
array([ 1., 2., 3.])

floor, ceil, rint 分别对浮点数向下、向上、四舍五入取整。

>>> a = np.array([1.1, 1.5, 1.9], float)
>>> np.floor(a)
array([ 1., 1., 1.])
>>> np.ceil(a)
array([ 2., 2., 2.])
>>> np.rint(a)
array([ 1., 2., 2.])

np.pi 为圆周率,np.e 为自然常数:

>>> np.pi
3.1415926535897931
>>> np.e
2.7182818284590451

数组的迭代

可以像迭代 list 一样迭代 np.array

>>> a = np.array([1, 4, 5], int)
>>> for x in a:
... print x
... <hit return>
1
4
5

对于多维数组,迭代会在第一维上进行:

>>> a = np.array([[1, 2], [3, 4], [5, 6]], float)
>>> for x in a:
... print x
... <hit return>
[ 1. 2.]
[ 3. 4.]
[ 5. 6.]

对于多维数组迭代,可以使用解构:

>>> a = np.array([[1, 2], [3, 4], [5, 6]], float)
>>> for (x, y) in a:
... print x * y
... <hit return>
2.0
12.0
30.0

基本的数组操作

有些运算需要作用于整个数组上,比如求和、求均值等。

>>> a = np.array([2, 4, 3], float)
>>> a.sum() # 求和
9.0
>>> a.prod() # 所有元素连乘
24.0

可以在 array 元素上调用方法,也可以使用在 np 上的函数:

>>> np.sum(a)
9.0
>>> np.prod(a)
24.0

在涉及统计的时候,常常会用到下列函数:

>>> a = np.array([2, 1, 9], float)
>>> a.mean() # 求均值
4.0
>>> a.var() # 方差(variance)
12.666666666666666
>>> a.std() # 标准差
3.5590260840104371

可以使用 maxmin 求最大、最小值:

>>> a = np.array([2, 1, 9], float)
>>> a.min()
1.0
>>> a.max()
9.0

argmaxargmin 返回数组中,最大和最小元素的下标:

>>> a = np.array([2, 1, 9], float)
>>> a.argmin()
1
>>> a.argmax()
2

对于多维数组,上面提到的针对数组的方法,可以接受一个参数 axis,用来指明这些方法作用的轴。即,对于二维数组而言,是针对行,还是针对列。

>>> a = np.array([[0, 2], [3, -1], [3, 5]], float)
>>> a.mean(axis=0)
array([ 2., 2.])
>>> a.mean(axis=1)
array([ 1., 1., 4.])
>>> a.min(axis=1)
array([ 0., -1., 3.])
>>> a.max(axis=0)
array([ 3., 5.])

对于多维数组,如果不指定 axis ,那么结果相当于将数组会被展平为一维数组,然后执行该方法。比如:

>>> a = np.array([[0, 2], [3, -1], [3, 5]], float)
>>> a.sum() # 对所有元素求和
12.0

list 一样,np.array 也可以进行排序:

>>> a = np.array([6, 2, 5, -1, 0], float)
>>> sorted(a)
[-1.0, 0.0, 2.0, 5.0, 6.0]
>>> a.sort()
>>> a
array([-1., 0., 2., 5., 6.])

使用 clip 将数组中的值,压缩到一个指定范围。

>>> a = np.array([6, 2, 5, -1, 0], float)
>>> a.clip(0, 5) # 小于 0 的数被置为 0,大于 5 的被置为 5
array([ 5., 2., 5., 0., 0.])

使用 unique 对数组去重:

>>> a = np.array([1, 1, 4, 5, 5, 5, 7], float)
>>> np.unique(a)
array([ 1., 4., 5., 7.])

对于二维数组,diagonal 方法能够取得对角线上的元素:

>>> a = np.array([[1, 2], [3, 4]], float)
>>> a.diagonal() # 并不要求二维数组为方阵
array([ 1., 4.])

比较操作

比较运算能够作用于数组的各个元素上,得到的结果是由布尔值构成的数组:

>>> a = np.array([1, 3, 0], float)
>>> b = np.array([0, 3, 2], float)
>>> a > b
array([ True, False, False], dtype=bool)

>>> a == b
array([False, True, False], dtype=bool)
>>> a <= b
array([False, True, True], dtype=bool)

>>> a = np.array([1, 3, 0], float)
>>> a > 2 # 可以和一个常数进行比较
array([False, True, False], dtype=bool)

anyall 方法能够作用于数组上:

>>> c = np.array([ True, False, False], bool)
>>> any(c) # 数组中是否至少有一个 True
True
>>> all(c) # 数组中是否全为 True
False

对于布尔数组,可以利用 logical_and, logical_or, logical_not,在上面进行与或非等布尔运算:

>>> a = np.array([1, 3, 0], float)
>>> np.logical_and(a > 0, a < 3)
array([ True, False, False], dtype=bool)
>>> b = np.array([True, False, True], bool)
>>> np.logical_not(b)
array([False, True, False], dtype=bool)
>>> c = np.array([False, True, False], bool)
>>> np.logical_or(b, c)
array([ True, True, False], dtype=bool)

where 可以实现在两个数组中选择元素来构造新数组:

>>> a = np.array([1, 3, 0], float)
>>> np.where(a != 0, 1 / a, a)
array([ 1. , 0.33333333, 0. ])

这里 a != 0 会得到一个含布尔值的数组 [False, True, False]where 根据这个数组中的值,如果为 False 就选第三个数组中对应的值,否则选择第二个数组中对应的值。

当然,也可以用于常数:

>>> np.where(a > 0, 3, 2)
array([3, 3, 2])

有时候需要得出数组中符合某种条件的元素的下标,此时可以使用 nonzero 方法:

>>> a = np.array([[1, 0], [0, 3]], float)
>>> a.nonzero()
(array([0, 1]), array([0, 1]))

nonzero 返回非 0 元素的下标,其结果是一个元组,元组的长度和数组 a 的维度相当,元组中每个元素又是一个数组,其中第一个数组中的值为第一个维度的下标,第二个数组为第二维度,以此类推。上例中结果说明 a[0, 0]a[1, 1] 为非零元素。

对于布尔值构成的数组,nonzero 返回值为 True 的元素的下标,因此可以用下面的方法,找出值大于 2 的元素的下标:

>>> a = np.array([[1, 0], [0, 3]], float)
>>> np.nonzero(a > 2)
(array([1]), array([1]))

要想找出符合条件的值,而非下标,可以将 nonzero 的结果作为下标来取值:

>>> a = np.array([[1, 0], [0, 3]], float)
>>> a[np.nonzero(a > 2)]
array([3.])

利用 np.isnannp.isfinite 可以判断元素值是否为 NaN 或 无穷:

>>> a = np.array([1, np.NaN, np.Inf], float)
>>> a
array([ 1., NaN, Inf])
>>> np.isnan(a)
array([False, True, False], dtype=bool)
>>> np.isfinite(a)
array([ True, False, False], dtype=bool)

数组元素选择和操作

要取得 np.array 中的值,可以在 [] 中放入适当的下标:

>>> a = np.array([[1, 0], [0, 3]], float)
>>> a[0, 0] # 使用两个维度上的下标来索引
1.0
>>> a[(0, 0)] # 使用元组
1.0

# 两个数组,分别指定两个维度的下标,这样可以一次取出多个值
>>> a[([0, 1], [0, 1])]
array([1., 3.])

# 使用数组也能达到同样效果
>>> a[[0, 1], [0, 1]]
array([1., 3.])

另外 np.array 还支持使用 np.array 来索引,如使用布尔数组来索引:

>>> a = np.array([[6, 4], [5, 9]], float)
>>> a >= 6
array([[True, False],
       [False, True]], dtype=bool)
>>> a[a >= 6]
array([ 6., 9.])

这里 a >= 6a 有相同的 shapea[a >= 6] 则返回 a >=6 中值为 True 的位置在 a 中所对应的元素。

还可以使用更加复杂的表达式:

>>> a[np.logical_and(a > 5, a < 9)]
>>> array([ 6.])

但本质就是通过一个含布尔值的 np.array 来进行索引。

一个 np.array 作为另一个的下标:

>>> a = np.array([2, 4, 6, 8], float)
>>> b = np.array([0, 0, 1, 3, 2, 1], int)
>>> a[b]
array([ 2., 2., 4., 8., 6., 4.])

take 方法,可以帮助取值:

>>> a = np.array([[1, 2], [3, 4]], float)
>>> a
array([[1., 2.],
       [3., 4.]])
>>> a.take(3) # 相当于 a[1][1]
4.0
>>> a.take([0, 1]) 
array([1., 2.])
>>> a.take(0, axis=0) # 相当于 a[0, :]
array([1., 2.])
>>> a.take(0, axis=1) # 相当于 a[:, 0]
array([1., 3.])

对于多维数组,take 如果不传入 axis 参数,就相当于将多维数组展平为一维数组后,再索引一样。

如果想要给指定下标的位置赋值,可以用到 put 方法:

>>> a = np.array([0, 1, 2, 3, 4, 5], float)
>>> b = np.array([9, 8, 7], float)
>>> a.put([0, 3], b)
>>> a
array([ 9., 1., 2., 8., 4., 5.])

put 的第一个参数为索引,第二个参数为要赋的值。这里的下标是数组展平后的索引。注意到上例中中,7 并没有被赋入 a,这是因为索引只有两个。

当索引够长,而值不够是,值能够进行扩展。

>>> a = np.array([[1, 2], [3, 4]], float)
>>> a.put([0, 1, 2, 3],  [1,2])
>>> a
array([[1., 2.],
       [1., 2.]])
>>> a.put([0, 1, 2, 3],  5)
>>> a
array([[5., 5.],
       [5., 5.]])

向量和矩阵的数学运算

完成向量的点乘和矩阵的乘法,需要用到 dot 方法:

>>> a = np.array([1, 2, 3], float)
>>> b = np.array([0, 1, 1], float)
>>> np.dot(a, b)
5.0

对于向量,inner, outer, cross 可用于计算內积、外积、和叉乘:

>>> a = np.array([1, 4, 0], float)
>>> b = np.array([2, 2, 1], float)

>>> np.outer(a, b)
array([[ 2., 2., 1.],
       [ 8., 8., 4.],
       [ 0., 0., 0.]])
>>> np.inner(a, b)
10.0
>>> np.cross(a, b)
array([ 4., -1., -6.])

np.linalg 中有大量涉及线性代数的计算函数,如:

>>> a = np.array([[4, 2, 0], [9, 3, 7], [1, 2, 1]], float)
>>> a
array([[ 4., 2., 0.],
       [ 9., 3., 7.],
       [ 1., 2., 1.]])
>>> np.linalg.det(a) # 得出行列式的值
-53.999999999999993

>>> vals, vecs = np.linalg.eig(a) # 得出特征值和特征向量

随机数

在子模块 np.random 中有若干函数来支持随机数生成。其中用到伪随机数生成算法,需要设置种子:

>>> np.random.seed(293423)

生成一组介于 [0, 1) 的随机数:

>>> np.random.rand(5)
array([ 0.40783762, 0.7550402 , 0.00919317, 0.01713451, 0.95299583])

也可以是多维的:

>>> np.random.rand(2,3)
array([[0.02258773, 0.45209532, 0.91634883],
       [0.45311731, 0.23780582, 0.03024893]])

生成单个随机数,介于[0, 1)

>>> np.random.random()
0.8573439034930186
>>> np.random.rand()
0.6180841214951669

生成 [min, max) 间的整数:

>>> np.random.randint(5, 10)
8
>>> np.random.randint(5, 10, size=3)
array([9, 5, 6])

生成具有特定分布类型的随机数,比如生成具有正态分布的随机数:

>>> np.random.normal(size=5)
array([-1.67215088, 0.65813053, -0.70150614, 0.91452499, 0.71440557])

# 可以调整方差和期望, μ = 1.5, δ^2 = 16.0
>>> np.random.normal(1.5, 4.0)
0.83636555041094318

打乱数组中的值:

>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.random.shuffle(a)
>>> a
array([7, 6, 9, 3, 2, 4, 8, 0, 1, 5])
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!