一、NumPy 广播(Broadcast)
广播是 NumPy 中一种对不同形状的数组进行数值计算的强大机制,它能够在不需要显式循环的情况下对数组执行算术运算。
具体来说,当进行算术运算(如加法、减法、乘法、除法等)时,NumPy会按照一定的规则,自动地处理不同形状的数组,使它们能够进行元素级的操作。关键的规则是:
-
维度数(轴数)要相同:如果两个数组的维度数不同,可以通过在形状较小的数组的前面插入长度为 1 的维度来使它们的维度数相同。
-
各维度的长度要兼容:当两个数组在某个维度上的长度不同时,NumPy会沿着长度为1的维度进行扩展,直到两个数组在所有维度上的形状都匹配为止。
举个例子,假设有数组 a
和 b
:
-
a.shape
是 (3, 1) -
b.shape
是 (1, 4)
这两个数组在进行乘法运算时,NumPy会通过广播将它们的形状扩展为:
-
a
扩展为 (3, 4) -
b
扩展为 (3, 4)
然后对应位置的元素进行相乘,得到最终的结果数组。
import numpy as np a = np.array([[3], [3], [3]]) b = np.array([[4, 4, 4, 4]]) c = a * b print(c)
当运算中的 2 个数组的形状不同时,numpy 将自动触发广播机制。
import numpy as np a=np.array([[0,1,2],[3,4,5],[5,6,7]]) b=np.array([0,1,2]) print(a+b)
下面的图片展示了数组 b 如何通过广播来与数组 a 兼容。
广播的规则:
-
形状对齐:NumPy会自动在较小的数组形状的左侧补 1,使得两个数组的形状在每个维度上保持一致。例如,形状为 (3, 1) 的数组和形状为 (1, 4) 的数组,通过广播可以对齐为 (3, 4)。
-
输出数组形状:输出数组的形状是输入数组形状的各个维度上的最大值。例如,如果有一个形状为 (3, 1) 和一个形状为 (1, 4) 的数组,它们通过广播运算后得到的数组形状为 (3, 4)。
-
条件判断:在进行广播时,NumPy会检查对应维度上的长度是否相同或其中一个数组长度为 1。如果这些条件不满足,就会出现 "ValueError: frames are not aligned" 错误,表示无法进行有效的广播操作。
-
广播运算:一旦形状对齐符合广播规则,NumPy会对每对对应元素执行适当的算术运算。例如,对于两个形状分别为 (3, 1) 和 (1, 4) 的数组进行乘法,会将第一个数组的每一行分别与第二个数组的每一列相乘,得到一个形状为 (3, 4) 的输出数组。
简单理解:对两个数组,分别比较他们的每一个维度(若其中一个数组没有当前维度则忽略),满足:
-
数组拥有相同形状。
-
当前维度的值相等。
-
当前维度的值有一个是 1。
若条件不满足,抛出 "ValueError: frames are not aligned" 异常。
二、Numpy 数组操作
1、修改数组形状
1.1、ndarray.reshape
""" 不改变数据的条件下修改形状: numpy.reshape(arr, newshape, order='C') - `arr`:要修改形状的数组 - `newshape`:整数或者整数数组,新的形状应当兼容原有形状 - order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'k' -- 元素在内存中的出现顺序 """ import numpy as np a=np.arange(6) b=a.reshape(3,2) print(b) numpy.ndarray.flat 是一个数组元素迭代器 a=np.arange(9).reshape(3,3) for row in a: print(row) for el in a.flat: print(el)
1.2、ndarray.flat
numpy.ndarray.flat 是一个数组元素迭代器
import numpy as np a=np.arange(8).reshape(4,2) for row in a: print(row) for el in a.flat: print(el)
1.3、ndarray.flatten
""" ndarray.flatten 返回一份数组拷贝,对拷贝所做的修改不会影响原始数组: ndarray.flatten(order='C') """ import numpy as np a=np.arange(8).reshape(2,4) print(a.flatten()) print(a.flatten(order='F'))#order(可选):'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序
1.4、ndarray.ravel
numpy.ravel() 展平的数组元素(扁平化),顺序通常是"C风格",返回的是数组视图(view,有点类似 C/C++引用reference的意味),修改会影响原始数组。
""" 用法: ndarray.ravel(a, order='C') """ import numpy as np a=np.arange(8).reshape(2,4) print(a.revel()) print(a.revel(order='F'))
2、翻转数组
函数 | 描述 |
---|---|
transpose | 转置 |
ndarray.T | 转置 |
2.1、numpy.transpose、ndarray.T
ndarray.T 类似 numpy.transpose:
import numpy as np a=np.arange(6).reshape(2,3) print(np.transpose(a)) print(a.T)
3、连接数组
3.1.numpy.concatenate
""" numpy.transpose(arr, axes) - `a1, a2, ...`:相同类型的数组 - `axis`:沿着它连接数组的轴,默认为 0 """ import numpy as np a=np.array([[1,2],[3,4]]) b=np.array([[5,6],[7,8]]) print(np.concatenate((a,b))) print(np.concatenate((a,b),axis=1))
4、分割数组
函数 | 数组及操作 |
---|---|
split | 将一个数组分割为多个子数组 |
hsplit | 将一个数组水平分割为多个子数组(按列) |
vsplit | 将一个数组垂直分割为多个子数组(按行) |
4.1、numpy.split、numpy.vsplit、numpy.hsplit
""" 沿特定的轴将数组分割为子数组: numpy.split(ary, indices_or_sections, axis) - `ary`:被分割的数组 - `indices_or_sections`:如果是一个整数,就用该数平均切分,如果是一个数组,为沿轴切分的位置(左开右闭) - `axis`:设置沿着哪个方向进行切分,默认为 0,横向切分,即水平方向。为 1 时,纵向切分,即竖直方向。 """ import numpy as np a=np.arange(9) b=np.split(a,3) print(b) b=np.split(a,[4,7]) print(b) axis 为 0 时在水平方向分割,axis 为 1 时在垂直方向分割 a=np.arange(16).reshape(4,4) b=np.split(a,1) print(b) c=np.split(a,2,1) print('沿水平方向分割:\n',c) d=np.hsplit(a,2) print('沿水平方向分割:\n',d) e = np.split(a, 2, 0) print('沿垂直方向分割:\n', e) f = np.vsplit(a, 2) print('沿垂直方向分割:\n', f) f=np.vsplit(a,2) print('沿垂直方向分割:\n',f)
三、NumPy 数学函数
NumPy 包含大量的各种数学运算的函数,包括三角函数,算术运算的函数,复数处理函数等。
1、三角函数
""" 三角函数:sin()、cos()、tan() """ import numpy as np a=np.array([0,30,45,60,90]) du=np.pi/180 print(np.sin(a*du)) print(np.cos(a*du)) print(np.tan(a*du)) """ arcsin,arccos,和 arctan 函数返回给定角度的 sin,cos 和 tan 的反三角函数。 可以通过 numpy.degrees() 函数将弧度转换为角度。 """ import numpy as np a=np.array([0,30,45,60,90]) sin = np.sin(a*np.pi/180) print(sin) inv=np.arcsin(sin) print(inv) print(np.degrees(inv))#arccos,arctan同理
1、舍入函数
""" numpy.around() 函数返回指定数字的四舍五入值: numpy.around(a,decimals) - a: 数组 - decimals: 舍入的小数位数。 默认值为0。 如果为负,整数将四舍五入到小数点左侧的位置 """ import numpy as np a = np.array([1.0,6.55, 12, 0.563, 11.52]) print (np.around(a)) print (np.around(a, 1)) print (np.around(a, decimals=-1)) #numpy.floor() 返回小于或者等于指定表达式的最大整数,即向下取整 a = np.array([-1.2, 2.5, -0.5, 0.8, 9]) print (np.floor(a)) # numpy.ceil() 返回大于或者等于指定表达式的最小整数,即向上取整 a = np.array([-1.2, 2.5, -0.5, 0.8, 9]) print (np.ceil(a))
四、NumPy 算术函数
NumPy 算术函数包含简单的加减乘除: add(),subtract(),multiply() 和 divide()。
需要注意的是数组必须具有相同的形状或符合数组广播规则。
import numpy as np a = np.arange(4, dtype = np.float_).reshape(2,2) b = np.array([5,5]) c=a+b c=a-b c=a*b c=a/b c=a//b # print (np.add(a,b)) print (np.subtract(a,b)) print (np.multiply(a,b)) print (np.divide(a,b))
1、numpy.reciprocal()
numpy.reciprocal() 函数返回参数逐元素的倒数。如 1/2倒数为 2/1。
import numpy as np a=np.array([0.5,1.66,2,10]) print(np.reciprocal(a))
2、numpy.power()
numpy.power() 函数将第一个输入数组中的元素作为底数,计算它与第二个输入数组中相应元素的幂。
实例
import numpy as np a=np.array([4,5,6]) print (np.power(a,2)) b=np.array([1,2,3]) print (np.power(a,b))
3、numpy.mod()
numpy.mod() 计算输入数组中相应元素的相除后的余数。
import numpy as np a = np.array([10,20,30]) b = np.array([5,7,9]) print (np.mod(a,b))
五、NumPy 副本和视图
-
视图(浅拷贝)
视图是对原始数据的一个别称或引用,通过视图可以访问和操作原始数据,但并不会产生数据的拷贝。当你对视图进行修改时,会影响到原始数据。视图一般发生在以下情况:
-
NumPy 切片操作:NumPy 的切片操作返回原数据的视图。例如,如果
arr
是一个 NumPy 数组,arr_view = arr[::2]
,则arr_view
是arr
的一个视图,修改arr_view
会影响到arr
。 -
调用
ndarray
的view()
函数:这个函数可以创建一个数组的视图,视图与原数组共享数据存储,但形状可能不同。修改视图会影响原数组。
副本(深拷贝)
副本是原始数据的完整拷贝,即使对副本进行修改也不会影响到原始数据,它们在物理内存中不同。副本一般发生在以下情况:
-
Python 序列的切片操作:当你对 Python 的列表或其他序列进行切片操作时,会生成一个新的序列对象,这个操作是深拷贝的。例如,
list_copy = list_original[:]
,list_copy
是list_original
的一个副本。 -
调用
copy()
函数:在 NumPy 中,ndarray
对象有一个copy()
方法,调用这个方法会生成一个数组的深拷贝。例如,arr_copy = arr.copy()
,则arr_copy
是arr
的一个完整副本。
补充说明
-
在 NumPy 中,虽然切片操作返回视图,但是有时候也可能返回副本,这取决于切片的方式和是否对结果进行修改。
-
NumPy 的
view()
方法与 Python 中的deepcopy()
函数并不直接相关,deepcopy()
主要用于 Python 中的一般对象,它会递归地拷贝对象及其包含的所有对象。
-
1、无复制
简单的赋值不会创建数组对象的副本。 相反,它使用原始数组的相同id()来访问它。 id()返回 Python 对象的通用标识符,类似于 C 中的指针。
此外,一个数组的任何变化都反映在另一个数组上。 例如,一个数组的形状改变也会改变另一个数组的形状。
import numpy as np a=np.arange(4) print(a) print(id(a)) b=a print(b) print(id(b)) b.shape =(2,2) print (b) print (a)
2、视图或浅拷贝
使用ndarray.view() 实现数组的浅拷贝
import numpy as np arr=np.arange(16) arr1=arr.view() print(arr,arr1,id(arr),id(arr1)) arr1[0]=25 print(arr,arr1,id(arr),id(arr1))
切片也是一种浅拷贝
import numpy as np arr=np.arange(9) a=arr[3:] b=arr[3:] b[2]=12 print(arr) print(id(a),id(b),id(arr[3:]))
3、副本或深拷贝
ndarray.copy() 函数创建一个副本。 对副本数据进行修改,不会影响到原始数据,它们物理内存不在同一位置。
import numpy as np a=np.array([[2,3],[4,5],[6,8]]) print(a) b=a.copy() print(b) print(id(a),id(b)) b[0,0]=10 print(b) print(a)
六、NumPy 矩阵库(Matrix)
NumPy 中包含了一个矩阵库 numpy.matlib,该模块中的函数返回的是一个矩阵,而不是 ndarray 对象。
矩阵里的元素可以是数字、符号或数学式。以下是一个由 6 个数字元素构成的 2 行 3 列的矩阵:
1、转置矩阵
NumPy 中除了可以使用 numpy.transpose 函数来对换数组的维度,还可以使用 T 属性。。
例如有个 m 行 n 列的矩阵,使用 t() 函数就能转换为 n 行 m 列的矩阵。
实例
import numpy as np a = np.arange(12).reshape(3,4) print ('原数组:') print (a) print ('转置数组:') print (a.T)
2、numpy.eye()
""" numpy.eye(n, M,k, dtype) - n: 返回矩阵的行数 - M: 返回矩阵的列数,默认为 n - k: 对角线的索引 - dtype: 数据类型 """ import numpy as np print (np.eye( 2, 3, 0, dtype = float))
3、numpy.identity()
numpy.identity() 函数返回给定大小的单位矩阵。
单位矩阵是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为 1,除此以外全都为 0。
import numpy as np # 大小为 2,类型位浮点型 print (np.identity(2, dtype = float))
4、numpy.random
numpy.random
是 NumPy 库中用于生成随机数的子模块,提供了各种生成随机数的函数。以下是一些常见的用法及其示例:
4.1.生成服从均匀分布的随机数
-
numpy.random.rand
: 生成指定形状的服从均匀分布 [0, 1) 的随机数。import numpy as np rand_nums = np.random.rand(3, 2) # 生成一个 3x2 的数组 print(rand_nums)
4.2.生成服从标准正态分布的随机数
-
numpy.random.randn
: 生成指定形状的服从标准正态分布 (均值为 0,标准差为 1) 的随机数。import numpy as np rand_nums = np.random.randn(2, 4) # 生成一个 2x4 的数组 print(rand_nums)
4.3.生成整数随机数
-
numpy.random.randint
: 生成指定范围内整数随机数。import numpy as np rand_int = np.random.randint(1, 10, size=(3, 3)) # 生成一个 3x3 的数组,范围在 [1, 10) 内 print(rand_int)
4.4.生成随机排列
-
numpy.random.permutation
: 返回一个序列的随机排列。import numpy as np arr = np.array([1, 2, 3, 4, 5]) rand_perm = np.random.permutation(arr) # 返回 arr 的随机排列 print(rand_perm)
5.随机种子的设置
numpy.random.seed
: 设置随机数生成器的种子,以确保随机数可复现性。
import numpy as np np.random.seed(0) # 设置随机数种子为 0 rand_nums = np.random.rand(2, 2) print(rand_nums)
七、NumPy 线性代数
函数 | 描述 |
---|---|
dot | 两个数组的点积。 |
vdot | 两个向量的点积 |
inner | 两个数组的内积 |
matmul | 两个数组的矩阵积 |
determinant | 数组的行列式 |
solve | 求解线性矩阵方程 |
inv | 计算矩阵的乘法逆矩阵 |
1、numpy.dot()
numpy.dot()
函数用于计算两个数组的点积(内积),对于一维数组,它计算的是它们的标准内积;对于多维数组,它返回的是它们的矩阵乘积。
numpy.dot(a, b, out=None)
参数说明:
-
a, b: 输入的数组,可以是一维或多维数组。对于一维数组,计算它们的内积;对于二维数组,计算它们的矩阵乘积。
-
out: 可选参数,用于指定结果的存储位置。
#一维数组的内积 import numpy as np # 定义两个一维数组 a = np.array([1, 2, 3]) b = np.array([4, 5, 6]) # 计算内积 dot_product = np.dot(a, b) print("数组 a:", a) print("数组 b:", b) print("内积结果:", dot_product) #二维数组的矩阵乘积 # 定义两个二维数组 c = np.array([[1, 2], [3, 4]]) d = np.array([[5, 6], [7, 8]]) # 计算矩阵乘积 matrix_product = np.dot(c, d) print("矩阵 c:") print(c) print("矩阵 d:") print(d) print("矩阵乘积的结果:") print(matrix_product)
2、numpy.vdot()
#向量的点积 import numpy as np # 定义两个向量 A = np.array([1+2j, 3+4j]) B = np.array([5+6j, 7+8j]) # 计算向量的点积 vdot_product = np.vdot(A, B) print("向量 A:", A) print("向量 B:", B) print("向量的点积:", vdot_product) #多维数组的扩展点积 # 定义两个二维数组 C = np.array([[1, 2], [3, 4]]) D = np.array([[5, 6], [7, 8]]) # 计算数组的扩展点积 vdot_product_2d = np.vdot(C, D) print("矩阵 C:") print(C) print("矩阵 D:") print(D) print("扩展点积的结果:", vdot_product_2d)
3、numpy.inner()
numpy.inner()
函数用于计算两个向量的内积,对于更高维度的数组,它的行为有一些特定规则:
-
一维数组的向量内积:
-
当
numpy.inner()
函数应用于两个一维数组时,它计算的是这两个向量的内积,也称为点积。
-
-
高维数组的乘积规则:
-
对于更高维度的数组(多维数组),
numpy.inner()
函数会在最后一个轴上进行乘积求和。具体来说,它会对数组的最后一个维度上的对应元素进行乘积,然后对这些乘积结果求和,得到一个降低了一个维度的数组。
-
用一个简单的例子来说明:
import numpy as np # 定义两个一维数组(向量) A = np.array([1, 2, 3]) B = np.array([4, 5, 6]) # 计算向量的内积(点积) inner_product = np.inner(A, B) print("向量 A:", A) print("向量 B:", B) print("向量的内积(点积):", inner_product)
4、numpy.matmul
numpy.matmul()
函数的确用于计算两个数组的矩阵乘积。让我来澄清一下:
-
二维数组的矩阵乘法:
-
当
numpy.matmul()
函数应用于两个二维数组(矩阵)时,它执行的是标准的矩阵乘法运算。这意味着它会按照矩阵乘法的规则,将第一个矩阵的行与第二个矩阵的列进行乘积运算,得到一个新的二维数组作为结果。
-
-
多维数组的广义乘法:
-
numpy.matmul()
还支持多维数组的乘法操作。对于高维数组,它会按照广义矩阵乘法的规则进行操作。具体来说,它会在最后两个维度上进行矩阵乘法运算,而其他维度保持不变。
-
举个例子来说明二维数组的矩阵乘法:
import numpy as np # 定义两个二维数组(矩阵) A = np.array([[1, 2], [3, 4]]) B = np.array([[5, 6], [7, 8]]) # 使用 matmul 计算矩阵乘积 C = np.matmul(A, B) print("矩阵 A:") print(A) print("\n矩阵 B:") print(B) print("\n矩阵乘积 C = A @ B:") print(C)
5、numpy.linalg.det()
numpy.linalg.det()
函数用于计算输入矩阵的行列式。在线性代数中,行列式是一个标量值,对于一个 ( n \times n ) 的方阵 ( A ),其行列式通常记作 ( \det(A) )。
行列式的计算涉及到矩阵的特征值和特征向量,其值表示了矩阵在线性变换下的缩放因子,可以用来判断矩阵是否可逆、解方程组的唯一性等。
以下是使用 numpy.linalg.det()
函数计算行列式的一个简单示例:
import numpy as np # 定义一个2x2的矩阵 A = np.array([[1, 2], [3, 4]]) # 计算矩阵A的行列式 det_A = np.linalg.det(A) print("矩阵 A:") print(A) print("\n矩阵 A 的行列式 det(A):") print(det_A)
6、numpy.linalg.solve()
numpy.linalg.inv()
函数确实是用来计算矩阵的逆矩阵(inverse matrix)。在线性代数中,逆矩阵是与原矩阵相乘后得到单位矩阵的矩阵。具体来说:
-
对于一个可逆的方阵 ( A ),存在一个矩阵 ( B ),使得 ( AB = BA = I ),其中 ( I ) 是单位矩阵。
-
如果矩阵 ( A ) 可逆,那么 ( B ) 就是 ( A ) 的逆矩阵,记作 ( A^{-1} )。
在 NumPy 中,使用 numpy.linalg.inv()
函数可以计算给定方阵的逆矩阵。这个函数接受一个方阵作为输入,并返回其逆矩阵(如果存在的话)。以下是一个简单的示例:
import numpy as np # 创建一个2x2的方阵 A = np.array([[1, 2], [3, 4]]) # 计算A的逆矩阵 A_inv = np.linalg.inv(A) print("原矩阵 A:") print(A) print("\nA的逆矩阵 A_inv:") print(A_inv) # 验证逆矩阵:A与A_inv相乘应该得到单位矩阵 identity = np.dot(A, A_inv) print("\nA * A_inv =") print(identity)
7、numpy.linalg.inv()
numpy.linalg.inv()
函数确实是用来计算矩阵的逆矩阵(inverse matrix)。在线性代数中,逆矩阵是与原矩阵相乘后得到单位矩阵的矩阵。具体来说:
-
对于一个可逆的方阵 ( A ),存在一个矩阵 ( B ),使得 ( AB = BA = I ),其中 ( I ) 是单位矩阵。
-
如果矩阵 ( A ) 可逆,那么 ( B ) 就是 ( A ) 的逆矩阵,记作 ( A^{-1} )。
在 NumPy 中,使用 numpy.linalg.inv()
函数可以计算给定方阵的逆矩阵。这个函数接受一个方阵作为输入,并返回其逆矩阵(如果存在的话)。以下是一个简单的示例:
import numpy as np # 创建一个2x2的方阵 A = np.array([[1, 2], [3, 4]]) # 计算A的逆矩阵 A_inv = np.linalg.inv(A) print("原矩阵 A:") print(A) print("\nA的逆矩阵 A_inv:") print(A_inv) # 验证逆矩阵:A与A_inv相乘应该得到单位矩阵 identity = np.dot(A, A_inv) print("\nA * A_inv =") print(identity)