NumPy 本身不提供建模和科学函数。NumPy 属于比较基础的库,虽然也提供一些诸如读取数据之类的方法,但是很多时候我们会使用 Pandas(基于 NumPy)去实现。

NumPy ndarray: 多维数组对象

NumPy的核心特征之一:N-维数组对象 ndarray;ndarray 是一个通用的多维同类数据容器。

ndarray 属性

ndarray shape 属性,用来表征数组每一维度的数量
ndarray ndim 属性,数组的维度
ndarray dtype 属性,描述数组的数据类型

ndarray 生成

array

将输入数据(列表、元组、数组以及其它序列)转换为 ndarray,如不显式指明数据类型,会自动推断;默认复制所有的输入数据。

In [1]: import numpy as np In [2]: data1 = [6, 7.5, 8, 0, 1] In [3]: arr1 = np.array(data1) In [4]: arr1 Out[4]: array([6. , 7.5, 8. , 0. , 1. ])

asarray

array 和 asarray 都可以将结构数据转化为ndarray。
array 和 asarray 主要区别就是当数据源是 ndarray 时,array 仍然会 copy 出一个副本,而 asarray不会。

如:

In [14]: arr1=np.ones((3,3)) In [15]: arr2=np.array(arr1) In [16]: arr3=np.asarray(arr1) In [17]: arr1[1]=2 In [18]: arr1 Out[18]: array([[1., 1., 1.], [2., 2., 2.], [1., 1., 1.]]) In [19]: arr2 Out[19]: array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) In [20]: arr3 Out[20]: array([[1., 1., 1.], [2., 2., 2.], [1., 1., 1.]])

arange

arange 是 Python 内建函数 range 的数组版,返回一个数组。

In [23]: np.arange(10) Out[23]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

ndarray 数据类型

根据 dtype 我们可以知道 ndarray 的数据类型。

astype 可以显式转换数据类型:

In [33]: arr1 Out[33]: array([1, 2, 3, 4, 5]) In [34]: arr1.dtype Out[34]: dtype('int32') In [35]: float_arr1 = arr1.astype(np.float64) In [36]: float_arr1 Out[36]: array([1., 2., 3., 4., 5.]) In [37]: float_arr1.dtype Out[37]: dtype('float64')

astype 生成一个新的数组。

数组算术(向量化计算)

数组的向量化计算,实际上就是指,在进行数组间的计算时,可以自动进行批量计算而不需要写循环。

例如:

In [4]: arr Out[4]: array([[1., 2., 3.], [4., 5., 6.]]) In [5]: arr * arr Out[5]: array([[ 1., 4., 9.], [16., 25., 36.]]) In [6]: arr - arr Out[6]: array([[0., 0., 0.], [0., 0., 0.]])

数组之间的比较,会产生一个布尔值数组:

In [11]: arr2 > arr Out[11]: array([[ True, True, True], [ True, True, True]])

数组的基础索引和切片

数组索引和 Python list 基本差不多。不过数组的切片并没有复制数组,所以相应的操作会体现在原数组上。

In [21]: arr Out[21]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [22]: arr2 = arr[4:7] In [23]: arr2 Out[23]: array([4, 5, 6]) In [24]: arr2[2] = 666 In [25]: arr2 Out[25]: array([ 4, 5, 666]) In [26]: arr Out[26]: array([ 0, 1, 2, 3, 4, 5, 666, 7, 8, 9])

这个特性和其它语言差别很大。因为 NumPy 是被设计成用于处理大数据的。如果想要数组切片的拷贝,需要用:arr[4:7].copy()

多组切片:

In [32]: arr Out[32]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In [33]: arr[:2, 1:] Out[33]: array([[2, 3], [5, 6]])

这个操作和 arr[:2][1:] 是不一样的:

In [34]: arr Out[34]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In [35]: arr[:2][1:] Out[35]: array([[4, 5, 6]])

arr[:2, 1:] 是以 arr 为对象进行切片,而 arr[:2][1:] 是对 arr 先进行 [:2],在此基础上再进行 [1:] 切片。

布尔索引

数组的比较也是可以向量化操作的,例:

In [42]: names = np.array(['A', 'B', 'C', 'D', 'E', 'A', 'G']) In [43]: data = np.random.rand(7, 4) In [44]: names Out[44]: array(['A', 'B', 'C', 'D', 'E', 'A', 'G'], dtype='<U1') In [45]: data Out[45]: array([[0.24544966, 0.95670048, 0.79969446, 0.66363606], [0.60439013, 0.80876096, 0.89888901, 0.24437441], [0.78994146, 0.04570998, 0.21567245, 0.6541382 ], [0.44615839, 0.03403341, 0.50506009, 0.67959134], [0.42704604, 0.32809779, 0.39220626, 0.10174385], [0.49334275, 0.4259239 , 0.21676384, 0.40422707], [0.29756227, 0.46491207, 0.45013549, 0.62143982]]) In [46]: names == 'A' Out[46]: array([ True, False, False, False, False, True, False]) In [47]: data[names == 'A'] Out[47]: array([[0.24544966, 0.95670048, 0.79969446, 0.66363606], [0.49334275, 0.4259239 , 0.21676384, 0.40422707]])

数组和字符串进行比较后,生成一个布尔类型的数组,用这个布尔类型的数组作为 data 的索引,可以获取 data 中布尔索引为 True 的对象。这里布尔数组的长度需要和 data 数组的轴索引长度一样。

在使用布尔索引时,也可以进行多组操作,比如 data[names == 'A', 2:]
使用“~”是对条件取反,比如 data[names != 'A'] 相当于 data[~(names == 'A')]

以及“|”“&”操作符:

In [49]: mask = (names == 'A') | (names == 'D') In [50]: data[mask] Out[50]: array([[0.24544966, 0.95670048, 0.79969446, 0.66363606], [0.44615839, 0.03403341, 0.50506009, 0.67959134], [0.49334275, 0.4259239 , 0.21676384, 0.40422707]])

神奇索引

传递一个列表或数组,包含所需要的顺序:

In [54]: arr Out[54]: array([[0., 0., 0., 0.], [1., 1., 1., 1.], [2., 2., 2., 2.], [3., 3., 3., 3.], [4., 4., 4., 4.], [5., 5., 5., 5.], [6., 6., 6., 6.], [7., 7., 7., 7.]]) In [55]: arr[[3, 1, 2]] Out[55]: array([[3., 3., 3., 3.], [1., 1., 1., 1.], [2., 2., 2., 2.]])

这样就会筛选出我们要的结果,并根据传入的顺序排序。

神奇索引和切片不一样,会复制产生新的数组。

数组转置和换轴

数组的 T 属性可以对数组进行转置:

In [57]: arr Out[57]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) In [58]: arr.T Out[58]: array([[ 0, 5, 10], [ 1, 6, 11], [ 2, 7, 12], [ 3, 8, 13], [ 4, 9, 14]])

np.dot 计算矩阵内积:

In [61]: np.dot(arr.T, arr) Out[61]: array([[125, 140, 155, 170, 185], [140, 158, 176, 194, 212], [155, 176, 197, 218, 239], [170, 194, 218, 242, 266], [185, 212, 239, 266, 293]])

transpose 置换轴:

In [65]: arr Out[65]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]]) In [66]: arr.transpose((1, 0, 2)) Out[66]: array([[[ 0, 1, 2, 3], [ 8, 9, 10, 11]], [[ 4, 5, 6, 7], [12, 13, 14, 15]]])

swapaxes 方法接收一对轴编号作为参数,并对轴进行调整用于重组数组:

In [67]: arr Out[67]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]]) In [69]: arr.swapaxes(1, 2) Out[69]: array([[[ 0, 4], [ 1, 5], [ 2, 6], [ 3, 7]], [[ 8, 12], [ 9, 13], [10, 14], [11, 15]]])

swapaxes 返回试图,没有进行数据复制。

通用函数

通用函数,是在 ndarray 数据中进行逐元素操作的函数。

举个例子:

In [3]: arr Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [4]: np.sqrt(arr) Out[4]: array([0. , 1. , 1.41421356, 1.73205081, 2. , 2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])

这种是一元通用函数。

二元通用函数接收两个数组,返回一个数组:

In [7]: x Out[7]: array([-0.37762491, -0.30829243, 0.13179841, 2.97603853, 0.53395938, -0.73487018, 0.17211808, -0.01583534]) In [8]: y Out[8]: array([ 0.55810261, 0.0360276 , -1.18459418, -0.98133524, -0.22668062, -0.92848982, -1.24477807, 1.32300964]) In [9]: np.maximum(x, y) Out[9]: array([ 0.55810261, 0.0360276 , 0.13179841, 2.97603853, 0.53395938, -0.73487018, 0.17211808, 1.32300964])

函数很多,这里不一一列了。

线性代数

NumPy 中,* 是矩阵逐元素乘积,而不是矩阵的点乘积。NumPy 中使用 dot 函数。

x.dot(y)

等价于:

np.dot(x, y)

numpy.linalg 拥有一个矩阵分解的标准函数集,以及其他常用函数,例如求逆和行列式求解。
例如:

In [5]: from numpy.linalg import inv, qr In [6]: X = np.random.randn(5, 5) In [7]: mat = X.T.dot(X) In [8]: inv(mat) Out[8]: array([[ 6.17041642, 4.85962396, -2.16021256, -0.64849763, -2.74491655], [ 4.85962396, 4.51310892, -2.06634005, -0.40201201, -2.71552471], [-2.16021256, -2.06634005, 1.1739588 , 0.22745939, 1.2661584 ], [-0.64849763, -0.40201201, 0.22745939, 0.27194759, 0.29603525], [-2.74491655, -2.71552471, 1.2661584 , 0.29603525, 2.06208411]]) In [9]: mat.dot(inv(mat)) Out[9]: array([[ 1.00000000e+00, -9.57526823e-16, 1.19754386e-16, 3.90918493e-17, -5.36585754e-16], [ 1.12865928e-16, 1.00000000e+00, -4.33587633e-16, -1.26481246e-16, -1.17804296e-16], [-1.87363062e-15, -2.95506312e-16, 1.00000000e+00, -4.98652189e-17, -8.94025147e-17], [-9.70222713e-16, -3.29142039e-16, -4.77772000e-16, 1.00000000e+00, -2.35717415e-16], [-7.35430460e-17, 1.33728672e-15, -1.00499786e-15, -1.19254377e-16, 1.00000000e+00]]) In [10]: q, r = qr(mat) In [11]: r Out[11]: array([[-3.90556439, 7.98286214, 4.30197969, -7.09432312, 3.80697826], [ 0. , -2.16268592, -3.2508382 , -0.78512893, -0.92558643], [ 0. , 0. , -2.82291938, 1.16908736, 1.91033049], [ 0. , 0. , 0. , -3.44258321, 0.63574694], [ 0. , 0. , 0. , 0. , 0.2189929 ]])

常用的 numpy.linalg 函数:

函数 描述
diag 将一个方阵的对角(或非对角)元素作为一组一维数组返回,或者将一维数组转换成一个方阵,并且在非对角线上有零点
dot 矩阵点乘
trace 计算对角线元素和
det 计算矩阵的行列式
eig 计算方阵的特征值和特征向量
inv 计算方阵的逆矩阵
pinv 计算矩阵的 Moore-Penrose 伪逆
qr 计算 QR 分解
svd 计算奇异值分解(SVD)
solve 求解 x 的线性系统 Ax = b,其中 A 是方阵
lstsq 计算 Ax = b 的最小二乘解

伪随机数生成

numpy.random 可以高效地生成多种概率分布下的完整样本数值组。

random 生成的数据可以称为伪随机数,因为它们是由具有确定性行为的算法根据随机数生成器中的随机种子生成的。可以通过 np.random.seed 更改 NumPy 的随机数种子。 numpy.random 中的数据生成函数公用了一个全局的随机数种子。

以下例子为创建一个独立的随机数生成器:

rng = np.random.RandomState(1234) rng.randn(10)