盘一盘 Python 系列 2 - NumPy (下)(三)

简介: 盘一盘 Python 系列 2 - NumPy (下)(三)

本文首发于“生信补给站”公众号 https://mp.weixin.qq.com/s/0-9zUTxZC6qefyjLyw1SSA



本节介绍四大类数组计算,具体有


  1. 元素层面 (element-wise) 计算
  2. 线性代数 (linear algebra) 计算
  3. 元素整合 (element aggregation) 计算
  4. 广播机制 (broadcasting) 计算


5.1 元素层面计算



Numpy 数组元素层面计算包括:


  1. 二元运算 (binary operation):加减乘除
  2. 数学函数:倒数、平方、指数、对数
  3. 比较运算 (comparison)


先定义两个数组 arr1 和 arr2。


arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])arr2 = np.ones((2,3)) * 2print( arr1 )print( arr2 )
[[1. 2. 3.]
[4. 5. 6.]]
[[2. 2. 2.]
[2. 2. 2.]]


加、减、乘、除


print( arr1 + arr2 + 1 )print( arr1 - arr2 )print( arr1 * arr2 )print( arr1 / arr2 )
[[4. 5. 6.]
 [7. 8. 9.]]
[[-1. 0. 1.]
 [ 2. 3. 4.]]
[[ 2. 4. 6.]
 [ 8. 10. 12.]]
[[0.5 1. 1.5]
 [2. 2.5 3. ]]


倒数、平方、指数、对数



print( 1 / arr1 )print( arr1 ** 2 )print( np.exp(arr1) )print( np.log(arr1) )
[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]


比较



arr1 > arr2arr1 > 3
array([[False, False, True],
       [ True, True, True]])
array([[False, False, False],
       [ True, True, True]])


从上面结果可知


  • 「数组和数组间的二元运算」都是在元素层面上进行的
  • 「作用在数组上的数学函数」都是作用在数组的元素层面上的。
  • 「数组和数组间的比较」都是在元素层面上进行的


但是在「数组和标量间的比较」时,python 好像先把 3 复制了和 arr1 形状一样的数组 [[3,3,3], [3,3,3]],然后再在元素层面上作比较。上述这个复制标量的操作叫做「广播机制」,是 NumPy 里最重要的一个特点,在下一节会详细讲到。



5.2 线性代数计算



在机器学习、金融工程和量化投资的编程过程中,因为运行速度的要求,通常会向量化 (vectorization) 而涉及大量的线性代数运算,尤其是矩阵之间的乘积运算。


但是,在 NumPy 默认不采用矩阵运算,而是数组 (ndarray) 运算。矩阵只是二维,而数组可以是任何维度,因此数组运算更通用些。


如果你非要二维数组 arr2d 进项矩阵运算,那么可以通过调用以下函数来实现:


  • A = np.mat(arr2d)
  • A = np.asmatrix(arr2d)


下面我们分别对「数组」和「矩阵」从创建、转置、求逆和相乘四个方面看看它们的同异。


创建

创建数组 arr2d 和矩阵 A,注意它们的输出有 array 和 matrix 的关键词。



arr2d = np.array([[1,2],[3,1]])arr2d
array([[1, 2],
       [3, 1]])




A = np.asmatrix(arr2d)A
matrix([[1, 2],
        [3, 1]])


转置

数组用 arr2d.T 操作或 arr.tranpose() 函数,而矩阵用 A.T 操作。主要原因就是 .T 只适合二维数据,上贴最后也举了个三维数组在轴 1 和轴 2 之间的转置,这时就需要用函数 arr2d.tranpose(1, 0, 2) 来实现了。


print( arr2d.T )print( arr2d.transpose() )print( A.T )
[[1 3]
 [2 1]]
[[1 3]
 [2 1]]
[[1 3]
 [2 1]]


求逆

数组用 np.linalg.inv() 函数,而矩阵用 A.I 和 A**-1 操作。



print( np.linalg.inv(arr2d) )print( A.I )print( A**-1 )
[[-0.2 0.4]
 [ 0.6 -0.2]]
[[-0.2 0.4]
 [ 0.6 -0.2]]
[[-0.2 0.4]
 [ 0.6 -0.2]]


相乘

相乘是个很模棱两可的概念


  • 数组相乘是在元素层面进行,
  • 矩阵相乘要就是数学定义的矩阵相乘 (比如第一个矩阵的列要和第二个矩阵的行一样)


看个例子,「二维数组」相乘「一维数组」,「矩阵」相乘「向量」,看看有什么有趣的结果。


首先定义「一维数组」arr 和 「向量」b:




arr = np.array([1,2])b = np.asmatrix(arr).Tprint( arr.shape, b.shape )
(2,) (2, 1)


由上面结果看出, arr 的形状是 (2,),只含一个元素的元组只说明 arr 是一维,数组是不分行数组或列数组的。而 b 的形状是 (2,1),显然是列向量。


相乘都是用 * 符号,



print( arr2d*arr )print( A*b )
[[1 4]
 [3 2]]
[[5]
 [5]]


由上面结果可知,


  • 二维数组相乘一维数组得到的还是个二维数组,解释它需要用到「广播机制」,这是下节的重点讨论内容。现在大概知道一维数组 [1 2] 第一个元素 1 乘上 [1 3] 得到 [1 3],而第二个元素 2 乘上 [2 1] 得到 [4 2]。


  • 而矩阵相乘向量的结果和我们学了很多年的线代结果很吻合。



再看一个例子,「二维数组」相乘「二维数组」,「矩阵」相乘「矩阵」



print( arr2d*arr2d )print( A*A )
[[1 4]
 [9 1]]
[[7 4]
 [6 7]]


由上面结果可知,


  • 虽然两个二维数组相乘得到二维数组,但不是根据数学上矩阵相乘的规则得来的,而且由元素层面相乘得到的。两个 [[1 2], [3,1]] 的元素相乘确实等于 [[1 4], [9,1]]。


  • 而矩阵相乘矩阵的结果和我们学了很多年的线代结果很吻合。


问题来了,那么怎么才能在数组上实现「矩阵相乘向量」和「矩阵相乘矩阵」呢?用点乘函数 dot()。



print( np.dot(arr2d,arr) )print( np.dot(arr2d,arr2d) )
[5 5]
[[7 4]
 [6 7]]


结果对了,但还有一个小小的差异


  • 矩阵相乘列向量的结果是个列向量,写成 [[5],[5]],形状是 (2,1)
  • 二维数组点乘一维数组结果是个一维数组,写成 [5, 5],形状是 (2,)


由此我们来分析下 NumPy 里的 dot() 函数,计算数组和数组之间的点乘结果。



点乘函数


本节的内容也来自〖张量 101〗,通常我们也把 n 维数组称为张量,点乘左右两边最常见的数组就是


  • 向量 (1D) 和向量 (1D)
  • 矩阵 (2D) 和向量 (1D)
  • 矩阵 (2D) 和矩阵 (2D)


分别看看三个简单例子。


例一np.dot(向量, 向量) 实际上做的就是内积,即把两个向量每个元素相乘,最后再加总。点乘结果 10 是个标量 (0D 数组),形状 = ()。



x = np.array( [1, 2, 3] )y = np.array( [3, 2, 1] )z = np.dot(x,y)print( z.shape )print( z )
()
10



例二np.dot(矩阵, 向量) 实际上做的就是普通的矩阵乘以向量。点乘结果是个向量 (1D 数组),形状 = (2, )。



x = np.array( [1, 2, 3] )y = np.array( [[3, 2, 1], [1, 1, 1]] )z = np.dot(y,x)print( z.shape )print( z )
(2,)
[10 6]



例三np.dot(矩阵, 矩阵) 实际上做的就是普通的矩阵乘以矩阵。点乘结果是个矩阵 (2D 数组),形状 = (2, 3)。






x = np.array( [[1, 2, 3], [1, 2, 3], [1, 2, 3]] )y = np.array( [[3, 2, 1], [1, 1, 1]] )z = np.dot(y,x)print( z.shape )print( z )
(2, 3)
[[ 6 12 18]
[ 3 6 9]]


从例二和例三看出,当 x 第二个维度的元素 (x.shape[1]) 和 y 第一个维度的元素 (y.shape[0]) 个数相等时,np.dot(X, Y) 才有意义,点乘得到的结果形状 = (X.shape[0], y.shape[1])。


上面例子都是低维数组 (维度 ≤ 2) 的点乘运算,接下来我们看两个稍微复杂的例子。



例四:当 x 是 3D 数组,y 是 1D 数组,np.dot(x, y) 是将 x 和 y 最后一维的元素相乘并加总。此例 x 的形状是 (2, 3, 4),y 的形状是 (4, ),因此点乘结果的形状是 (2, 3)。






x = np.ones( shape=(2, 3, 4) )y = np.array( [1, 2, 3, 4] )z = np.dot(x,y)print( z.shape )print( z )
(2, 3)
[[10. 10. 10]
[10. 10. 10]]



例五:当 x 是 3D 数组,y 是 2D 数组,np.dot(x, y) 是将 x 的最后一维和 y 的倒数第二维的元素相乘并加总。此例 x 的形状是 (2, 3, 4),y 的形状是 (4, 2),因此点乘结果的形状是 (2, 3, 2)。


x = np.random.normal( 0, 1, size=(2, 3, 4) )y = np.random.normal( 0, 1, size=(4, 2) )z = np.dot(x,y)print( z.shape )print( z )
(2, 3, 2)
[[[ 2.11753451 -0.27546168]
  [-1.23348676 0.42524653]
  [-4.349676 -0.3030879 ]]
 [[ 0.15537744, 0.44865273]
  [-3.09328194, -0.43473885]
  [ 0.27844225, -0.48024693]]]


例五的规则也适用于 nD 数组和 mD 数组 (当 m ≥ 2 时) 的点乘。


5.3 元素整合计算



在数组中,元素可以以不同方式整合 (aggregation)。拿求和 (sum) 函数来说,我们可以对数组


  • 所有的元素求和
  • 在某个轴 (axis) 上的元素求和


先定义数组



arr = np.arange(1,7).reshape((2,3))arr
array([[1, 2, 3],
       [4, 5, 6]])


不难看出它是一个矩阵,分别对全部元素、跨行 (across rows)、跨列 (across columns) 求和:


print( 'The total sum is', arr.sum() )print( 'The sum across rows is', arr.sum(axis=0) )print( 'The sum across columns is', arr.sum(axis=1) )
The total sum is 21
The sum across rows is [5 7 9]
The sum across columns is [ 6 15]


分析上述结果:


  • 1, 2, 3, 4, 5, 6 的总和是 21
  • 跨行求和 = [1 2 3] + [4 5 6] = [5 7 9]
  • 跨列求和 = [1+2+3 4+5+6] = [6 15]


行和列这些概念对矩阵 (二维矩阵) 才适用,高维矩阵还是要用轴 (axis) 来区分每个维度。让我们抛弃「行列」这些特殊概念,拥抱「轴」这个通用概念来重看数组 (一到四维) 把。



规律:n 维数组就有 n 层方括号。最外层方括号代表「轴 0」即 axis=0,依次往里方括号对应的 axis 的计数加 1。


严格来说,numpy 打印出来的数组可以想象带有多层方括号的一行数字。比如二维矩阵可想象成


   [[1, 2, 3],[4, 5, 6]]


三维矩阵可想象成


   [[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]]


由于屏幕的宽度不够,我们才把它们写成一列列的,如下


   [ [ [1, 2, 3]

        [4, 5, 6] ] 

     [ [7, 8, 9] 

        [10, 11, 12] ] ]


但在你脑海里,应该把它们想成一整行。这样会便于你理解如何按不同轴做整合运算。


有了轴的概念,我们再来看看 sum() 求和函数。

相关文章
|
15天前
|
程序员 开发工具 索引
图解Python numpy基本操作
图解Python numpy基本操作
|
6天前
|
存储 并行计算 数据挖掘
Python中的NumPy库:科学计算与数据分析的基石
Python中的NumPy库:科学计算与数据分析的基石
62 0
|
8天前
|
存储 索引 Python
python学习——NumPy数值计算基础
NumPy基础知识概览:涉及nan(非数字)和inf(无穷)的概念,nan在文件读取或不适当计算时出现,inf在除0操作中出现。数组操作有深拷贝(a=b.copy())、浅拷贝(a=b[:])和引用(a=b)。创建数组方式多样,如`np.array()`、`np.arange()`等。数据类型转换如`np.float64()`、`np.int8()`。随机数生成包含均匀分布、正态分布等。数组索引和切片支持多维操作。改变数组形状用`reshape()`,展平用`ravel()`和`flatten()`。矩阵运算包括加减乘、转置、逆矩阵等。
31 2
python学习——NumPy数值计算基础
|
12天前
|
Python
python相关库的安装:pandas,numpy,matplotlib,statsmodels
python相关库的安装:pandas,numpy,matplotlib,statsmodels
|
20天前
|
SQL 数据采集 数据挖掘
构建高效的Python数据处理流水线:使用Pandas和NumPy优化数据分析任务
在数据科学和分析领域,Python一直是最受欢迎的编程语言之一。本文将介绍如何通过使用Pandas和NumPy库构建高效的数据处理流水线,从而加速数据分析任务的执行。我们将讨论如何优化数据加载、清洗、转换和分析的过程,以及如何利用这些库中的强大功能来提高代码的性能和可维护性。
|
20天前
|
存储 测试技术 程序员
【Python】—— NumPy基础及取值操作
【Python】—— NumPy基础及取值操作
|
20天前
|
索引 Python
【Python】—— Numpy 初体验
【Python】—— Numpy 初体验
|
20天前
|
存储 机器学习/深度学习 数据处理
NumPy:从初识到实战,探索Python科学计算的无限可能
NumPy:从初识到实战,探索Python科学计算的无限可能
40 0
|
20天前
|
机器学习/深度学习 存储 算法
Python中的NumPy库:数值计算与科学计算的基石
【2月更文挑战第29天】NumPy是Python科学计算的核心库,专注于高效处理大型多维数组和矩阵。其核心是ndarray对象,提供快速数组操作和数学运算,支持线性代数、随机数生成等功能。NumPy广泛应用于数据处理、科学计算和机器学习,简化了矩阵运算、统计分析和算法实现,是数据科学和AI领域的重要工具。
|
20天前
|
存储 索引 Python
请解释Python中的NumPy库以及它的主要用途。
【2月更文挑战第27天】【2月更文挑战第97篇】请解释Python中的NumPy库以及它的主要用途。