人工智能中的大量问题无论是数据预处理、特征表示、模型输入还是结果计算本质上都离不开对数值数据的组织、变换与运算。要理解后续的数据分析、机器学习乃至深度学习就必须先理解如何把数据表示成可计算的结构以及如何高效地对这些数据进行批量处理。NumPyNumerical Python正是 Python 中承担这一基础任务的核心工具。它以 ndarray 为中心把标量、向量、矩阵以及更高维数据统一到同一种数组结构中使我们能够用更简洁、更高效的方式完成索引、切片、统计、变形与运算等操作。因此学习 NumPy实质上是在为后续学习人工智能相关内容打地基。安装 NumPypip install numpy导入 NumPyimport numpy as np本次课围绕一个具体任务展开用 NumPy 处理一张学生成绩表。像学生成绩表这样“行列规则清楚、元素都是数值”的数据本质上就是 NumPy 最适合处理的二维数组场景。通过这一任务逐步学习如何建立数组、理解结构、提取数据、完成统计、调整形状并保存结果从而在真实问题中掌握 NumPy 的核心用法。一、用学生成绩表理解 NumPy 数组假设某班有 4 名学生学习了 3 门课程语文、数学、英语。现在要把他们的成绩整理成一张表。import numpy as np scores np.array([ [85, 92, 80], # 学生1 [88, 76, 95], # 学生2 [90, 89, 84], # 学生3 [82, 87, 90] # 学生4]) print(scores)说明scores 就是一个 NumPy 数组对象类型为 ndarray。这张成绩表可以这样理解每一行表示一位学生每一列表示一门课程每个位置上的数值表示某位学生某门课的成绩。这正是 NumPy 最擅长处理的数据形式因为学生成绩表本质上是一张规则的数值型二维表。NumPy 数组的核心特点可以概括为三点其一同构性即同一个数组中的元素通常具有统一的数据类型其二多维性即它可以表示 0 维、1 维、2 维及更高维数据其三规则性即二维数组的每一行长度应一致更高维数组的结构也必须规则。查看 scores 的类型print(type(scores))输出class numpy.ndarray这说明它已经不是普通列表而是 NumPy 的数组对象。从更一般的角度看NumPy 数组并不只用来表示成绩表。一个数、一列数据、一张二维表甚至更高维的数据结构都可以用数组表示。但对于入门来说把它先理解成“适合计算的数值表格”通常最容易把握。延伸阅读《NumPy快速认识 ndarray 数组》《Python图解 NumPy》二、数组的结构维度、形状与数据类型建立成绩表之后第一步不是立刻做计算而是先看清这张表本身的结构。NumPy 中最基础的几个结构属性正好可以回答这个问题。print(维度数, scores.ndim)print(形状, scores.shape)print(元素总数, scores.size)print(数据类型, scores.dtype)输出示意维度数 2形状 (4, 3)元素总数 12数据类型 int64这几个结果分别表示不同含义。1、维度ndimndim 表示数组有多少个维度。这里成绩表是二维的所以结果是 2。可以粗略理解为0 维一个数标量 Scalar1 维一列数据向量 Vector2 维一张表矩阵 Matrix3 维及以上更复杂的数据块张量 Tensor2、形状shapeshape 的返回值是一个元组用来描述各维度的长度。这里结果是 (4, 3)说明有 4 行即 4 位学生有 3 列即 3 门课程。3、元素总数sizesize 表示数组中元素总数。这里共有 4 × 3 12 个成绩所以结果是 12。4、数据类型dtypedtype 表示数组中元素的数据类型。由于这里都是整数成绩所以通常会显示为整数类型。NumPy 数组通常要求元素类型统一这也是它能高效进行批量运算的重要原因之一。延伸阅读《NumPy数组属性》《NumPydtype 数据类型》三、数组从哪里来创建与基本查看前面的成绩表是直接手工录入的这当然是最直观的方式。但在实际使用中数组既可以由现有数据转换而来也可以按规律、按模板或按随机方式批量生成。1、把已有数据变成数组这是最基础的方式。比如使用 np.array() 可将 Python 列表转换成 NumPy 数组scores np.array([ [85, 92, 80], [88, 76, 95]])或者arr np.array([1, 2, 3], dtypefloat) # 显式指定元素类型为浮点型dtype 参数可用于显式指定数组元素类型这在后续数值计算中很常见。从语法上看np.array(5) 可创建 0 维数组np.array([1, 2, 3]) 可创建 1 维数组np.array([[1, 2], [3, 4]]) 可创建 2 维数组。理解这一点有助于后面进一步理解维度与形状。2、用规律生成一组数据例如使用 np.arange() 生成 0 到 9 的整数arr np.arange(10)# [0 1 2 3 4 5 6 7 8 9]使用 np.linspace() 可生成某个区间上均匀分布的数arr np.linspace(0, 100, 6)# [ 0. 20. 40. 60. 80. 100.]如果想生成等比序列可以使用 np.logspace()arr np.logspace(1, 3, 3) # [ 10. 100. 1000.]这类方法常用于模拟数据、构造测试数据或生成连续数值。arange() 更强调步长linspace() 强调区间内点数logspace() 则强调按对数刻度生成数值。3、创建特殊数组有时我们需要先创建一张“空白成绩表”或“模板表”再逐步填入数据。这时可以使用zeros np.zeros((4, 3))ones np.ones((4, 3))full np.full((4, 3), 60)它们分别表示np.zeros()创建全 0 数组np.ones()创建全 1 数组np.full()创建全部填充为指定值的数组。如果要创建单位矩阵可以使用 np.eye() 和 np.identity()eye_arr np.eye(3)identity_arr np.identity(3)单位矩阵指的是对角线全 1其它元素全 0 的数组[[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]4、创建未初始化数组有时只想先申请一块内存而暂时不关心其中的值可以使用 np.empty()empty_arr np.empty((2, 3))需要注意np.empty() 创建出来的数组元素值是未初始化的因此通常不可直接当作有效数据使用它更适合在后续会立即覆盖赋值的场景。5、生成随机数据如果想模拟一张随机成绩表也可以这样写np.random.seed(42)random_scores np.random.randint(60, 101, size(4, 3))说明seed(42) 用于固定随机结果保证每次运行都一致randint(60, 101, size(4, 3)) 表示生成一个 4 行 3 列、分数范围在 60 到 100 之间的整数数组。延伸阅读《NumPy 函数手册数组创建》四、如何取数据与改数据索引、切片和赋值有了成绩表之后最自然的问题就是怎样查看某位学生的成绩怎样提取某门课程的数据怎样修改录入错误的分数。这些问题都属于同一类操作从数组中取数据、改数据。1、查看某位学生的全部成绩数组索引从 0 开始。支持负索引-1 表示最后一个元素。print(第一位学生的成绩, scores[0]) print(第三位学生的成绩, scores[2])说明scores[0] 表示第 0 行即第一位学生的全部成绩。2、查看某门课程的全部成绩二维及更高维数组都要用逗号分隔不同维度。冒号 : 单独使用时表示该维度的所有元素。print(全班语文成绩, scores[:, 0])print(全班数学成绩, scores[:, 1])print(全班英语成绩, scores[:, 2])说明scores[:, 0] 表示所有学生的第 1 门课成绩scores[:, 1] 表示所有学生的第 2 门课成绩。3、查看某位学生某门课程的成绩print(第二位学生的英语成绩, scores[1, 2]) print(scores[-1, -1]) # 最后一位学生的最后一门课程成绩说明[1, 2] 表示第 2 位学生、第 3 门课程。4、提取成绩表中的一部分一维切片的基本语法为array[start:stop:step]例如提取前两位学生的全部成绩print(scores[:2])提取前两门课程的成绩print(scores[:, :2])提取左上角的一个 2×2 子表print(scores[:2, :2]) # [start:stop] 形式切片取前两行、前两列这些都属于切片操作。可以把它理解成对表格的一块区域进行截取。5、修改分数如果发现录入错误也可以直接改写数组中的元素。例如将第一位学生的英语成绩从 80 改成 82scores[0, 2] 82print(scores)也可以修改整行数据。例如更新第四位学生的全部成绩scores[3] [84, 88, 91]print(scores)延伸阅读《NumPy数组元素访问》《NumPy数组元素修改》《NumPy 函数手册数组索引与切片》《NumPy 函数手册数组元素修改操作》五、让整张表一起运算整体运算与广播NumPy 最核心的优势之一就是它支持整体运算。也就是说不必逐个元素写循环而是可以直接让整张成绩表参与计算。1、整体加分数组与标量的运算假设学校决定每门课统一加 5 分可以直接写boosted_scores scores 5print(boosted_scores)说明 5 会自动作用到成绩表中的每一个元素上。如果要求“最高不超过 100 分”可以使用 np.clip()boosted_scores np.clip(scores 5, 0, 100)print(boosted_scores)这表示把所有结果限制在 0 到 100 的范围内。2、比较目标成绩数组与数组的运算假设我们还想看看每位学生与目标分数之间的差距可以建立一张目标分数表target np.full((4, 3), 90) gap scores - targetprint(gap)这里的减法是逐元素运算。两个形状相同的数组会在对应位置上一一相减。3、不同课程加不同分广播如果不是每门课统一加 5 分而是语文加 2 分数学加 5 分英语加 3 分那么可以这样写bonus np.array([2, 5, 3]) # 形状为 (3,) adjusted_scores scores bonusprint(adjusted_scores)说明之所以能直接相加是因为 NumPy 的广播机制在起作用。可以直观地理解为这组长度为 3 的加分规则会自动应用到每一位学生身上。更准确地说这是因为 scores 的形状是 (4, 3)而 bonus 的形状是 (3,)二者在列方向上可以对齐因此能够进行广播。延伸阅读《NumPy 函数手册数值运算》《NumPy广播机制与广播规则》六、真正开始分析成绩统计函数与 axis成绩表建立好了数据也会查看和修改了接下来最重要的任务就是统计分析。例如每位学生总分是多少每位学生平均分是多少各科平均分是多少各科最高分、最低分分别是多少。这些问题都要用到 NumPy 的统计函数而其中最关键的概念是 axis。axis 可理解为“按哪一个维度进行聚合”。对一维数组而言通常只有 axis0对二维数组而言常见的是 axis0 和 axis1。1、计算每位学生的总分和平均分total_scores np.sum(scores, axis1) # axis1对每一行聚合mean_scores np.mean(scores, axis1) # axis1对每一行聚合 print(每位学生总分, total_scores)print(每位学生平均分, mean_scores)为什么这里用 axis1因为每一行表示一位学生按行统计就能得到每位学生的总体表现。2、计算各科平均分、最高分和最低分subject_means np.mean(scores, axis0)subject_max np.max(scores, axis0)subject_min np.min(scores, axis0) print(各科平均分, subject_means)print(各科最高分, subject_max)print(各科最低分, subject_min)说明使用 axis0因为每一列表示一门课程按列统计就能得到课程维度的结果。3、几种常见统计函数除了总和、平均值、最大值、最小值NumPy 还提供了几种很常见的统计函数print(全班成绩中位数, np.median(scores))print(全班成绩标准差, np.std(scores))print(全班成绩方差, np.var(scores))print(各科最低分所在行索引, np.argmin(scores, axis0))其中np.median()计算中位数np.std()计算标准差np.var()计算方差np.argmin()返回最小值位置的索引。如果想看累积求和也可以使用 np.cumsum()print(第一位学生成绩累积和, np.cumsum(scores[0]))np.cumsum() 返回累积和在某些分析任务中也很有用。延伸阅读《NumPy 函数手册聚合与统计》七、整理和扩展成绩表变形、转置、展平与拼接在实际使用中我们并不只是对现有成绩表做运算还常常需要调整表的结构。例如一串分数要重新整理成表格希望从“按学生看成绩”切换成“按课程看成绩”把表格展平成一维序列增加一位新学生增加一门新课程。这些操作虽然表面不同但本质上都属于“调整数组结构”。1、变形reshape()假设现在有 12 个分数按顺序存放在一个一维数组中flat_scores np.array([85, 92, 82, 88, 76, 95, 90, 89, 84, 84, 88, 91])table_scores flat_scores.reshape(4, 3) print(table_scores)说明reshape(4, 3) 的含义是把这 12 个数重新组织为 4 行 3 列的一张表。这说明变形并不是改变数据本身而是改变数据的组织方式。2、resize()原地修改形状除了 np.reshape()NumPy 还提供 np.resize()arr np.arange(12)arr.resize((4, 3)) print(arr)reshape() 通常返回新的视图或新结构而 resize() 会原地修改数组本身因此入门阶段更常用、也更安全的写法通常还是 reshape()。3、转置交换观察角度print(原成绩表)print(scores) print(转置后的成绩表)print(scores.T)转置以后原来每一行表示学生现在每一行变成课程。转置的意义不只是数学上的形式变化也是在切换观察数据的角度。4、展平flatten() 与 ravel()有时我们希望把二维表“拉平”为一维数组这时可以使用展平操作。flat1 scores.flatten()flat2 scores.ravel() print(flat1)print(flat2)二者的区别在于np.flatten() 返回的是拷贝np.ravel() 通常返回的是视图修改结果可能影响原数组。例如a scores.flatten()b scores.ravel() a[0] 999print(flatten 后修改不影响原数组)print(scores) b[0] 777print(ravel 后修改可能影响原数组)print(scores)拷贝表示生成一份独立数据视图表示与原数组共享底层数据因此修改时可能联动。5、增加一位新学生如果新来一位学生三门成绩分别为 91、86、88可以这样添加new_student np.array([[91, 86, 88]])new_scores np.vstack((scores, new_student)) print(new_scores)说明使用 np.vstack()因为新增学生本质上是新增一行。6、增加一门新课程如果现在又增加一门物理课4 位学生的物理成绩如下physics np.array([[83], [91], [88], [86]])scores_with_physics np.hstack((scores, physics)) print(scores_with_physics)说明使用 np.hstack()因为新增课程本质上是新增一列。除了 vstack() 和 hstack()NumPy 还提供 stack()它的特点是新增一个维度后再堆叠数组。变形、转置、展平和拼接本质上都是在调整数组的组织结构。延伸阅读《NumPy数组复制与视图》《NumPy 函数手册数组结构调整》八、综合示例完成一次完整的成绩表分析与保存最后把前面的知识放到同一个示例中完成一次较完整的成绩表分析并把结果保存下来。import numpy as np # 4 位学生3 门课程语文、数学、英语scores np.array([ [85, 92, 82], [88, 76, 95], [90, 89, 84], [84, 88, 91]]) # 查看数组结构print(形状, scores.shape)print(数据类型, scores.dtype) # 提取数据print(\n第一位学生的成绩, scores[0])print(全班数学成绩, scores[:, 1]) # 统计分析total_scores np.sum(scores, axis1)student_means np.mean(scores, axis1)subject_means np.mean(scores, axis0) print(\n每位学生总分, total_scores)print(每位学生平均分, student_means)print(各科平均分, subject_means) # 调分bonus np.array([2, 5, 3])adjusted_scores np.clip(scores bonus, 0, 100) print(\n调整后的成绩表)print(adjusted_scores) # 增加一位新学生new_student np.array([[91, 86, 88]])new_scores np.vstack((scores, new_student)) print(\n增加新学生后的成绩表)print(new_scores) # 保存为 NumPy 二进制文件np.save(scores.npy, new_scores) # 从二进制文件读回loaded_scores np.load(scores.npy)print(\n从 .npy 文件读取后的成绩表)print(loaded_scores) # 保存为文本文件np.savetxt(scores.txt, new_scores, fmt%d) # 从文本文件读回loaded_txt_scores np.loadtxt(scores.txt) # 文本读取后通常默认为浮点型数组print(\n从文本文件读取后的成绩表)print(loaded_txt_scores)文件读写说明np.save() / np.load() 读写的是 NumPy 原生二进制格式 .npy适合完整保存数组结构与数据类型np.savetxt() / np.loadtxt() 适合读写文本文件便于人工查看但读入结果默认常为浮点型如需整数可结合参数进一步控制。延伸阅读《NumPy 函数手册文件读写》 小结本次课以学生成绩表为例学习了 NumPy 数组的基本概念、创建方法、结构属性、索引与切片、整体运算、统计分析、形状调整以及文件读写等内容。通过这些基础知识可以初步建立面向数组进行数据组织与计算的认识。应用实例《NumPy 应用实例校园奶茶店销量分析简单的一维数组处理》《NumPy 应用实例用户行为数据分析归一化和标准化处理》“点赞有美意赞赏是鼓励”