深入浅出 NumPy:核心概念详解与 PyPI 安装全攻略
引言:开启科学计算的大门
在现代科学研究、数据分析、机器学习以及人工智能领域,Python 已经成为最受欢迎的编程语言之一。而 Python 之所以能在这些领域大放异彩,很大程度上得益于其强大且丰富的第三方库生态系统。在众多库中,NumPy(Numerical Python 的缩写)无疑是基石中的基石。
NumPy 提供了一个高性能的多维数组对象以及用于处理这些数组的工具。它是许多其他科学计算库(如 SciPy, Matplotlib, scikit-learn, TensorFlow, PyTorch 等)的基础。可以说,离开了 NumPy,Python 在数值计算方面的效率将大打折扣,很多复杂的数据处理和分析任务也将难以实现。
本文旨在为初学者和希望系统性回顾 NumPy 基础知识的读者,提供一个全面的指南。我们将首先详细讲解如何通过 Python 的包管理工具 PyPI(Python Package Index)安装 NumPy,然后深入剖析 NumPy 最核心的概念——多维数组(ndarray)及其重要属性,并详细介绍数组的创建、索引、切片、操作以及 NumPy 的广播机制等关键知识点。通过本文的学习,您将能够建立起扎实的 NumPy 基础,为进一步探索更高级的科学计算和数据分析领域打下坚实的基础。
第一部分:NumPy 的安装——通过 PyPI
在开始学习 NumPy 的核心概念之前,我们首先需要将它安装到您的 Python 环境中。PyPI 是 Python 官方的第三方库仓库,而 pip
是 Python 默认的包管理器,通过 pip
从 PyPI 安装库是最常见、最便捷的方式。
1. 前提条件
在安装 NumPy 之前,请确保您的系统已经满足以下条件:
- 安装了 Python: NumPy 支持 Python 3.7 及以上版本。您可以从 Python 官方网站 (https://www.python.org/downloads/) 下载并安装最新版本的 Python。安装时请勾选 “Add Python to PATH” 选项,以便在命令行中直接使用
python
和pip
命令。 - 安装了 pip: 从 Python 3.4 开始,
pip
默认随 Python 一同安装。如果您的 Python 版本较旧或pip
出现问题,可以按照 Python 官方文档的方法进行安装或升级pip
。
2. 强烈推荐:使用虚拟环境(Virtual Environment)
在安装任何第三方库时,都强烈推荐使用虚拟环境。虚拟环境可以在您的系统中创建一个独立的 Python 环境,使得项目所需的库与系统全局的 Python 环境隔离开来。这样做的好处包括:
- 避免库版本冲突: 不同项目可能依赖同一库的不同版本,虚拟环境可以为每个项目维护独立的依赖关系,避免冲突。
- 环境纯净: 您可以只在虚拟环境中安装当前项目必需的库,保持环境整洁。
- 易于管理: 复制、备份或删除项目环境变得非常简单。
创建和激活虚拟环境的方法取决于您使用的工具。Python 3.3+ 自带 venv
模块,是创建虚拟环境的标准方式。
使用 venv
创建虚拟环境(推荐):
- 打开命令行或终端:
- Windows: 命令提示符 (cmd) 或 PowerShell
- macOS/Linux: 终端
- 进入您的项目目录: 例如
cd path/to/your/project
- 创建虚拟环境: 执行命令
python -m venv myenv
。这会在当前目录下创建一个名为myenv
的文件夹(您可以替换myenv
为任何您喜欢的名称)。 - 激活虚拟环境:
- Windows:
.\myenv\Scripts\activate
- macOS/Linux:
source myenv/bin/activate
激活成功后,您的命令行提示符前面会显示虚拟环境的名称(例如(myenv)
),表示您当前的操作都在这个独立的 Python 环境中进行。
- Windows:
现在您已经进入了虚拟环境,接下来就可以放心地安装 NumPy 了。
3. 使用 pip 安装 NumPy
在激活了虚拟环境后,安装 NumPy 就非常简单了。只需执行以下命令:
bash
pip install numpy
这条命令会连接到 PyPI,下载最新版本的 NumPy 包及其所有依赖(如果 NumPy 有依赖其他库的话),并将其安装到当前激活的虚拟环境中。
如果您需要安装特定版本的 NumPy,可以使用以下命令:
bash
pip install numpy==1.26.4 # 替换为您需要的版本号
安装过程可能需要一些时间,具体取决于您的网络速度。安装完成后,pip
会显示安装成功的消息。
4. 验证安装
安装完成后,您可以简单地在 Python 环境中导入 NumPy 来验证是否安装成功。
- 进入 Python 交互式环境: 在命令行中输入
python
或python3
(取决于您的系统配置),确保您仍在激活的虚拟环境中。 -
导入 NumPy 并查看版本:
python
import numpy as np
print(np.__version__)如果能够成功导入 NumPy 并打印出其版本号(例如
1.26.4
或更高版本),说明 NumPy 已经成功安装并可以在您的虚拟环境中使用。 -
尝试一个简单的 NumPy 操作:
python
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr)
print(arr.shape)如果这段代码能够顺利执行并打印出数组和其形状,恭喜您,NumPy 环境已准备就绪,您可以开始探索它的强大功能了!
5. 安装故障排除
在安装过程中,您可能会遇到一些问题。以下是一些常见的故障及其解决方法:
- 网络问题: 如果下载速度很慢或连接超时,可能是网络问题。您可以尝试更换网络环境,或者使用国内的 PyPI 镜像源来加速下载。例如,使用豆瓣源:
bash
pip install numpy -i https://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com
或者清华源:
bash
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn - pip 版本过旧: 有时旧版本的 pip 可能无法正确安装最新的库。您可以升级 pip:
bash
python -m pip install --upgrade pip - 权限问题: 在某些系统上,直接安装到系统全局 Python 环境可能需要管理员权限。这就是为什么强烈推荐使用虚拟环境的原因,在虚拟环境中通常没有权限问题。如果您坚持不使用虚拟环境并在全局环境安装遇到权限问题,可以尝试使用
sudo
(macOS/Linux) 或以管理员身份运行命令提示符 (Windows),但这通常不是推荐的做法。 - 编译器或构建工具缺失: 在某些情况下,安装某些需要编译的 Python 库(NumPy 通常预编译好了轮子文件
.whl
,但在特殊情况下或安装旧版本时可能需要)可能依赖于 C 或 Fortran 编译器。如果您在 Windows 上遇到编译错误,可能需要安装 Microsoft Visual C++ Build Tools。在 Linux 上,可能需要安装build-essential
或类似的开发工具包。但是,对于大多数现代 Python 版本和 NumPy 版本,通过 pip 安装应该会自动下载预编译好的二进制包,通常不会遇到这个问题。
通过以上步骤,您应该能够成功安装 NumPy 并为其后续学习做好准备。
第二部分:NumPy 的核心概念——多维数组(ndarray)及其操作
安装好 NumPy 后,我们就可以深入学习它的核心——ndarray
对象以及如何有效地使用它。
1. ndarray
:NumPy 的基石
ndarray
(n-dimensional array) 是 NumPy 中最重要的数据结构。它表示一个由具有相同数据类型的元素组成的多维同质数组。与 Python 原生的列表(list)相比,ndarray
具有以下显著优势:
- 速度快: NumPy 的操作是向量化的,并且许多底层代码是用 C 或 Fortran 编写的,这使得它在处理大型数组时比 Python 列表快得多。
- 内存效率高:
ndarray
中的元素都是相同类型的,存储更紧凑。此外,NumPy 可以直接对整块内存进行操作,而 Python 列表存储的是对象的引用,开销更大。 - 功能丰富: NumPy 提供了大量的数学函数和操作,可以轻松地对整个数组或数组的子集进行计算。
简而言之,当进行数值计算时,尽可能地使用 NumPy 数组而不是 Python 列表。
2. ndarray
的重要属性
每个 ndarray
对象都有一些重要的属性,可以帮助我们了解数组的结构和内容:
.shape
: 一个元组,表示数组在每个维度上的大小。例如,一个形状为(3, 4)
的二维数组有 3 行和 4 列。一个一维数组的形状可能像(5,)
。.ndim
: 一个整数,表示数组的维度数量(轴的数量)。.size
: 一个整数,表示数组中元素的总数量,等于.shape
元组中所有元素的乘积。.dtype
: 一个对象,表示数组中元素的数据类型。所有元素都必须具有相同的数据类型。NumPy 提供了多种数据类型,如int64
(64位整数),float64
(64位浮点数),bool
(布尔值),complex128
(128位复数) 等。.itemsize
: 一个整数,表示数组中每个元素占用的字节数。例如,float64
的itemsize
通常是 8。.data
: 一个缓冲区对象,指向数组元素所在的内存地址。通常我们不需要直接操作它。
示例:
“`python
import numpy as np
创建一个二维数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
输出:
[[1 2 3]
[4 5 6]]
print(“形状:”, arr.shape) # 输出: 形状: (2, 3)
print(“维度:”, arr.ndim) # 输出: 维度: 2
print(“元素总数:”, arr.size) # 输出: 元素总数: 6
print(“元素类型:”, arr.dtype) # 输出: 元素类型: int64 (或根据系统可能是 int32)
print(“单个元素字节数:”, arr.itemsize) # 输出: 单个元素字节数: 8 (如果 dtype 是 int64)
“`
3. 创建 ndarray
有多种方法可以创建 ndarray
:
-
从 Python 列表或元组创建: 使用
np.array()
函数。“`python
list_data = [1, 2, 3]
arr1d = np.array(list_data)
print(arr1d) # [1 2 3]list_of_lists = [[1, 2], [3, 4]]
arr2d = np.array(list_of_lists)
print(arr2d)[[1 2]
[3 4]]
可以指定数据类型
arr_float = np.array([1.0, 2.5, 3.1], dtype=np.float32)
print(arr_float) # [1. 2.5 3.1]
print(arr_float.dtype) # float32
“` -
使用 NumPy 的内置函数: NumPy 提供了许多用于创建特定类型数组的函数。
np.zeros(shape, dtype=float)
:创建一个指定形状和类型的全零数组。np.ones(shape, dtype=float)
:创建一个指定形状和类型的全一数组。np.empty(shape, dtype=float)
:创建一个指定形状和类型的空数组,其元素值是随机的(取决于内存中的内容)。np.arange(start, stop, step, dtype=None)
:创建一个给定间隔内的均匀间隔值数组(类似 Python 的range
)。stop
值不包含在内。np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
:创建一个指定间隔内包含指定数量元素的均匀间隔值数组。stop
值默认包含在内。np.full(shape, fill_value, dtype=None)
:创建一个指定形状和类型的全填充数组。np.eye(N, M=None, k=0, dtype=float)
:创建一个对角线为 1,其余元素为 0 的二维数组(单位矩阵)。N
是行数,M
是列数,k
是对角线索引(0 为主对角线)。
示例:
“`python
zeros_arr = np.zeros((2, 3))
print(“全零数组:\n”, zeros_arr)[[0. 0. 0.]
[0. 0. 0.]]
ones_arr = np.ones((4,))
print(“全一数组:\n”, ones_arr)[1. 1. 1. 1.]
empty_arr = np.empty((2, 2))
print(“空数组:\n”, empty_arr) # 值不确定,每次运行可能不同range_arr = np.arange(0, 10, 2)
print(“arange数组:”, range_arr) # [0 2 4 6 8]linspace_arr = np.linspace(0, 1, 5)
print(“linspace数组:”, linspace_arr) # [0. 0.25 0.5 0.75 1. ]full_arr = np.full((3, 2), 7)
print(“全7数组:\n”, full_arr)[[7 7]
[7 7]
[7 7]]
eye_arr = np.eye(3)
print(“单位矩阵:\n”, eye_arr)[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
“`
4. 数组索引与切片
像 Python 列表一样,NumPy 数组也可以通过索引和切片访问其元素。但 NumPy 的索引和切片功能更加强大和灵活。
-
基本索引: 使用方括号
[]
访问单个元素或子数组。对于多维数组,可以使用逗号分隔不同维度的索引。“`python
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“原始数组:\n”, arr)print(“第一个元素:”, arr[0, 0]) # 输出: 1
print(“第二行第三列元素:”, arr[1, 2]) # 输出: 6
print(“第三行:”, arr[2]) # 输出: [7 8 9]
print(“第一列:”, arr[:, 0]) # 输出: [1 4 7] (使用切片和基本索引)
“` -
切片 (Slicing): 使用
start:stop:step
语法获取数组的子视图。切片操作返回的是原数组的“视图”(view),而不是副本(copy)。这意味着对切片进行的修改会直接影响原数组。“`python
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])print(“前两行:\n”, arr[:2, :]) # 或 arr[:2]
[[1 2 3]
[4 5 6]]
print(“后两列:\n”, arr[:, 1:])
[[2 3]
[5 6]
[8 9]]
print(“第二行和第三行,第二列到第三列:\n”, arr[1:3, 1:3])
[[5 6]
[8 9]]
切片是视图,修改会影响原数组
sub_arr = arr[:2, :2]
print(“子数组视图:\n”, sub_arr)[[1 2]
[4 5]]
sub_arr[0, 0] = 99
print(“修改子数组后:\n”, sub_arr)[[99 2]
[ 4 5]]
print(“修改后原始数组:\n”, arr)
[[99 2 3]
[ 4 5 6]
[ 7 8 9]]
“`
-
花式索引 / 整数数组索引 (Fancy Indexing / Integer Array Indexing): 使用一个整数数组或列表作为索引,可以根据索引数组的元素来选择原数组中的元素。花式索引返回的是数组的“副本”(copy),而不是视图。
“`python
arr = np.array([10, 20, 30, 40, 50])
indices = [1, 3, 0]
selected_elements = arr[indices]
print(“通过整数数组索引选择元素:”, selected_elements) # [20 40 10]arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
row_indices = [0, 2, 1]
col_indices = [1, 0, 2]选择元素 (arr2d[0, 1]), (arr2d[2, 0]), (arr2d[1, 2])
selected_elements_2d = arr2d[row_indices, col_indices]
print(“通过整数数组索引选择二维元素:”, selected_elements_2d) # [2 7 6]花式索引是副本,修改不影响原数组
copy_arr = arr[indices]
copy_arr[0] = 999
print(“修改副本后:”, copy_arr) # [999 40 10]
print(“原始数组:”, arr) # [10 20 30 40 50] (未改变)
“` -
布尔索引 (Boolean Indexing / Masking): 使用一个与原数组形状相同的布尔数组作为索引,布尔数组中为
True
的位置对应的原数组元素会被选中。布尔索引返回的也是数组的“副本”。“`python
arr = np.array([10, 20, 30, 40, 50])
mask = arr > 25
print(“布尔掩码:”, mask) # [False False True True True]
selected_elements = arr[mask]
print(“通过布尔索引选择元素:”, selected_elements) # [30 40 50]arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
mask2d = arr2d % 2 == 0 # 选择偶数
print(“二维布尔掩码:\n”, mask2d)[[False True False]
[ True False True]
[False True False]]
selected_elements_2d = arr2d[mask2d]
print(“通过布尔索引选择二维元素:”, selected_elements_2d) # [2 4 6 8] (结果是一维数组)布尔索引是副本,修改不影响原数组
copy_arr = arr[mask]
copy_arr[0] = 999
print(“修改副本后:”, copy_arr) # [999 40 50]
print(“原始数组:”, arr) # [10 20 30 40 50] (未改变)
``
.copy()
理解切片是视图而花式索引/布尔索引是副本对于避免意外修改数据非常重要。如果您需要切片的副本,可以使用方法:
sub_arr_copy = arr[:2, :2].copy()`。
5. 数组操作
NumPy 提供了丰富的数组操作功能,包括元素级别的数学运算、比较运算、逻辑运算以及聚合函数等。
-
元素级别运算: 算术运算符 (+, -, , /, //, %, *) 和比较运算符 (>, <, >=, <=, ==, !=) 在 NumPy 数组上默认执行元素级别的操作。
“`python
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])print(“加法:”, arr1 + arr2) # [5 7 9]
print(“乘法:”, arr1 * arr2) # [4 10 18]
print(“平方:”, arr1 ** 2) # [1 4 9]
print(“比较:”, arr1 > arr2) # [False False False]arr2d_a = np.array([[1, 2], [3, 4]])
arr2d_b = np.array([[5, 6], [7, 8]])
print(“二维加法:\n”, arr2d_a + arr2d_b)[[ 6 8]
[10 12]]
“`
这些操作也非常快,因为它们是 NumPy 内部向量化实现的。 -
数学函数: NumPy 提供了大量的通用函数(ufunc),可以直接应用于数组的每个元素。
“`python
arr = np.array([0, np.pi/2, np.pi])
print(“sin:”, np.sin(arr)) # [0.000e+00 1.000e+00 1.225e-16] (接近于0)arr = np.array([1, 4, 9])
print(“sqrt:”, np.sqrt(arr)) # [1. 2. 3.]arr = np.array([1, 2, 3])
print(“exp:”, np.exp(arr)) # [ 2.71828183 7.3890561 20.08553692]
``
np.abs
常见的 ufunc 包括,
np.log,
np.log10,
np.ceil,
np.floor,
np.round,
np.maximum,
np.minimum` 等。 -
聚合函数 (Aggregation): NumPy 提供了计算数组统计特性的函数,如求和、平均值、最小值、最大值等。这些函数可以应用于整个数组,也可以沿着某个特定的轴(维度)进行计算。
“`python
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“原始数组:\n”, arr)print(“总和:”, np.sum(arr)) # 输出: 45 (1+2+3+4+5+6+7+8+9)
print(“平均值:”, np.mean(arr)) # 输出: 5.0 (45 / 9)
print(“最小值:”, np.min(arr)) # 输出: 1
print(“最大值:”, np.max(arr)) # 输出: 9
print(“标准差:”, np.std(arr)) # 输出: 2.581989…沿着轴进行计算
print(“沿着行求和 (axis=0):”, np.sum(arr, axis=0)) # [1+4+7, 2+5+8, 3+6+9] = [12 15 18]
print(“沿着列求和 (axis=1):”, np.sum(arr, axis=1)) # [1+2+3, 4+5+6, 7+8+9] = [ 6 15 24]print(“沿着行求最大值 (axis=0):”, np.max(arr, axis=0)) # [7 8 9]
print(“沿着列求最小值 (axis=1):”, np.min(arr, axis=1)) # [1 4 7]
``
axis
理解参数非常重要。对于一个 N 维数组,
axis=0表示沿着第一个轴(通常是“行”)进行操作,结果会减少一个维度;
axis=1` 表示沿着第二个轴(通常是“列”)进行操作,依此类推。 -
矩阵乘法: 对于二维数组,NumPy 提供了矩阵乘法的功能。从 Python 3.5 开始,可以使用
@
运算符进行矩阵乘法。NumPy 也提供了np.dot()
函数,虽然它可以用于矩阵乘法,但对于多维数组有更广泛的含义(点积)。“`python
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])print(“矩阵乘法 (@ 运算符):\n”, mat1 @ mat2)
[[15 + 27, 16 + 28],
[35 + 47, 36 + 48]]
[[19 22]
[43 50]]
print(“矩阵乘法 (np.dot):\n”, np.dot(mat1, mat2))
[[19 22]
[43 50]]
“`
6. 广播机制 (Broadcasting)
广播是 NumPy 的一个强大而灵活的功能,它描述了 NumPy 如何在执行算术运算时处理具有不同形状的数组。当两个形状不同的数组进行元素级别运算时,如果满足某些规则,NumPy 会自动扩展(“广播”)其中一个或两个数组,使其形状兼容,而无需实际复制数据。
广播的规则:当操作两个数组时,NumPy 会逐个比较它们的维度,从最后一个维度开始向前比较。两个维度是兼容的,如果:
1. 它们相等。
2. 其中一个维度为 1。
不兼容的维度会引发错误。
如果两个数组的维度数量不同,那么维度较少的数组会在其前面填充 1,直到维度数量相等,然后再按照上述规则进行比较。
示例:
-
标量与数组广播: 标量可以广播到任何形状的数组。
python
arr = np.array([1, 2, 3])
print(arr + 5) # [1+5, 2+5, 3+5] = [6 7 8]
这里的标量5
被广播成形状为(3,)
的数组[5, 5, 5]
,然后进行元素加法。 -
一维数组与二维数组广播:
“`python
arr2d = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
arr1d = np.array([10, 20, 30]) # 形状 (3,)print(arr2d + arr1d)
广播过程:
arr1d 的形状 (3,) 被扩展为 (1, 3)
然后 (1, 3) 沿着第一个轴(行)被广播以匹配 (2, 3)
相当于 [[10, 20, 30], [10, 20, 30]]
结果:
[[1 + 10, 2 + 20, 3 + 30],
[4 + 10, 5 + 20, 6 + 30]]
[[11 22 33]
[14 25 36]]
“`
-
不兼容的广播示例:
“`python
arr2d = np.array([[1, 2], [3, 4]]) # 形状 (2, 2)
arr1d = np.array([10, 20, 30]) # 形状 (3,)比较维度:
数组1: (2, 2)
数组2: ( , 3) -> 扩展为 (1, 3)
从后向前比较:
最后一个维度: 2 和 3 -> 不兼容
这将引发 ValueError: operands could not be broadcast together with shapes (2,2) (3,)
print(arr2d + arr1d) # 会报错
“`
广播机制是 NumPy 提高效率的关键之一,它避免了创建大型的中间数组副本。理解广播规则对于编写高效的 NumPy 代码非常重要。
7. 数组的重塑与操作
NumPy 提供了多种方法来改变数组的形状或组合/分割数组。
-
重塑 (Reshaping): 使用
.reshape()
方法或np.reshape()
函数改变数组的形状。新的形状必须与原数组的总元素数量一致。可以使用-1
让 NumPy 自动计算该维度的大小。“`python
arr = np.arange(12) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(“原始数组:”, arr)reshaped_arr = arr.reshape((3, 4))
print(“重塑为 (3, 4):\n”, reshaped_arr)[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
reshaped_arr_auto = arr.reshape((2, -1)) # 自动计算列数
print(“重塑为 (2, -1):\n”, reshaped_arr_auto)[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
``
.reshape()` 返回的是一个视图(如果可能的话),否则是副本。 -
展平 (Flattening): 将多维数组变为一维数组。可以使用
.ravel()
或.flatten()
。.ravel()
返回的是一个视图(如果可能),而.flatten()
总是返回一个副本。python
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print("展平 (ravel):", arr2d.ravel()) # [1 2 3 4 5 6]
print("展平 (flatten):", arr2d.flatten()) # [1 2 3 4 5 6] -
转置 (Transposing): 交换数组的维度。对于二维数组,就是行列互换。可以使用
.T
属性或np.transpose()
函数。“`python
arr2d = np.array([[1, 2], [3, 4], [5, 6]]) # 形状 (3, 2)
print(“原始数组:\n”, arr2d)transposed_arr = arr2d.T
print(“转置后数组:\n”, transposed_arr) # 形状 (2, 3)[[1 3 5]
[2 4 6]]
“`
转置通常返回的是视图。 -
堆叠 (Stacking): 将多个数组沿某个轴堆叠起来形成一个更高维度的数组。
np.vstack()
(垂直堆叠,沿第一个轴)和np.hstack()
(水平堆叠,沿第二个轴)是常用的函数。更通用的函数是np.concatenate()
,它允许指定沿着哪个轴连接。“`python
arr1 = np.array([1, 2])
arr2 = np.array([3, 4])print(“垂直堆叠 (vstack):\n”, np.vstack((arr1, arr2)))
[[1 2]
[3 4]]
print(“水平堆叠 (hstack):”, np.hstack((arr1, arr2)))
[1 2 3 4]
arr_a = np.array([[1, 2], [3, 4]])
arr_b = np.array([[5, 6], [7, 8]])print(“concatenate 沿轴 0:\n”, np.concatenate((arr_a, arr_b), axis=0))
[[1 2]
[3 4]
[5 6]
[7 8]]
print(“concatenate 沿轴 1:\n”, np.concatenate((arr_a, arr_b), axis=1))
[[1 2 5 6]
[3 4 7 8]]
“`
-
分割 (Splitting): 将一个数组沿某个轴分割成多个子数组。
np.vsplit()
(垂直分割)、np.hsplit()
(水平分割)和np.split()
是常用的函数。“`python
arr = np.arange(12).reshape((3, 4))
print(“原始数组:\n”, arr)split_v = np.vsplit(arr, 3) # 沿垂直方向分割成 3 份
print(“垂直分割:\n”, split_v)[array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]
split_h = np.hsplit(arr, 2) # 沿水平方向分割成 2 份
print(“水平分割:\n”, split_h)[array([[ 0, 1], [ 4, 5], [ 8, 9]]), array([[ 2, 3], [ 6, 7], [10, 11]])]
“`
-
添加/移除维度:
-
使用
np.newaxis
或None
在现有轴上添加一个新的维度。这常用于广播。“`python
arr = np.array([1, 2, 3]) # 形状 (3,)
print(“原始形状:”, arr.shape)arr_row = arr[np.newaxis, :] # 在第一个轴前添加一个新维度
print(“添加行维度后形状:”, arr_row.shape) # (1, 3)
print(arr_row) # [[1 2 3]]arr_col = arr[:, np.newaxis] # 在第二个轴(后面)添加一个新维度
print(“添加列维度后形状:”, arr_col.shape) # (3, 1)
print(arr_col)[[1]
[2]
[3]]
``
np.squeeze()` 移除形状中大小为 1 的维度。
* 使用“`python
arr = np.array([[[1], [2], [3]]]) # 形状 (1, 3, 1)
print(“原始形状:”, arr.shape)squeezed_arr = np.squeeze(arr)
print(“移除大小为 1 的维度后形状:”, squeezed_arr.shape) # (3,)
print(squeezed_arr) # [1 2 3]
“`
-
8. 数据类型 (dtype
) 的重要性
前面提到,ndarray
是同质的,所有元素必须具有相同的数据类型。数据类型 (dtype
) 决定了数组中每个元素在内存中占用的空间以及如何解释这些字节。指定合适的数据类型对于内存使用和计算性能都很重要。
NumPy 提供了比 Python 原生类型更丰富、更精确的数值类型,例如不同位数的整数(int8
, int16
, int32
, int64
)、浮点数(float16
, float32
, float64
)、复数(complex64
, complex128
)、布尔值(bool
)等。
创建数组时,NumPy 会根据输入的数据自动推断 dtype
。如果需要特定的类型,可以使用 dtype
参数显式指定。
“`python
arr_int = np.array([1, 2, 3]) # 默认可能是 int64 或 int32
print(arr_int.dtype)
arr_float = np.array([1.0, 2.0, 3.0]) # 默认 float64
print(arr_float.dtype)
arr_explicit_float = np.array([1, 2, 3], dtype=np.float32)
print(arr_explicit_float.dtype) # float32
arr_bool = np.array([0, 1, 0], dtype=np.bool_)
print(arr_bool.dtype) # bool_
print(arr_bool) # [False True False]
“`
可以通过 .astype()
方法将数组转换为不同的数据类型,这会返回一个新数组(副本)。
python
arr_float = np.array([1.5, 2.3, 3.7])
arr_int_casted = arr_float.astype(np.int64)
print(arr_int_casted) # [1 2 3] (小数部分被截断)
print(arr_int_casted.dtype) # int64
选择合适的数据类型可以显著影响程序的内存占用,尤其是在处理海量数据时。例如,如果确定数据不会超过某个范围,使用 int32
而不是默认的 int64
可以节省一半的内存。
结论
NumPy 作为 Python 科学计算的基石,其重要性不言而喻。通过本文,我们详细了解了如何通过 PyPI 及其配套工具 pip 安装 NumPy,并强烈推荐了使用虚拟环境的最佳实践。随后,我们深入学习了 NumPy 最核心的数据结构 ndarray
的特性,包括其形状、维度、大小和数据类型等重要属性。我们还探讨了创建 ndarray
的各种方法,以及 NumPy 强大的索引、切片和各种数组操作,特别是广播机制这一独特的强大功能。
掌握 NumPy 的 ndarray
及其操作是使用 Python 进行高效数值计算和数据分析的关键。虽然本文涵盖了 NumPy 的核心概念,但这仅仅是 NumPy 功能的冰山一角。NumPy 还提供了线性代数、随机数生成、傅里叶变换等更多高级功能。
学习 NumPy 的最佳方式是实践。建议您在阅读本文的同时,在 Python 环境中动手尝试每个代码示例,并通过修改参数和数据来观察结果。熟悉并熟练运用 ndarray
和 NumPy 的各种函数,将极大地提升您在科学计算和数据处理领域的效率。
现在,您已经掌握了 NumPy 的安装方法和核心概念,可以自信地开始使用它来解决实际问题了。祝您在探索 NumPy 的世界中取得丰硕的成果!