人工智能中的大量问题,无论是数据预处理、特征表示、模型输入,还是结果计算,本质上都离不开对数值数据的组织、变换与运算。要理解后续的数据分析、机器学习乃至深度学习,就必须先理解:如何把数据表示成可计算的结构,以及如何高效地对这些数据进行批量处理。

NumPy(Numerical Python)正是 Python 中承担这一基础任务的核心工具。它以 ndarray 为中心,把标量、向量、矩阵以及更高维数据统一到同一种数组结构中,使我们能够用更简洁、更高效的方式完成索引、切片、统计、变形与运算等操作。因此,学习 NumPy,实质上是在为后续学习人工智能相关内容打地基。

安装 NumPy:

pip install numpy

导入 NumPy:

import numpy as np

本次课围绕一个具体任务展开:用 NumPy 处理一张学生成绩表。像学生成绩表这样“行列规则清楚、元素都是数值”的数据,本质上就是 NumPy 最适合处理的二维数组场景。

通过这一任务,逐步学习如何建立数组、理解结构、提取数据、完成统计、调整形状并保存结果,从而在真实问题中掌握 NumPy 的核心用法。

一、用学生成绩表理解 NumPy 数组

假设某班有 4 名学生,学习了 3 门课程:语文、数学、英语。现在要把他们的成绩整理成一张表。

print(scores)

说明:scores 就是一个 NumPy 数组对象,类型为 ndarray。

这张成绩表可以这样理解:

每一行表示一位学生;

每一列表示一门课程;

每个位置上的数值表示某位学生某门课的成绩。

这正是 NumPy 最擅长处理的数据形式,因为学生成绩表本质上是一张规则的数值型二维表。

NumPy 数组的核心特点可以概括为三点:

其一,同构性,即同一个数组中的元素通常具有统一的数据类型;

其二,多维性,即它可以表示 0 维、1 维、2 维及更高维数据;

其三,规则性,即二维数组的每一行长度应一致,更高维数组的结构也必须规则。

查看 scores 的类型:

print(type(scores))

输出:

这说明它已经不是普通列表,而是 NumPy 的数组对象。

从更一般的角度看,NumPy 数组并不只用来表示成绩表。一个数、一列数据、一张二维表,甚至更高维的数据结构,都可以用数组表示。但对于入门来说,把它先理解成“适合计算的数值表格”,通常最容易把握。

二、数组的结构:维度、形状与数据类型

建立成绩表之后,第一步不是立刻做计算,而是先看清这张表本身的结构。NumPy 中最基础的几个结构属性,正好可以回答这个问题。

print("数据类型:", scores.dtype)

输出(示意):

数据类型: int64

这几个结果分别表示不同含义。

1、维度:ndim

ndim 表示数组有多少个维度。这里成绩表是二维的,所以结果是 2。

可以粗略理解为:

0 维:一个数(标量 Scalar)

1 维:一列数据(向量 Vector)

2 维:一张表(矩阵 Matrix)

3 维及以上:更复杂的数据块(张量 Tensor)

2、形状:shape

shape 的返回值是一个元组,用来描述各维度的长度。

这里结果是 (4, 3),说明:

有 4 行,即 4 位学生;

有 3 列,即 3 门课程。

3、元素总数:size

size 表示数组中元素总数。这里共有 4 × 3 = 12 个成绩,所以结果是 12。

4、数据类型:dtype

dtype 表示数组中元素的数据类型。由于这里都是整数成绩,所以通常会显示为整数类型。

NumPy 数组通常要求元素类型统一,这也是它能高效进行批量运算的重要原因之一。

三、数组从哪里来:创建与基本查看

前面的成绩表是直接手工录入的,这当然是最直观的方式。

但在实际使用中,数组既可以由现有数据转换而来,也可以按规律、按模板或按随机方式批量生成。

1、把已有数据变成数组

这是最基础的方式。比如使用 np.array() 可将 Python 列表转换成 NumPy 数组:

])

或者:

arr = np.array([1, 2, 3], dtype=float)  # 显式指定元素类型为浮点型

dtype= 参数可用于显式指定数组元素类型,这在后续数值计算中很常见。

从语法上看,np.array(5) 可创建 0 维数组,np.array([1, 2, 3]) 可创建 1 维数组,np.array([[1, 2], [3, 4]]) 可创建 2 维数组。理解这一点,有助于后面进一步理解维度与形状。

2、用规律生成一组数据

例如,使用 np.arange() 生成 0 到 9 的整数:

# [0 1 2 3 4 5 6 7 8 9]

使用 np.linspace() 可生成某个区间上均匀分布的数:

# [  0.  20.  40.  60.  80. 100.]

如果想生成等比序列,可以使用 np.logspace():

 # [  10.  100. 1000.]

这类方法常用于模拟数据、构造测试数据或生成连续数值。arange() 更强调步长,linspace() 强调区间内点数,logspace() 则强调按对数刻度生成数值。

3、创建特殊数组

有时我们需要先创建一张“空白成绩表”或“模板表”,再逐步填入数据。这时可以使用:

full = np.full((4, 3), 60)

它们分别表示:

np.zeros():创建全 0 数组;

np.ones():创建全 1 数组;

np.full():创建全部填充为指定值的数组。

如果要创建单位矩阵,可以使用 np.eye() 和 np.identity():

identity_arr = np.identity(3)

单位矩阵指的是对角线全 1,其它元素全 0 的数组:

 [0. 0. 1.]]

4、创建未初始化数组

有时只想先申请一块内存,而暂时不关心其中的值,可以使用 np.empty():

empty_arr = np.empty((2, 3))

需要注意,np.empty() 创建出来的数组元素值是未初始化的,因此通常不可直接当作有效数据使用,它更适合在后续会立即覆盖赋值的场景。

5、生成随机数据

如果想模拟一张随机成绩表,也可以这样写:

random_scores = np.random.randint(60, 101, size=(4, 3))

seed(42) 用于固定随机结果,保证每次运行都一致;

randint(60, 101, size=(4, 3)) 表示生成一个 4 行 3 列、分数范围在 60 到 100 之间的整数数组。

四、如何取数据与改数据:索引、切片和赋值

有了成绩表之后,最自然的问题就是:怎样查看某位学生的成绩,怎样提取某门课程的数据,怎样修改录入错误的分数。这些问题都属于同一类操作:从数组中取数据、改数据。

1、查看某位学生的全部成绩

数组索引从 0 开始。支持负索引,-1 表示最后一个元素。

print("第三位学生的成绩:", scores[2])

scores[0] 表示第 0 行,即第一位学生的全部成绩。

2、查看某门课程的全部成绩

二维及更高维数组都要用逗号分隔不同维度。

冒号 : 单独使用时,表示该维度的所有元素。

print("全班英语成绩:", scores[:, 2])

scores[:, 0] 表示所有学生的第 1 门课成绩;

scores[:, 1] 表示所有学生的第 2 门课成绩。

3、查看某位学生某门课程的成绩

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 改成 82:

print(scores)

也可以修改整行数据。例如更新第四位学生的全部成绩:

print(scores)

五、让整张表一起运算:整体运算与广播

NumPy 最核心的优势之一,就是它支持整体运算。也就是说,不必逐个元素写循环,而是可以直接让整张成绩表参与计算。

1、整体加分:数组与标量的运算

假设学校决定每门课统一加 5 分,可以直接写:

print(boosted_scores)

说明:+ 5 会自动作用到成绩表中的每一个元素上。

如果要求“最高不超过 100 分”,可以使用 np.clip():

print(boosted_scores)

这表示把所有结果限制在 0 到 100 的范围内。

2、比较目标成绩:数组与数组的运算

假设我们还想看看每位学生与目标分数之间的差距,可以建立一张目标分数表:

print(gap)

这里的减法是逐元素运算。两个形状相同的数组会在对应位置上一一相减。

3、不同课程加不同分:广播

如果不是每门课统一加 5 分,而是:

语文加 2 分;

数学加 5 分;

英语加 3 分;

那么可以这样写:

print(adjusted_scores)

说明:之所以能直接相加,是因为 NumPy 的广播机制在起作用。可以直观地理解为:这组长度为 3 的加分规则,会自动应用到每一位学生身上。

更准确地说,这是因为 scores 的形状是 (4, 3),而 bonus 的形状是 (3,),二者在列方向上可以对齐,因此能够进行广播。

六、真正开始分析成绩:统计函数与 axis

成绩表建立好了,数据也会查看和修改了,接下来最重要的任务就是统计分析。例如:

每位学生总分是多少;

每位学生平均分是多少;

各科平均分是多少;

各科最高分、最低分分别是多少。

这些问题都要用到 NumPy 的统计函数,而其中最关键的概念是 axis。

axis 可理解为“按哪一个维度进行聚合”。对一维数组而言,通常只有 axis=0;对二维数组而言,常见的是 axis=0 和 axis=1。

1、计算每位学生的总分和平均分

print("每位学生平均分:", mean_scores)

为什么这里用 axis=1?因为每一行表示一位学生,按行统计,就能得到每位学生的总体表现。

2、计算各科平均分、最高分和最低分

print("各科最低分:", subject_min)

说明:使用 axis=0,因为每一列表示一门课程,按列统计,就能得到课程维度的结果。

3、几种常见统计函数

除了总和、平均值、最大值、最小值,NumPy 还提供了几种很常见的统计函数:

print("各科最低分所在行索引:", np.argmin(scores, axis=0))

其中:

np.median():计算中位数;

np.std():计算标准差;

np.var():计算方差;

np.argmin():返回最小值位置的索引。

如果想看累积求和,也可以使用 np.cumsum():

print("第一位学生成绩累积和:", np.cumsum(scores[0]))

np.cumsum() 返回累积和,在某些分析任务中也很有用。

七、整理和扩展成绩表:变形、转置、展平与拼接

在实际使用中,我们并不只是对现有成绩表做运算,还常常需要调整表的结构。例如:

一串分数要重新整理成表格;

希望从“按学生看成绩”切换成“按课程看成绩”;

把表格展平成一维序列;

增加一位新学生;

增加一门新课程。

这些操作虽然表面不同,但本质上都属于“调整数组结构”。

1、变形:reshape()

假设现在有 12 个分数,按顺序存放在一个一维数组中:

print(table_scores)

说明:reshape(4, 3) 的含义,是把这 12 个数重新组织为 4 行 3 列的一张表。

这说明,变形并不是改变数据本身,而是改变数据的组织方式。

2、resize():原地修改形状

除了 np.reshape(),NumPy 还提供 np.resize():

print(arr)

reshape() 通常返回新的视图或新结构,而 resize() 会原地修改数组本身,因此入门阶段更常用、也更安全的写法通常还是 reshape()。

3、转置:交换观察角度

print(scores.T)

转置以后:

原来每一行表示学生;

现在每一行变成课程。

转置的意义不只是数学上的形式变化,也是在切换观察数据的角度。

4、展平:flatten() 与 ravel()

有时我们希望把二维表“拉平”为一维数组,这时可以使用展平操作。

print(flat2)

二者的区别在于:

np.flatten() 返回的是拷贝;

np.ravel() 通常返回的是视图,修改结果可能影响原数组。

例如:

print(scores)

拷贝表示生成一份独立数据;视图表示与原数组共享底层数据,因此修改时可能联动。

5、增加一位新学生

如果新来一位学生,三门成绩分别为 91、86、88,可以这样添加:

print(new_scores)

说明:使用 np.vstack(),因为新增学生,本质上是新增一行。

6、增加一门新课程

如果现在又增加一门物理课,4 位学生的物理成绩如下:

print(scores_with_physics)

说明:使用 np.hstack(),因为新增课程,本质上是新增一列。

除了 vstack() 和 hstack(),NumPy 还提供 stack(),它的特点是新增一个维度后再堆叠数组。

变形、转置、展平和拼接,本质上都是在调整数组的组织结构。

八、综合示例:完成一次完整的成绩表分析与保存

最后,把前面的知识放到同一个示例中,完成一次较完整的成绩表分析,并把结果保存下来。

文件读写说明:

np.save() / np.load() 读写的是 NumPy 原生二进制格式 .npy,适合完整保存数组结构与数据类型;

np.savetxt() / np.loadtxt() 适合读写文本文件,便于人工查看,但读入结果默认常为浮点型;如需整数,可结合参数进一步控制。

小结

本次课以学生成绩表为例,学习了 NumPy 数组的基本概念、创建方法、结构属性、索引与切片、整体运算、统计分析、形状调整以及文件读写等内容。通过这些基础知识,可以初步建立面向数组进行数据组织与计算的认识。

应用实例:

打开网易新闻 查看精彩图片

点赞有美意,赞赏是鼓励