自学Python

Chapter 6

第6章Python科学计算基础:NumPy模块

章节主张

第6章Python科学计算基础:NumPy模块这一章真正要建立的,不是孤立知识点,而是围绕“NumPy 数组是科学计算的底层对象、环境与工具链先于语法细节、基础语法是后续一切抽象的地基”形成一套能反复练习、能迁移到真实任务里的 Python 学习路径。

中文释读

读 第6章Python科学计算基础:NumPy模块 时,不要只把它当作语法或模块说明书。更有价值的读法,是问自己:这一章解决了我在编程、科学计算或数据分析流程中的哪个瓶颈?

这本书的强项在于把工具、基础语法、科学计算库和案例工作流连成一条线。章节页因此会把原书内容翻译成“知识点 -> 操作动作 -> 易错边界 -> 迁移练习”的学习结构。

本章知识路线

关键论据

  • NumPy是Python的一个科学计算基础模块,一些高级的第三方科学计算模块如SciPy、Matplotlib、Pandas等,都是基于NumPy构建的。
  • NumPy模块具有以下特性: ●强大的多维数组类型和实用的函数。
  • ●C、C++、Fortran语言为底层的实现。
  • Anaconda环境中已经集成了NumPy模块,不需要再次安装。
  • NumPy模块可以在命令行中用pip更新: NumPy模块的源代码放在GitHub网站上,地址为 通常使用以下方式导入NumPy模块: 在下文中,为了方便,本书以np作为NumPy模块的缩写。

行动清单

  • 先用最小代码样例跑通本章核心知识点。
  • 把本章知识点接到一个真实小任务上,而不只停留在示例。
  • 记录本章最容易报错的边界条件和调试方式。
  • 复盘:这一章内容如何进入自己的长期 Python 学习系统。

本章图版学习路径

图版按知识点分组,适合拿来做复现、迁移和复盘练习,不只是“看一眼效果”。

源章节深读

6.1 NumPy模块简介

这节解决什么问题:这节要解决的不是背下“6.1 NumPy模块简介”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.1 NumPy模块简介”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.1 NumPy模块简介 背后的对象模型、输入输出和适用边界。

NumPy是Python的一个科学计算基础模块,一些高级的第三方科学计算模块如SciPy、Matplotlib、Pandas等,都是基于NumPy构建的。NumPy模块具有以下特性: ●强大的多维数组类型和实用的函数。 ●C、C++、Fortran语言为底层的实现。 ●线性代数、傅里叶变换和随机数支持。 ●高效的数据存储容器。 Anaconda环境中已经集成了NumPy模块,不需要再次安装。NumPy模块可以在命令行中用pip更新: NumPy模块的源代码放在GitHub网站上,地址为 通常使用以下方式导入NumPy模块: 在下文中,为了方便,本书以np作为NumPy模块的缩写。本书使用的NumPy版本为1.22.3,如下所示:

6.2 数组基础

这节解决什么问题:这节要解决的不是背下“6.2 数组基础”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.2 数组基础”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.2 数组基础 背后的对象模型、输入输出和适用边界。

数组 (Array)是NumPy中的核心数据类型。整个NumPy模块都是围绕数组来构建的。 导入NumPy模块: 数组的全称是 N维数组 (N-dimensional Array,ndarray),它是一个大小和形状固定的多维容器。多维数组对象可以使用np.array()函数构造,函数的参数可以是列表、元组,也可以是另一个数组。例如,用列表作为参数构造一维数组: 用元组作为参数构造一维数组: 用一个一维数组a作为参数,返回一个相同内容的数组: 构造一个大小为2×3的二维数组: 为了方便,在之后的内容中,本书用数组作为N维数组的简称。与列表相比,数组有一些列表没有的功能。例如,列表不支持直接用加法将每个元素都加1的操作: 而数组可以方便地实现这样的功能: 与列表加法直接聚合两个列表不同,两个数组相加是将对应位置的元素相加: 数组的数乘是将对应元素乘以给定的数: 两个数组之间还支持乘法和除法操作,返回对应元素数字的乘积或商: 数组有一些基本的属性,这些属性包含了数组的相关信息。导入NumPy模块: .shape属性可以获得数组的形状: 对于N维数组,属性.shape返回的是一个大小为N的元组,每个元素对应每个维度的大小。这里由于数组是1维,所以属性.shape返回的是一个大小为1的元组。除了属性,也可以使用np.shape()函数来查看数组的形状: np.shape()函数返回的是该对象转换为数组之后的形状,因此,该函数不仅可以作用于数组,还可以作用于其他数据类型: 多维列表的形状: 单个元素是没有形状的,其返回的结果为空元组: 与列表不同,数组要求所有的元素是同一类型,属性.dtype可以查看数组的数据类型:

6.3 数组操作

这节解决什么问题:这节要解决的不是背下“6.3 数组操作”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.3 数组操作”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.3 数组操作 背后的对象模型、输入输出和适用边界。

数组支持很多操作,本节将按照不同的类型对数组的操作进行介绍。 数组支持很多数值相关的方法。导入NumPy模块: 1.求和:.sum()方法 .sum()方法可以对数组进行求和操作,默认对所有元素进行求和: 多维数组可以通过axis参数指定求和的维度,比如维度0: 指定维度1求和: 本书约定维度i-1表示数组的第i维。对于形状为(a 0 ,a 1 ,...,a n-1 )的n维数组,对维度i求和会得到一个形状为(a 0 ,a 1 ,...,a i-1 ,a i+1 ,...,a n-1 )的n-1维数组,即沿着维度i求和得到的新数组的形状是原数组去除维度i之后的结果。因此,在上面的例子中,形状为(2,3)的二维数组a沿着维度0求和后得到一个形状为(3,)的一维数组,沿着维度1求和得到一个形状为(2,)的一维数组。axis指定的维度可以为负数,规则与索引相同。 与.sum()方法对应,NumPy提供了一个np.sum()函数,可以用它来进行相同的操作: np.sum()函数支持对非数组类型的对象进行求和,返回一个数组。 2.求积:.prod()方法 .prod()方法的使用与.sum()方法相同,只不过是将求和变成了求积。例如,不指定维度的时候是全局求积: 该方法也有对应的函数形式np.prod(): 3.最值:.max()和.min()方法 .max()和.min()方法的使用也和.sum()方法相同,只是将求和的操作变成了求最大值或最小值。例如,数组的最大值和最小值: 沿着某个维度求最大值和最小值: 对应的函数形式分别为np.max()和np.min()。 4.最值位置:.argmax()和.argmin()方法 .argmax()和.argmin()方法可以分别返回最大值和最小值的位置,例如,

6.4 数组广播机制

这节解决什么问题:这节要解决的不是背下“6.4 数组广播机制”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.4 数组广播机制”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.4 数组广播机制 背后的对象模型、输入输出和适用边界。

数组支持广播机制,支持对一些形状不同但满足一定条件的多个数组进行一些二元操作。 导入NumPy模块: 数组支持一些常见的二元操作,如四则运算和逻辑比较操作等。在之前的例子中,两个数组通常都是大小相同的: 也有两个不同维度操作的例子,如数组的数乘: 事实上,一维数组a与2数乘相当于一维数组a与将2扩展成另一个与a大小相同的数组相乘。 来看另一个更复杂的例子: 将b修改一个形状为(3,)的一维数组,再与a相加,能得到相同的结果: NumPy检查到b的维度与a的维度匹配后,将一维数组b扩展为之前的二维形式,得到相同的结果。如果再将a变成一个形状为(1,4)的列向量,a加b依然成立: 当两个数组进行二元操作时,NumPy会对它们的形状进行检查,如果两个数组形状匹配,NumPy会按照一定的规则将它们变成两个形状相同的数组,再进行相应的二元操作。这种匹配数组形状的模式叫作 广播机制 (Broadcasting)。在广播机制中,两个数组的匹配规则如下。 ●规则1:两个数组的形状完全一致。 ●规则2:两个数组的维度一样,对应的维度大小相同,或者其中一个大小为1。 ●规则3:两个数组的维度个数不同时,在低维数组前增加大小为1的维度直到与高维数组维度相等,然后应用前两个规则判断。 匹配成功后,结果数组每个维度的大小取两个数组对应维度大小较大的一个。利用广播机制,不难解释上面例子中的运算过程。对于第一个加法,两个数组的形状分别为: 两个数组形状相同,根据规则1,匹配成功,结果形状为4×3。 对于第二个加法,两个数组的形状分别为: 两者维度不一样,根据规则3,先在低维数组b前增加大小为1的维度,再根据规则2,匹配成功,结果形状为4×3。 对于第三个加法,两个数组的形状分别为: 两者维度不一样,根据规则3,先在低维数组b

6.5 数组索引进阶

这节解决什么问题:这节要解决的不是背下“6.5 数组索引进阶”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.5 数组索引进阶”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.5 数组索引进阶 背后的对象模型、输入输出和适用边界。

Python中的索引机制可以表示为x[obj]。当对象obj是一个元组时,元组的括号可以省略,因此x[(exp1,exp2,...,expN)]的索引写法与x[exp1,exp2,...,expN]是等价的。在NumPy中,根据对象obj的不同,数组索引可以分成基础索引和高级索引两大类。 NumPy将Python的基础索引(如列表、字符串等)扩展到了N维数组。数组的基础索引需要满足: ●索引对象是整数。 ●索引对象是slice对象。 ●索引对象是一个由整数、slice对象构成的元组。 除此之外,基础索引还可以使用np.newaxis和Python内置的省略对象Ellipsis。导入NumPy模块: 1.整数元组索引单个元素 在基础索引中,最简单的情况是使用一个N维整数元组索引N维数组的单个元素。这N个整数分别代表数组N个维度的索引值。 一方面,对于N维数组的维度i(i=0,1,…,N-1),若其大小为d,根据Python从0开始的索引规则,该维度合法的索引值应当处于0~d;另一方面,为了支持负索引,当索引值n为负数时,NumPy会将其转化为索引n+d。 2.slice元组索引子数组 类似于列表的切片操作,NumPy支持用包含N个slice对象的元组索引得到子数组,这N个slice对象分别作用在各自的维度: 如果索引N维数组时所用的元组维度小于N,那么NumPy会自动将后面缺失的维度补全为“:”。例如,a[1:3]相当于a[1:3,:]: 3.整数与slice对象的混用 在索引时,还可以使用数字与slice对象的组合。此时,整数i的作用相当于slice对象i:i+1,但二者得到的维度有所差别,使用数字索引得到的数组维度比使用slice对象小1: 索引单个元素可以看成是一种特殊的混用。对于N维数组,

6.6 数组读写

这节解决什么问题:这节要解决的不是背下“6.6 数组读写”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.6 数组读写”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.6 数组读写 背后的对象模型、输入输出和适用边界。

数组可以很方便地支持读写操作。 导入NumPy模块: 可以用np.loadtxt()函数从文本文件中读取数据。假设有这样的一个文件myfile.txt,内容为: 用np.loadtxt()函数读取: 如果文件中的数据不是空格分割,而是逗号分割: 可以加上参数delimiter指定分隔符: 完整的用法为: 还有一个功能更强大的np.genfromtxt()函数,能处理更多的情况,但其速度慢、效率低。 导入NumPy模块: np.savetxt()可以将单个数组写入文件,默认使用科学计数法的形式保存数字: 可以使用fmt参数修改写入的格式: 还可以用delimiter参数指定分隔符: 直接读写文本方式不如使用二进制读写速度快、效率高。可以将数组存储成二进制格式,并进行读取。导入NumPy模块: 用于保存数组的函数如下。 ●np.save(file_name,arr):保存单个数组,.npy格式。 ●np.savez(file_name,*args,**kwds):保存多个数组,无压缩的.npz格式。 用于读取数组的函数为np.load(file_name),对于.npy文件,返回保存的数组;对于.npz文件,返回一个由名称-数组对组成的字典。 考虑两个数组:

6.7 随机数组

这节解决什么问题:这节要解决的不是背下“6.7 随机数组”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.7 随机数组”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.7 随机数组 背后的对象模型、输入输出和适用边界。

NumPy中的随机数组是通过子模块numpy.random实现的,这里只介绍一些简单的用法。导入NumPy模块: np.random.rand()函数可以用来生成0~1区间指定大小的随机数组: np.random.randn()函数生成的则是服从标准正态分布的随机数组。 与标准模块random类似,numpy.random也有choice()函数,不过功能更强大: 该函数从一维数组或列表a中,随机选取出size大小形状的元素组成数组。其中,replace参数表示选择的元素是否可重复,p是一个与a大小相同的数组,表示a中各个元素被选中的概率,默认为等概率: np.random.shuffle()函数支持对数组的乱序操作: 对于多维数组来说,乱序只在它的维度0进行:

6.8 实例:使用NumPy实现K近邻查找

这节解决什么问题:这节要解决的不是背下“6.8 实例:使用NumPy实现K近邻查找”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.8 实例:使用NumPy实现K近邻查找”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.8 实例:使用NumPy实现K近邻查找 背后的对象模型、输入输出和适用边界。

K近邻是一种基础的数据分析算法,利用NumPy的基础功能,可以快速实现K近邻的算法。K近邻算法的定义如下,已知有一组M个N维向量数据,对于给定的一个N维向量,从这组数据里找到与该向量距离最近的K个结果。在数学中,衡量两个向量的距离可以用二范数(即欧氏距离),两个向量的欧氏距离可以这样计算: M个N维向量可以组成一个大小为(M,N)的二维数组X,单一的N维向量是一个大小为(N,)的一维数组y: 它们的欧氏距离可以利用数组广播机制计算: 事实上,由于开方函数的单调性,距离最近的K个结果可以直接利用np.sum()的结果得到。为了得到距离最近的K个结果,需要对distance进行排序,得到前K个索引。该操作可以通过np.argsort()实现: 如果不考虑这K个索引的顺序,而只需要前K个结果,可以使用np.argpartition()函数更快地获得结果: 因此,可以将K近邻计算定义为如下函数: 该函数接受4个参数,返回最近邻的K个索引位置。如果strict参数被设为True,使用np.argsort()返回有序的前K个,否则返回不保序的前K个。 这个实例仅用于演示如何使用NumPy进行高效计算,实际使用时,K近邻的算法在很多第三方模块中已经有更好的实现,可以直接调用。

6.9 本章学习笔记

这节解决什么问题:这节要解决的不是背下“6.9 本章学习笔记”,而是把这个知识点放进真正可运行、可调试、可迁移的 Python 学习流程里。

怎么用到自己的系统:把它写进自己的学习系统:先用最小例子跑通,再把“6.9 本章学习笔记”迁移到一个实际小任务中,最后记录哪些边界条件最容易报错。

常见错误:常见误区是只记住 API 或代码片段,却没理解 6.9 本章学习笔记 背后的对象模型、输入输出和适用边界。

本章对NumPy模块进行了简单的介绍。NumPy模块的核心是数组,掌握数组的用法和细节是十分必要的。 学完本章,读者应该做到: ●掌握NumPy数组类型的基本属性和类型。 ●掌握NumPy数组的生成和索引。 ●掌握NumPy数组的迭代。 ●掌握NumPy数组的读写。 ●掌握NumPy数组的广播机制。 ●了解NumPy数组的索引机制。 ●掌握NumPy随机数组的生成和使用。 1.本章新术语 本章涉及的新术语见表6-2。 表6-2 本章涉及的新术语 2.本章新函数 本章涉及的新函数见表6-3。 表6-3 本章涉及的新函数 (续) 3.本章Python 2与Python 3的区别 本章涉及的Python 2与Python 3的区别见表6-4。

辨析题:如果你只会照抄 第6章Python科学计算基础:NumPy模块 里的例子,却无法换一个输入自己写出来,真正缺的是什么?

多数时候缺的不是记忆,而是对象模型、输入输出边界和操作顺序的理解。把知识点换到新输入上依然能运行,才算真的进入了自己的系统。