数据科学必备:NumPy 详解
在当今数据爆炸的时代,数据科学已成为驱动创新和决策的关键领域。而 Python 作为数据科学领域最受欢迎的编程语言之一,其强大之处不仅在于语言本身的易用性,更在于其丰富且高效的第三方库生态系统。在这其中,NumPy(Numerical Python)无疑是基石中的基石,是进行数值计算、数据分析以及构建更高级数据科学工具的必要前置条件。
本文将深入探讨 NumPy 的核心概念、主要功能以及它为何对于数据科学工作者来说是不可或缺的工具。
一、 NumPy 是什么?为什么它是数据科学的基石?
NumPy 是 Python 中用于科学计算的基础包。它提供了一个高性能的多维数组对象(ndarray
),以及用于处理这些数组的工具。虽然 Python 内建的列表(list
)可以存储不同类型的数据,并且非常灵活,但在处理大量同类型数值数据时,list
的效率远不及 NumPy 数组。
为什么 NumPy 如此重要?
- 高性能: NumPy 的核心
ndarray
对象采用 C 语言实现,这使得它在处理大型数组时具有极高的性能,远超 Python 的原生列表。数据科学常常涉及处理海量数据,性能是至关重要的考量。 - 内存效率: NumPy 数组存储同质数据(相同数据类型),内存布局紧凑且连续,这比 Python 列表存储异质数据(可以存储不同类型的对象引用)更加高效,尤其是在处理大数据时能显著减少内存占用。
- 强大的功能: NumPy 提供了大量的数学函数、线性代数操作、傅里叶变换、随机数生成等功能,这些都是数据科学、机器学习和科学计算中不可或缺的工具。
- 向量化操作: NumPy 允许对整个数组进行操作(称为向量化操作),而无需编写显式的循环。这不仅大大简化了代码,提高了开发效率,而且由于底层是优化过的 C 实现,执行速度也更快。
- 生态系统的基础: NumPy 是许多其他重要科学计算和数据科学库(如 Pandas、SciPy、Matplotlib、Scikit-learn、TensorFlow、PyTorch 等)的基础。这些库在底层都依赖于 NumPy 数组进行数据存储和操作。掌握 NumPy 是学习和使用这些库的前提。
简而言之,NumPy 为 Python 提供了一套高效处理数值数据的能力,正是这种能力使得 Python 能够胜任高性能计算和大规模数据处理的任务,从而成为数据科学的主流语言。
二、 NumPy 的核心:多维数组 ndarray
ndarray
是 NumPy 中最重要的数据结构。它代表一个具有相同数据类型的元素构成的多维同质数组。理解 ndarray
是掌握 NumPy 的关键。
2.1 ndarray
的基本属性
一个 ndarray
对象有几个重要的属性:
ndim
: 数组的维数(轴的数量)。例如,一维数组ndim
为 1,二维数组(矩阵)ndim
为 2。shape
: 一个表示数组各维度大小的元组。例如,一个 2 行 3 列的矩阵shape
为(2, 3)
。size
: 数组中元素的总个数,等于shape
中元素的乘积。dtype
: 数组中元素的数据类型。NumPy 支持多种数据类型,如int64
(64位整数)、float64
(64位浮点数)、bool
(布尔值)等。所有元素的dtype
必须相同。itemsize
: 数组中每个元素占用的字节数。data
: 包含实际数组元素的缓冲区。
2.2 创建 ndarray
NumPy 提供了多种创建数组的方式:
1. 从 Python 列表或元组创建:
使用 np.array()
函数可以将 Python 的列表或元组转换为 NumPy 数组。
“`python
import numpy as np
从列表创建一维数组
list_data = [1, 2, 3, 4, 5]
arr1d = np.array(list_data)
print(“一维数组:”)
print(arr1d)
print(“维数:”, arr1d.ndim)
print(“形状:”, arr1d.shape)
print(“类型:”, arr1d.dtype)
从嵌套列表创建二维数组
list_of_lists = [[1, 2, 3], [4, 5, 6]]
arr2d = np.array(list_of_lists)
print(“\n二维数组:”)
print(arr2d)
print(“维数:”, arr2d.ndim)
print(“形状:”, arr2d.shape)
print(“类型:”, arr2d.dtype)
创建指定数据类型的数组
arr_float = np.array([1, 2, 3], dtype=np.float64)
print(“\n指定浮点数类型的数组:”)
print(arr_float)
print(“类型:”, arr_float.dtype)
“`
2. 创建全零、全一或空数组:
np.zeros(shape)
: 创建指定形状和数据类型(默认为 float64)的全零数组。np.ones(shape)
: 创建指定形状和数据类型(默认为 float64)的全一数组。np.empty(shape)
: 创建指定形状和数据类型的空数组。数组中的元素是未初始化的,其值是内存中的任意值。
“`python
创建全零数组
zeros_arr = np.zeros((2, 3))
print(“\n全零数组 (2×3):”)
print(zeros_arr)
创建全一数组
ones_arr = np.ones((3, 2), dtype=np.int32) # 指定整数类型
print(“\n全一数组 (3×2, int32):”)
print(ones_arr)
创建空数组
empty_arr = np.empty((2, 2))
print(“\n空数组 (2×2):”)
print(empty_arr) # 注意:值是随机的
“`
3. 创建有序数列:
np.arange([start,] stop[, step,], dtype=None)
: 类似于 Python 的range()
函数,创建等差数列,但不包含stop
值。np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
: 创建指定数量(num
)的等差数列,包含start
和stop
值。
“`python
使用 arange 创建数列
arr_range = np.arange(0, 10, 2) # 从0开始,小于10,步长为2
print(“\n使用 arange 创建的数列:”)
print(arr_range)
使用 linspace 创建数列
arr_linspace = np.linspace(0, 1, 5) # 在0和1之间创建5个点,包含0和1
print(“\n使用 linspace 创建的数列:”)
print(arr_linspace)
“`
4. 创建随机数数组:
np.random
模块提供了多种创建随机数数组的函数。
np.random.rand(d0, d1, ..., dn)
: 创建指定形状的 [0, 1) 之间均匀分布的随机数数组。np.random.randn(d0, d1, ..., dn)
: 创建指定形状的标准正态分布(均值为0,方差为1)的随机数数组。np.random.randint(low, high=None, size=None, dtype='l')
: 创建指定范围内的随机整数数组。np.random.random(size=None)
: 创建指定形状的 [0.0, 1.0) 之间均匀分布的随机浮点数数组 (与rand
类似,但参数是元组)。
“`python
创建 3×3 的均匀分布随机数数组
rand_arr = np.random.rand(3, 3)
print(“\n3x3 均匀分布随机数数组:”)
print(rand_arr)
创建 2×4 的标准正态分布随机数数组
randn_arr = np.random.randn(2, 4)
print(“\n2x4 标准正态分布随机数数组:”)
print(randn_arr)
创建一个包含 10 个 1 到 100 之间的随机整数数组
randint_arr = np.random.randint(1, 101, size=10)
print(“\n包含 10 个 1-100 随机整数的数组:”)
print(randint_arr)
“`
三、 数组的索引和切片
访问和操作数组元素是 NumPy 的核心功能之一。NumPy 提供了灵活且强大的索引和切片方式。
3.1 基本索引和切片
与 Python 列表类似,可以使用方括号 []
结合索引或切片来访问元素。对于多维数组,使用逗号 ,
分隔各维度的索引或切片。
“`python
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(“原始二维数组:”)
print(arr)
访问单个元素 (行, 列)
print(“\n访问元素 (1, 2):”, arr[1, 2]) # 第2行第3列的元素 (索引从0开始)
切片:获取第一行
print(“\n获取第一行:”, arr[0, :]) # 或 arr[0]
切片:获取第三列
print(“\n获取第三列:”, arr[:, 2])
切片:获取子矩阵 (前两行,后两列)
print(“\n获取子矩阵 (前两行,后两列):”)
print(arr[:2, 1:])
使用步长进行切片
arr1d_step = np.arange(10)
print(“\n一维数组:”, arr1d_step)
print(“步长为2的切片:”, arr1d_step[::2]) # 从头到尾,步长为2
print(“逆序:”, arr1d_step[::-1]) # 逆序排列
“`
重要说明: 数组切片创建的是原数组的视图(view),而不是副本(copy)。这意味着修改切片的内容会影响原数组。如果需要独立的副本,应使用 .copy()
方法。
“`python
arr_slice = arr[:2, 1:]
print(“\n切片视图:”)
print(arr_slice)
修改切片中的元素
arr_slice[0, 0] = 99
print(“\n修改切片后,原数组:”)
print(arr) # 原数组也被修改了!
创建一个独立的副本
arr_copy = arr[:2, 1:].copy()
arr_copy[0, 0] = 100
print(“\n修改副本后,原数组:”)
print(arr) # 原数组未被修改
print(“副本:”)
print(arr_copy)
“`
3.2 布尔索引 (花式索引的一种)
布尔索引允许使用一个与数组形状相同的布尔数组来选择元素。布尔数组中为 True
的位置对应的原数组元素将被选中。这在数据过滤和选择时非常有用。
“`python
arr = np.array([10, 20, 30, 40, 50, 60])
print(“\n原始数组:”, arr)
创建一个布尔数组,选择大于30的元素
mask = arr > 30
print(“布尔掩码:”, mask)
使用布尔掩码进行索引
filtered_arr = arr[mask]
print(“过滤后的数组 (大于30):”, filtered_arr)
可以直接在索引中创建布尔表达式
print(“直接过滤 (小于等于40):”, arr[arr <= 40])
在二维数组中使用布尔索引
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“\n二维数组:\n”, arr2d)
print(“大于 5 的元素:”, arr2d[arr2d > 5]) # 返回一个一维数组
“`
3.3 整数数组索引 (花式索引的一种)
可以使用整数数组来选择任意顺序的元素子集。
“`python
arr = np.array([10, 20, 30, 40, 50])
print(“\n原始数组:”, arr)
使用索引列表选择元素
indices = [0, 3, 1]
selected_elements = arr[indices]
print(“使用索引列表选择的元素:”, selected_elements) # 结果是 [10, 40, 20]
在二维数组中使用整数数组索引
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“\n二维数组:\n”, arr2d)
选择 (0,0), (1,1), (2,2) 位置的元素
rows = np.array([0, 1, 2])
cols = np.array([0, 1, 2])
print(“对角线元素:”, arr2d[rows, cols]) # 结果是 [1 5 9]
选择 (0,1), (1,0), (2,1) 位置的元素
rows = np.array([0, 1, 2])
cols = np.array([1, 0, 1])
print(“指定位置元素:”, arr2d[rows, cols]) # 结果是 [2 4 8]
复制或重新排列行/列
print(“重复第0行和第2行:\n”, arr2d[[0, 2, 0]])
“`
四、 数组运算:向量化与广播
NumPy 真正的强大之处在于其向量化运算能力。它可以对整个数组执行元素级操作,而无需显式编写 Python 循环。此外,NumPy 的广播(Broadcasting)机制允许在不同形状的数组之间执行运算。
4.1 元素级运算
NumPy 数组之间的基本算术运算(加、减、乘、除、幂等)以及许多数学函数都是元素级的。
“`python
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
数组相加 (元素级)
print(“\n数组相加:”, arr1 + arr2) # [5 7 9]
数组相乘 (元素级)
print(“数组相乘:”, arr1 * arr2) # [ 4 10 18]
标量与数组相乘 (元素级)
print(“标量乘以数组:”, arr1 * 5) # [ 5 10 15]
数组开方 (元素级)
print(“数组开方:”, np.sqrt(arr2))
数组比较 (元素级)
print(“数组比较 (arr1 > arr2):”, arr1 > arr2) # [False False False]
“`
4.2 广播 (Broadcasting)
广播是 NumPy 中一个非常重要的概念,它描述了 NumPy 如何在不同形状的数组之间执行算术运算。当两个数组的形状不同,NumPy 会尝试自动调整其中一个或两个数组的形状,使它们兼容,然后进行元素级运算。这个过程类似于“广播”较小数组以匹配较大数组的形状,而无需实际复制数据。
广播遵循一套严格的规则,但最常见的场景可以概括为:
- 如果两个数组的维度数不同,那么将维度数较小的数组的 shape 前面补 1,直到它们的维度数相同。
- 沿维度大小为 1 的维度进行广播。
- 如果沿任何一个维度,两个数组的大小都不相等且都不为 1,则会引发错误。
常见广播示例:
-
标量与数组运算: 标量可以被广播到任意形状的数组,与数组中的每个元素进行运算。
python
arr = np.array([[1, 2], [3, 4]])
print("\n原始数组:\n", arr)
print("数组 + 标量 10:\n", arr + 10) -
一维数组与二维数组运算: 一个一维数组(向量)可以与一个二维数组(矩阵)进行广播运算,前提是向量的长度与矩阵的某一维(通常是列)匹配。
“`python
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3)
vector = np.array([10, 20, 30]) # shape (3,)vector 的 shape (3,) 会被广播成 (1, 3),然后沿轴 0 进行广播
print(“\n矩阵 + 向量:\n”, matrix + vector)
结果:
[[1+10, 2+20, 3+30],
[4+10, 5+20, 6+30]]
``
(2,)
如果向量的 shape 是并且你想让它加到矩阵的行上,你需要先将其转置或使用
reshape使其 shape 变成
(2, 1),NumPy 再将其广播到
(2, 3)`。“`python
vector_col = np.array([[10], [20]]) # shape (2, 1)
print(“\n矩阵 + 列向量:\n”, matrix + vector_col)结果:
[[1+10, 2+10, 3+10],
[4+20, 5+20, 6+20]]
“`
理解广播机制对于编写简洁高效的 NumPy 代码至关重要。
五、 线性代数操作
NumPy 的 np.linalg
模块提供了丰富的线性代数功能,这对于许多数据科学任务(如机器学习算法中的矩阵运算、数据降维等)非常重要。
“`python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
矩阵乘法 (使用 @ 运算符 或 np.dot)
注意:这不是元素级乘法 (*)
print(“\n矩阵 A:\n”, A)
print(“矩阵 B:\n”, B)
print(“矩阵乘法 (A @ B):\n”, A @ B)
print(“矩阵乘法 (np.dot(A, B)):\n”, np.dot(A, B))
矩阵转置 (.T 属性)
print(“\n矩阵 A 的转置:\n”, A.T)
计算行列式
print(“\n矩阵 A 的行列式:”, np.linalg.det(A))
计算逆矩阵
try:
A_inv = np.linalg.inv(A)
print(“\n矩阵 A 的逆矩阵:\n”, A_inv)
# 验证 A @ A_inv 是否接近单位矩阵
print(“A @ A_inv:\n”, A @ A_inv) # 结果接近单位矩阵 (由于浮点数精度可能略有差异)
except np.linalg.LinAlgError:
print(“\n矩阵 A 是奇异矩阵,无法计算逆矩阵。”)
计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
print(“\n矩阵 A 的特征值:”, eigenvalues)
print(“矩阵 A 的特征向量:\n”, eigenvectors) # 每一列是一个特征向量
“`
np.linalg
模块还包括求解线性方程组、计算奇异值分解 (SVD)、计算范数等许多高级功能。
六、 统计分析
NumPy 提供了许多用于对数组进行描述性统计的函数。这些函数通常可以直接作为数组的方法调用,也可以作为 NumPy 的顶级函数调用。
“`python
data = np.array([10, 20, 30, 40, 50, 60])
print(“\n原始数据:”, data)
计算均值 (Mean)
print(“均值:”, np.mean(data)) # 或 data.mean()
计算中位数 (Median)
print(“中位数:”, np.median(data))
计算标准差 (Standard Deviation)
print(“标准差:”, np.std(data)) # 或 data.std()
计算方差 (Variance)
print(“方差:”, np.var(data)) # 或 data.var()
计算总和 (Sum)
print(“总和:”, np.sum(data)) # 或 data.sum()
计算最小值 (Minimum)
print(“最小值:”, np.min(data)) # 或 data.min()
计算最大值 (Maximum)
print(“最大值:”, np.max(data)) # 或 data.max()
计算累计和 (Cumulative Sum)
print(“累计和:”, np.cumsum(data))
计算累计积 (Cumulative Product)
print(“累计积:”, np.cumprod(data))
“`
处理多维数组的统计:axis
参数
对于多维数组,统计函数通常有一个 axis
参数,用于指定沿哪个轴进行计算。
axis=0
: 沿列进行计算(垂直方向)。axis=1
: 沿行进行计算(水平方向)。axis=None
(默认): 计算整个数组的总和。
“`python
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3)
print(“\n二维数组:\n”, matrix)
计算整个数组的均值
print(“整个数组的均值:”, np.mean(matrix))
计算每列的均值 (axis=0)
print(“每列的均值:”, np.mean(matrix, axis=0)) # 结果 shape (3,) -> [ (1+4)/2, (2+5)/2, (3+6)/2 ]
计算每行的均值 (axis=1)
print(“每行的均值:”, np.mean(matrix, axis=1)) # 结果 shape (2,) -> [ (1+2+3)/3, (4+5+6)/3 ]
计算每列的总和
print(“每列的总和:”, np.sum(matrix, axis=0))
计算每行的最大值
print(“每行的最大值:”, np.max(matrix, axis=1))
``
axis` 参数是 NumPy 统计和许多操作中非常重要的概念,理解它对于正确处理多维数据至关重要。
七、 数组形状操作
改变数组的形状、拼接或分割数组是数据准备和转换中常用的操作。
7.1 改变数组形状
reshape(shape)
: 返回一个具有新形状的数组,原数组数据不变。新形状必须与原数组的元素总数兼容。可以使用-1
在新形状中代表一个由 NumPy 自动推断的维度。ravel()
: 返回一个扁平化(一维)的数组视图。flatten()
: 返回一个扁平化(一维)的数组副本。
“`python
arr = np.arange(12) # 一个包含 12 个元素的数组
print(“\n原始数组:”, arr)
改变形状为 3 行 4 列
reshaped_arr = arr.reshape((3, 4))
print(“\n改变形状为 (3, 4):\n”, reshaped_arr)
改变形状为 4 行 3 列
print(“\n改变形状为 (4, 3):\n”, arr.reshape((4, 3)))
使用 -1 推断维度 (例如,2行,列数自动计算)
print(“\n改变形状为 (2, -1):\n”, arr.reshape((2, -1)))
扁平化数组 (视图)
print(“\n扁平化 (ravel):”, reshaped_arr.ravel())
扁平化数组 (副本)
print(“扁平化 (flatten):”, reshaped_arr.flatten())
“`
7.2 拼接和分割数组
np.concatenate((arr1, arr2, ...), axis=0)
: 沿着指定轴将多个数组拼接起来。np.vstack((arr1, arr2, ...))
: 垂直(按行)堆叠数组(等同于concatenate
沿着axis=0
)。np.hstack((arr1, arr2, ...))
: 水平(按列)堆叠数组(等同于concatenate
沿着axis=1
)。np.split(ary, indices_or_sections, axis=0)
: 将一个数组沿着指定轴分割成多个子数组。
“`python
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
print(“\narr1:\n”, arr1)
print(“arr2:\n”, arr2)
垂直拼接 (按行)
vstack_arr = np.vstack((arr1, arr2))
print(“\n垂直拼接 (vstack):\n”, vstack_arr)
水平拼接 (按列)
hstack_arr = np.hstack((arr1, arr2))
print(“\n水平拼接 (hstack):\n”, hstack_arr)
使用 concatenate 沿着 axis=0 拼接 (垂直)
concat_v = np.concatenate((arr1, arr2), axis=0)
print(“\n使用 concatenate 垂直拼接:\n”, concat_v)
使用 concatenate 沿着 axis=1 拼接 (水平)
concat_h = np.concatenate((arr1, arr2), axis=1)
print(“\n使用 concatenate 水平拼接:\n”, concat_h)
分割数组
arr_to_split = np.arange(16).reshape((4, 4))
print(“\n待分割数组:\n”, arr_to_split)
沿着 axis=0 平均分割成 2 块
split_rows = np.split(arr_to_split, 2, axis=0)
print(“\n沿行平均分割成 2 块:”)
for sub_arr in split_rows:
print(sub_arr)
沿着 axis=1 在指定索引处分割
split_cols = np.split(arr_to_split, [1, 3], axis=1) # 在索引 1 和 3 之前分割
print(“\n沿列在索引 [1, 3] 处分割:”)
for sub_arr in split_cols:
print(sub_arr)
“`
八、 NumPy 与其他库的集成
NumPy 不仅自身功能强大,更重要的是它构成了整个 Python 数据科学生态系统的底层。
- Pandas: Pandas 的核心数据结构
DataFrame
和Series
就是建立在 NumPy 数组之上的。Pandas 利用 NumPy 的高效数组操作来处理结构化数据。将 Pandas 数据转换为 NumPy 数组 (df.values
) 或从 NumPy 数组创建 Pandas 对象 (pd.DataFrame(arr)
) 是常见操作。 - SciPy: SciPy 是一个更高级的科学计算库,它提供了 NumPy 没有的模块,如优化、积分、插值、信号处理、图像处理等。SciPy 的许多函数接受 NumPy 数组作为输入并返回 NumPy 数组。
- Matplotlib: Matplotlib 是一个绘图库。它能够直接接受 NumPy 数组作为输入进行可视化,是绘制数据图表的常用工具。
- Scikit-learn: Scikit-learn 是一个流行的机器学习库。其所有的算法都期望输入的数据是 NumPy 数组或 SciPy 稀疏矩阵。训练和预测的结果也通常是 NumPy 数组。
- 深度学习框架 (TensorFlow, PyTorch): 这些框架中的张量(Tensor)对象在概念上和操作上与 NumPy 数组非常相似,并且提供了方便的函数用于 NumPy 数组和框架自带张量之间的相互转换。
因此,深入理解 NumPy 是有效使用这些高级库的前提。
九、 性能考量与向量化的重要性
前面多次提到 NumPy 的高性能,这主要归功于向量化和其底层实现。
- 向量化: 避免使用显式的 Python
for
循环来处理数组元素。NumPy 提供的各种函数(如np.sum()
,arr + 5
,np.linalg.eig()
,arr[arr > 0]
) 都是向量化操作。它们在底层是用优化的 C 或 Fortran 代码实现的,可以一次处理整个数组或数组的切片,避免了 Python 解释器带来的开销。 - 底层实现: NumPy 的核心是用 C 实现的,这使得它能够直接操作内存,并且可以利用底层的并行化和 SIMD (Single Instruction, Multiple Data) 指令,极大地提高了计算速度。
对比 Python 列表和 NumPy 数组的性能差异 (示例概念,不实际运行复杂基准测试代码):
“`python
概念性示例,展示向量化 vs 循环
场景:将一个大数组中的每个元素都加 1
使用 Python 列表和循环 (慢)
large_list = list(range(1000000))
new_list = []
for item in large_list:
new_list.append(item + 1)
使用 NumPy 数组和向量化 (快)
large_arr = np.arange(1000000)
new_arr = large_arr + 1 # 向量化操作
“`
在处理大规模数据时,这种性能差异会变得非常显著。因此,在进行数据科学计算时,应尽量使用 NumPy 提供的向量化操作,而不是自己编写 Python 循环。
十、 总结
NumPy 是 Python 数据科学生态系统中不可或缺的基础库。它提供了一个高效的多维数组对象 ndarray
,以及丰富的函数用于进行数值计算、线性代数、统计分析和数据操作。掌握 NumPy 的核心概念(ndarray
、索引、切片、广播、向量化)是进行高效数据处理和分析的关键。
无论是进行简单的数据加载和预处理,还是构建复杂的机器学习模型,NumPy 都将是您最常用的工具之一。它是通往 Pandas、SciPy、Scikit-learn 等更高级库的必经之路。花时间深入学习和实践 NumPy 的各种功能,将极大地提升您在数据科学领域的效率和能力。
从创建数组到进行复杂的数学运算,从灵活的索引切片到强大的广播机制,NumPy 为 Python 带来了处理海量数值数据的“肌肉”。它是数据科学家的“瑞士军刀”,必备且强大。
通过本文的详细介绍,希望您对 NumPy 有了更深入的理解,并认识到它在数据科学中的核心地位。接下来,最重要的是动手实践,通过编写代码来巩固这些知识,并在实际项目中灵活运用 NumPy 的强大功能。