学习 NumPy:一份详尽的指南 – wiki基地

我已为你写好一篇详细描述“学习 NumPy:一份详尽的指南”的文章。


学习 NumPy:一份详尽的指南

引言

在数据科学、机器学习、工程和科学计算的广阔领域中,Python 已经成为了一种不可或缺的工具。而在这片繁荣的生态系统中,NumPy(Numerical Python)无疑是最为基石的库之一。它提供了一个高性能的多维数组对象,即 ndarray,以及用于高效操作这些数组的大量函数。

为什么 NumPy 如此重要?

NumPy 的核心价值在于它显著提升了 Python 处理数值数据的能力,尤其是在大规模数据集上。它不仅仅是 Python 列表的替代品,更是一个经过优化的、功能强大的数学计算引擎。以下是 NumPy 脱颖而出的几个关键原因:

  1. 卓越的性能:与标准的 Python 列表相比,NumPy 数组上的操作在内部由 C 或 C++ 实现,这使得它们的速度最多可以快 50 倍,特别是在处理大型数据集时。这种性能的提升对于数据密集型任务至关重要。
  2. 内存效率:NumPy 数组的内存占用更低,提供了一种更高效的方式来存储和处理大量数据。这意味着你可以在有限的内存资源下处理更大的数据集。
  3. 操作便利性:NumPy 提供了一套丰富的内置函数,可以简化复杂的数学计算,从基本的算术运算到高级的线性代数操作,应有尽有。这大大减少了你需要编写的代码量。
  4. 矢量化操作:NumPy 支持矢量化操作,允许你对整个数组执行元素级计算,而无需编写显式的 Python 循环。这不仅提升了代码的简洁性,也进一步优化了执行速度。
  5. 无缝集成:NumPy 与 Python 科学计算生态系统中的其他关键库(如 Pandas、Matplotlib、SciPy 和 scikit-learn)无缝集成,是这些库正常运行的基础。

简而言之,如果你想在 Python 中进行任何形式的数值计算或数据处理,学习和掌握 NumPy 都是必不可少的一步。本指南将带你深入了解 NumPy 的世界,从基础概念到高级应用,助你充分利用其强大功能。

核心概念

ndarray 对象:NumPy 的核心

ndarray(N-dimensional array object)是 NumPy 的核心数据结构,它表示一个同构的、N 维的固定大小项目数组。与 Python 内置的列表不同,ndarray 中的所有元素必须是相同的数据类型(例如,所有都是整数或所有都是浮点数),并且一旦创建,其总大小就不能改变。

ndarray 是 NumPy 效率和强大功能的基础,它允许我们以优化的方式存储和操作大量数值数据。

ndarray 的关键属性

理解 ndarray 的属性对于有效使用 NumPy 至关重要。以下是一些最常用的属性:

  1. ndim:数组的维度(轴的数量)。例如,一维数组的 ndim 为 1,二维矩阵的 ndim 为 2。
    “`python
    import numpy as np
    arr_1d = np.array([1, 2, 3])
    print(arr_1d.ndim) # 输出: 1

    arr_2d = np.array([[1, 2], [3, 4]])
    print(arr_2d.ndim) # 输出: 2
    “`

  2. shape:一个元组,表示数组在每个维度上的大小。例如,一个 2 行 3 列的矩阵的 shape(2, 3)
    python
    print(arr_1d.shape) # 输出: (3,)
    print(arr_2d.shape) # 输出: (2, 2)

  3. size:数组中元素的总数,等于 shape 中所有元素的乘积。
    python
    print(arr_1d.size) # 输出: 3
    print(arr_2d.size) # 输出: 4

  4. dtype:数组中元素的预期数据类型。NumPy 支持多种数据类型,例如 int64float32bool_ 等。理解 dtype 对于内存管理和确保计算的准确性非常重要。
    python
    print(arr_1d.dtype) # 输出: int64 (或平台相关的整数类型)
    arr_float = np.array([1.0, 2.5])
    print(arr_float.dtype) # 输出: float64

  5. itemsize:数组中每个元素所占的字节数。
    python
    print(arr_1d.itemsize) # 输出: 8 (对于 int64)
    print(arr_float.itemsize) # 输出: 8 (对于 float64)

  6. nbytes:数组所有元素消耗的总字节数,等于 size * itemsize
    python
    print(arr_1d.nbytes) # 输出: 24 (3 * 8)
    print(arr_float.nbytes) # 输出: 16 (2 * 8)

通过掌握这些基本概念和属性,你就能为后续更复杂的 NumPy 操作打下坚实的基础。

安装和导入 NumPy

在开始使用 NumPy 之前,你需要将其安装到你的 Python 环境中。NumPy 的安装过程非常简单,通常使用 Python 的包管理器 pipconda

使用 pip 安装

如果你使用的是标准的 Python 发行版,pip 是最常用的安装方式:

bash
pip install numpy

使用 Conda 安装

如果你使用的是 Anaconda 或 Miniconda 发行版,你可以使用 conda 进行安装。conda 是一个强大的包、依赖和环境管理工具,特别适合数据科学项目:

bash
conda install numpy

无论你选择哪种安装方式,一旦安装完成,你就可以在 Python 脚本或交互式环境中导入 NumPy 并开始使用了。

导入 NumPy

在 Python 代码中,通常会使用一个约定俗成的别名 np 来导入 NumPy。这样做可以使得代码更加简洁易读:

“`python
import numpy as np

现在你可以使用 np 前缀来调用 NumPy 的所有函数和对象

例如:

arr = np.array([1, 2, 3])
print(arr)
“`

通过这简单的两步——安装和导入,你就已经准备好踏上 NumPy 的学习之旅了。

数组的创建

NumPy 提供了多种灵活的方法来创建 ndarray。了解这些创建方法是掌握 NumPy 的关键一步。

1. 从 Python 列表或元组创建

最常见的创建方式是将 Python 的列表或元组转换为 NumPy 数组。

“`python
import numpy as np

从列表创建一维数组

list_data = [1, 2, 3, 4, 5]
arr_1d = np.array(list_data)
print(“从列表创建的一维数组:\n”, arr_1d)

从嵌套列表创建二维数组 (矩阵)

nested_list_data = [[1, 2, 3], [4, 5, 6]]
arr_2d = np.array(nested_list_data)
print(“\n从嵌套列表创建的二维数组:\n”, arr_2d)

从元组创建

tuple_data = (10, 20, 30)
arr_from_tuple = np.array(tuple_data)
print(“\n从元组创建的数组:\n”, arr_from_tuple)
“`

你也可以在创建时指定数据类型:

python
arr_float = np.array([1, 2, 3], dtype=float)
print("\n指定数据类型的数组:\n", arr_float)

2. 创建全零、全一或指定值的数组

NumPy 提供了便捷函数来创建填充特定值的数组,这在初始化数组时非常有用。

  • np.zeros(): 创建一个所有元素都为 0 的数组。
    “`python
    zeros_1d = np.zeros(5) # 一维数组,5个元素
    print(“\n全零一维数组:\n”, zeros_1d)

    zeros_2d = np.zeros((3, 4)) # 二维数组,3行4列
    print(“\n全零二维数组:\n”, zeros_2d)
    “`

  • np.ones(): 创建一个所有元素都为 1 的数组。
    python
    ones_2d = np.ones((2, 3))
    print("\n全一二维数组:\n", ones_2d)

  • np.full(): 创建一个所有元素都为指定值的数组。
    python
    full_array = np.full((2, 2), 7) # 2x2 数组,所有元素为 7
    print("\n填充指定值的数组:\n", full_array)

3. 创建等差数列

  • np.arange(): 类似于 Python 内置的 range() 函数,可以创建指定间隔的等差数列。
    python
    arange_arr = np.arange(0, 10, 2) # 从 0 开始,到 10 (不包含),步长为 2
    print("\n使用 arange 创建等差数列:\n", arange_arr) # [0 2 4 6 8]

  • np.linspace(): 在指定区间内均匀地生成指定数量的样本。
    python
    linspace_arr = np.linspace(0, 1, 5) # 从 0 到 1 (包含),生成 5 个点
    print("\n使用 linspace 创建等间隔数组:\n", linspace_arr) # [0. 0.25 0.5 0.75 1. ]

4. 创建单位矩阵和随机数组

  • np.eye(): 创建一个单位矩阵(对角线为 1,其余为 0)。
    python
    identity_matrix = np.eye(3) # 3x3 单位矩阵
    print("\n单位矩阵:\n", identity_matrix)

  • np.random 模块: 用于生成各种随机数的数组。这是数据科学和机器学习中常用功能。
    “`python
    random_integers = np.random.randint(0, 10, size=(2, 3)) # 0到9之间的随机整数,2×3 矩阵
    print(“\n随机整数数组:\n”, random_integers)

    random_floats = np.random.rand(2, 2) # 0到1之间的随机浮点数,2×2 矩阵
    print(“\n随机浮点数数组:\n”, random_floats)
    “`

掌握了这些数组创建方法,你就拥有了构建任何 NumPy 数据结构的基础。

数组的索引与切片

NumPy 数组的索引和切片功能与 Python 列表类似,但更加强大和灵活,尤其是在处理多维数组时。

1. 基本索引

可以通过使用方括号 [] 和索引值来访问数组中的单个元素。索引从 0 开始。

一维数组索引

“`python
import numpy as np

arr = np.array([10, 20, 30, 40, 50])
print(“原数组:”, arr)

访问第一个元素

print(“第一个元素:”, arr[0]) # 输出: 10

访问最后一个元素 (负数索引)

print(“最后一个元素:”, arr[-1]) # 输出: 50
“`

多维数组索引

对于多维数组,可以使用逗号分隔的索引来访问元素,顺序为 [行索引, 列索引, ...]

“`python
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(“\n原二维数组:\n”, arr_2d)

访问第二行第一列的元素 (索引 1, 0)

print(“arr_2d[1, 0]:”, arr_2d[1, 0]) # 输出: 4

访问第三行第三列的元素 (索引 2, 2)

print(“arr_2d[2, 2]:”, arr_2d[2, 2]) # 输出: 9
“`

2. 切片 (Slicing)

切片允许你从数组中提取子数组。语法是 [start:end:step],与 Python 列表的切片规则相同:start 是包含的,end 是不包含的。

一维数组切片

“`python
print(“\n原数组:”, arr) # arr = [10, 20, 30, 40, 50]

提取前三个元素

print(“arr[0:3]:”, arr[0:3]) # 输出: [10 20 30]

提取从第二个元素到末尾的元素

print(“arr[1:]:”, arr[1:]) # 输出: [20 30 40 50]

提取所有元素,步长为 2

print(“arr[::2]:”, arr[::2]) # 输出: [10 30 50]
“`

多维数组切片

多维数组的切片可以分别应用于每个维度。

“`python
print(“\n原二维数组:\n”, arr_2d)

arr_2d = [[1, 2, 3],

[4, 5, 6],

[7, 8, 9]]

提取前两行

print(“arr_2d[:2]:\n”, arr_2d[:2])

输出:

[[1 2 3]

[4 5 6]]

提取所有行的第二列

print(“arr_2d[:, 1]:”, arr_2d[:, 1]) # 输出: [2 5 8]

提取第一行和第二行的前两列

print(“arr_2d[:2, :2]:\n”, arr_2d[:2, :2])

输出:

[[1 2]

[4 5]]

混合使用索引和切片

print(“arr_2d[1, 1:]:”, arr_2d[1, 1:]) # 访问第二行,从第二个元素到末尾

输出: [5 6]

“`

3. 高级索引 (Fancy Indexing)

高级索引允许你使用整数数组或布尔数组作为索引来选择非连续的或任意形状的子集。

整数数组索引

使用一个整数数组来指定要选择的行或列的索引。

“`python

假设我们有一个数组

arr_fancy = np.array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[9, 10, 11]])
print(“\n原高级索引数组:\n”, arr_fancy)

选择第 0, 2, 3 行

rows = np.array([0, 2, 3])
print(“选择特定行:\n”, arr_fancy[rows])

输出:

[[ 0 1 2]

[ 6 7 8]

[ 9 10 11]]

选择第 0, 2 行的第 0, 1 列

print(“选择特定行和列:\n”, arr_fancy[[0, 2], [0, 1]]) # (0,0) 和 (2,1) 位置的元素

输出: [0 7]

“`

布尔数组索引

使用一个布尔数组作为索引,该布尔数组与原始数组的形状相同或可广播。只有布尔数组中对应位置为 True 的元素才会被选择。

“`python
arr_bool = np.array([10, 20, 30, 40, 50])
print(“\n布尔索引原数组:”, arr_bool)

选择所有大于 25 的元素

bool_mask = (arr_bool > 25)
print(“布尔掩码:”, bool_mask) # 输出: [False False True True True]
print(“大于 25 的元素:”, arr_bool[bool_mask]) # 输出: [30 40 50]

也可以直接在索引中使用条件

print(“直接使用条件选择元素:”, arr_bool[arr_bool % 20 == 0]) # 输出: [20 40]
“`

高级索引是 NumPy 中非常强大的功能,它能让你以非常简洁高效的方式处理复杂的数据选择任务。

数组操作

NumPy 提供了强大的功能来改变数组的形状、组合多个数组或将数组拆分为多个部分,以及添加或删除元素。

1. 改变数组形状 (Reshaping)

改变数组的形状是 NumPy 中一项非常常见的操作,它允许你在不改变数据的情况下重新组织数组的维度。

  • reshape(): 将数组重塑为新的形状。新形状必须与原数组的元素总数兼容。
    “`python
    import numpy as np

    arr = np.arange(12) # 创建一个从 0 到 11 的一维数组
    print(“原始数组:\n”, arr)

    重塑为 3 行 4 列的二维数组

    reshaped_arr = arr.reshape(3, 4)
    print(“\n重塑为 3×4 数组:\n”, reshaped_arr)

    输出:

    [[ 0 1 2 3]

    [ 4 5 6 7]

    [ 8 9 10 11]]

    使用 -1 让 NumPy 自动计算维度大小

    reshaped_arr_auto = arr.reshape(2, -1) # 2 行,列数自动计算
    print(“\n重塑为 2 行,自动计算列数:\n”, reshaped_arr_auto)

    输出:

    [[ 0 1 2 3 4 5]

    [ 6 7 8 9 10 11]]

    “`

  • ravel()flatten(): 将多维数组展平为一维数组。

    • ravel() 返回一个视图(如果可能),这意味着对返回数组的修改可能会影响原始数组。
    • flatten() 始终返回一个副本,修改不会影响原始数组。
      “`python
      print(“\n原始重塑数组:\n”, reshaped_arr)

    raveled_arr = reshaped_arr.ravel()
    print(“ravel() 展平:\n”, raveled_arr)

    flattened_arr = reshaped_arr.flatten()
    print(“flatten() 展平:\n”, flattened_arr)
    “`

  • transpose().T: 交换数组的轴,对于二维数组,这表示行和列互换(矩阵转置)。
    “`python
    transposed_arr = reshaped_arr.transpose()
    print(“\n转置数组 (transpose):\n”, transposed_arr)

    transposed_arr_T = reshaped_arr.T
    print(“转置数组 (.T):\n”, transposed_arr_T)
    “`

2. 连接数组 (Joining)

NumPy 提供了多种函数来将多个数组沿不同的轴连接起来。

  • np.concatenate(): 沿现有轴连接一系列数组。
    “`python
    arr1 = np.array([[1, 2], [3, 4]])
    arr2 = np.array([[5, 6], [7, 8]])

    沿垂直方向 (axis=0) 连接

    concat_v = np.concatenate((arr1, arr2), axis=0)
    print(“\n垂直连接 (axis=0):\n”, concat_v)

    输出:

    [[1 2]

    [3 4]

    [5 6]

    [7 8]]

    沿水平方向 (axis=1) 连接

    concat_h = np.concatenate((arr1, arr2), axis=1)
    print(“水平连接 (axis=1):\n”, concat_h)

    输出:

    [[1 2 5 6]

    [3 4 7 8]]

    “`

  • np.stack(): 沿一个新轴连接一系列数组。这意味着连接后的数组会增加一个维度。
    python
    stacked_arr = np.stack((arr1, arr2), axis=0) # 在新轴 0 上堆叠
    print("\n堆叠数组 (axis=0):\n", stacked_arr)
    # 输出:
    # [[[1 2]
    # [3 4]]
    #
    # [[5 6]
    # [7 8]]]
    print("堆叠数组形状:", stacked_arr.shape) # (2, 2, 2)

  • np.vstack(): 垂直堆叠(按行堆叠)数组,等同于 np.concatenate(..., axis=0)

  • np.hstack(): 水平堆叠(按列堆叠)数组,等同于 np.concatenate(..., axis=1)
  • np.dstack(): 深度堆叠数组(沿第三个轴)。

3. 分割数组 (Splitting)

与连接相反,NumPy 也提供了将数组分割成多个子数组的函数。

  • np.split(): 沿指定轴将数组分割成多个子数组。
    “`python
    split_arr = np.arange(16).reshape(4, 4)
    print(“\n原始分割数组:\n”, split_arr)

    沿水平方向 (axis=1) 分割成两部分

    split_h = np.split(split_arr, 2, axis=1)
    print(“\n水平分割成两部分:\n”, split_h[0], “\n”, split_h[1])

    输出:

    [[ 0 1] [[ 2 3]

    [ 4 5] [ 6 7]

    [ 8 9] [10 11]

    [12 13]] [14 15]]

    沿垂直方向 (axis=0) 分割成四部分

    split_v = np.split(split_arr, 4, axis=0)
    print(“\n垂直分割成四部分:\n”, split_v[0], “\n”, split_v[1]) # 以此类推
    “`

  • np.hsplit(): 水平分割数组(按列分割)。

  • np.vsplit(): 垂直分割数组(按行分割)。

4. 添加和删除元素

虽然 NumPy 数组的大小是固定的,但可以通过创建新数组的方式实现添加或删除元素的效果。

  • np.append(): 将值追加到数组的末尾,并返回一个新数组。
    python
    arr_to_append = np.array([1, 2, 3])
    appended_arr = np.append(arr_to_append, [4, 5])
    print("\n追加元素后的数组:\n", appended_arr)

  • np.insert(): 在指定索引之前插入值,并返回一个新数组。
    python
    arr_to_insert = np.array([1, 2, 3])
    inserted_arr = np.insert(arr_to_insert, 1, 99) # 在索引 1 之前插入 99
    print("\n插入元素后的数组:\n", inserted_arr)

  • np.delete(): 删除指定索引或切片处的元素,并返回一个新数组。
    python
    arr_to_delete = np.array([1, 2, 3, 4, 5])
    deleted_arr = np.delete(arr_to_delete, [1, 3]) # 删除索引 1 和 3 处的元素
    print("\n删除元素后的数组:\n", deleted_arr)

理解这些数组操作函数将大大提升你在处理复杂数据集时的效率和灵活性。

数学运算

NumPy 最强大的功能之一是它能够对数组执行各种数学运算,而且这些操作通常都经过高度优化,运行效率极高。

1. 基本算术运算

NumPy 数组支持直接的元素级算术运算。当对两个形状兼容的数组执行算术运算时,结果数组的每个元素都将是对应位置元素的运算结果。

“`python
import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

加法

print(“加法:”, arr1 + arr2) # 输出: [5 7 9]

减法

print(“减法:”, arr1 – arr2) # 输出: [-3 -3 -3]

乘法 (元素级乘法)

print(“乘法:”, arr1 * arr2) # 输出: [ 4 10 18]

除法

print(“除法:”, arr1 / arr2) # 输出: [0.25 0.4 0.5 ]

幂运算

print(“幂运算:”, arr1 ** 2) # 输出: [1 4 9]

与标量进行运算

print(“与标量相加:”, arr1 + 10) # 输出: [11 12 13]
“`

2. 逻辑运算

NumPy 也支持元素级的逻辑运算,返回一个布尔数组。

“`python
arr_logic = np.array([True, False, True])
arr_logic2 = np.array([False, True, True])

print(“逻辑与 (AND):”, np.logical_and(arr_logic, arr_logic2)) # 输出: [False False True]
print(“逻辑或 (OR):”, np.logical_or(arr_logic, arr_logic2)) # 输出: [ True True True]
print(“逻辑非 (NOT):”, np.logical_not(arr_logic)) # 输出: [False True False]
“`

3. 广播 (Broadcasting)

广播是 NumPy 的一个强大功能,它允许 NumPy 在执行算术运算时处理形状不同的数组。在某些条件下,较小的数组会被“广播”到较大数组的形状,以便进行元素级操作。

规则:
1. 如果两个数组的维度数不相同,那么小维度数的数组的形状会在其前面补 1,直到维度数相同。
2. 如果两个数组的维度在任何一个轴上大小不同,且其中一个为 1,那么该维度会扩展到另一个数组的大小。
3. 如果两个数组的维度在任何一个轴上大小不同,且都没有 1,则会引发错误。

“`python
A = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
B = np.array([10, 20, 30]) # 形状 (3,)

print(“\n数组 A:\n”, A)
print(“数组 B:”, B)

B 会被广播成 [[10, 20, 30], [10, 20, 30]] 进行加法

print(“广播示例 (A + B):\n”, A + B)

输出:

[[11 22 33]

[14 25 36]]

C = np.array([[10], [20]]) # 形状 (2, 1)
print(“\n数组 C:\n”, C)

C 会被广播成 [[10, 10, 10], [20, 20, 20]]

B 会被广播成 [[10, 20, 30], [10, 20, 30]]

然后两者相加

print(“更复杂的广播示例 (C + B):\n”, C + B)

输出:

[[20 30 40]

[30 40 50]]

“`

4. 聚合函数 (Aggregation Functions)

聚合函数对数组进行操作并返回一个单一的结果,如求和、平均值、最大值等。

“`python
arr_agg = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

print(“\n原始聚合数组:\n”, arr_agg)

求和

print(“所有元素的和:”, np.sum(arr_agg)) # 输出: 45

按列求和 (axis=0)

print(“按列求和:”, np.sum(arr_agg, axis=0)) # 输出: [12 15 18]

按行求和 (axis=1)

print(“按行求和:”, np.sum(arr_agg, axis=1)) # 输出: [ 6 15 24]

平均值

print(“平均值:”, np.mean(arr_agg)) # 输出: 5.0

最大值

print(“最大值:”, np.max(arr_agg)) # 输出: 9
print(“每列的最大值:”, np.max(arr_agg, axis=0)) # 输出: [7 8 9]

最小值

print(“最小值:”, np.min(arr_agg)) # 输出: 1

标准差

print(“标准差:”, np.std(arr_agg)) # 输出: 2.581989…
“`

5. 线性代数

NumPy 的 linalg 模块提供了执行高级线性代数运算的功能。

“`python
A_matrix = np.array([[1, 2], [3, 4]])
B_matrix = np.array([[5, 6], [7, 8]])

print(“\n矩阵 A:\n”, A_matrix)
print(“矩阵 B:\n”, B_matrix)

矩阵乘法

matrix_product = np.dot(A_matrix, B_matrix)
print(“矩阵乘法 (np.dot):\n”, matrix_product)

或者使用 @ 运算符 (Python 3.5+)

matrix_product_at = A_matrix @ B_matrix
print(“矩阵乘法 (@ 运算符):\n”, matrix_product_at)

输出:

[[19 22]

[43 50]]

矩阵的行列式

determinant_A = np.linalg.det(A_matrix)
print(“矩阵 A 的行列式:”, determinant_A) # 输出: -2.0

矩阵的逆

inverse_A = np.linalg.inv(A_matrix)
print(“矩阵 A 的逆:\n”, inverse_A)

输出:

[[-2. 1. ]

[ 1.5 -0.5]]

“`

NumPy 提供的这些数学运算功能是数据分析、机器学习和科学计算的基石,能够让你高效地处理复杂的数值问题。

效率提示

为了充分发挥 NumPy 的性能优势,了解一些效率优化的技巧是很有帮助的。

1. 矢量化 (Vectorization)

这是 NumPy 效率提升的核心。 尽可能避免使用 Python 循环来遍历数组元素,而是使用 NumPy 提供的矢量化操作。NumPy 的许多函数(如 np.sum(), np.mean(), np.dot() 以及基本的算术运算符)都是在底层用 C 实现的,因此比 Python 循环快得多。

“`python
import numpy as np
import time

arr_size = 10**7
arr = np.arange(arr_size)

使用 NumPy 矢量化操作

start_time = time.time()
np_sum = np.sum(arr)
end_time = time.time()
print(f”NumPy sum 耗时: {end_time – start_time:.4f} 秒”)

使用 Python 循环 (效率低下)

start_time = time.time()
py_sum = sum(arr) # 注意:这里 sum() 函数本身是优化的,但如果手动编写循环会更慢
end_time = time.time()
print(f”Python sum 耗时: {end_time – start_time:.4f} 秒”)

更明显的 Python 循环示例 (非常慢,不建议运行在大型数组上)

total = 0

start_time = time.time()

for x in arr:

total += x

end_time = time.time()

print(f”手动 Python 循环耗时: {end_time – start_time:.4f} 秒”)

“`
通过矢量化,可以将复杂的操作转换为对整个数组进行的高效函数调用。

2. 选择合适的数据类型 (Data Types)

NumPy 数组是同构的,所有元素都具有相同的数据类型。选择最适合你数据的数据类型可以显著节省内存并可能提高计算速度。例如,如果你知道所有数据都是小整数,使用 np.int8np.int16 比默认的 np.int64 更节省内存。

“`python
arr_int64 = np.array([1, 2, 3], dtype=np.int64)
arr_int8 = np.array([1, 2, 3], dtype=np.int8)

print(f”int64 数组占用内存: {arr_int64.nbytes} 字节”)
print(f”int8 数组占用内存: {arr_int8.nbytes} 字节”)
“`
在处理大型数据集时,这一点尤为重要。

3. 就地操作 (In-Place Operations)

在可能的情况下,尽量使用就地操作(例如 arr += 1 而不是 arr = arr + 1)。就地操作会修改原始数组,而不是创建新的数组,这可以减少内存分配和释放的开销,从而提高效率。

“`python
arr = np.arange(5)
print(“原始数组:”, arr)

非就地操作 (创建新数组)

new_arr = arr + 1
print(“非就地操作后的新数组:”, new_arr)
print(“原始数组未变:”, arr) # 原始数组未变

就地操作 (修改原始数组)

arr += 1
print(“就地操作后的原始数组:”, arr) # 原始数组被修改
“`

4. 避免隐式复制 (Avoid Implicit Copying)

当对 NumPy 数组进行切片时,通常会得到原始数组的视图 (view),而不是副本 (copy)。对视图的修改会影响原始数组。如果你确实需要一个独立的副本,请显式使用 .copy() 方法。

“`python
original_arr = np.arange(5)
print(“原始数组:”, original_arr)

切片创建视图

view_arr = original_arr[1:4]
print(“视图数组:”, view_arr)

view_arr[0] = 99 # 修改视图
print(“修改视图后原始数组:”, original_arr) # 原始数组受到影响

创建副本

copy_arr = original_arr[1:4].copy()
print(“\n副本数组:”, copy_arr)

copy_arr[0] = 100 # 修改副本
print(“修改副本后原始数组:”, original_arr) # 原始数组未受影响
“`
理解视图和副本的区别可以帮助你避免不必要的内存使用和潜在的错误。

通过遵循这些效率提示,你可以编写出更快速、更内存友好的 NumPy 代码,从而更有效地处理你的数据。

应用领域

NumPy 作为 Python 科学计算生态系统的基石,其应用领域非常广泛,几乎涵盖了所有涉及数值数据处理的科学和工程领域。

1. 数据分析与处理

  • Pandas 库的基础:Pandas 是 Python 中最流行的数据分析库,它的 DataFrame 对象实际上是建立在 NumPy 数组之上的。NumPy 提供的高效数组操作使得 Pandas 能够处理和分析大规模表格数据。
  • 数据清洗与预处理:在数据分析流程中,数据清洗和预处理(如缺失值处理、数据转换、特征缩放等)通常需要对数值数据进行高效操作,NumPy 在此扮演了关键角色。
  • 统计分析:NumPy 提供了丰富的统计函数(如均值、中位数、标准差、方差等),可以直接应用于数据集进行快速统计分析。

2. 机器学习与深度学习

  • 数据表示:在机器学习中,数据通常以矩阵或张量(多维数组)的形式表示。NumPy 的 ndarray 是表示这些数据的标准格式。
  • 算法实现:许多机器学习算法(如线性回归、逻辑回归、K-Means、主成分分析等)的核心是矩阵运算和数值优化,这些操作都可以通过 NumPy 高效实现。
  • 深度学习框架:TensorFlow、PyTorch 等主流深度学习框架在底层都广泛使用了 NumPy 或与 NumPy 兼容的数组结构,进行张量操作和梯度计算。

3. 图像处理

  • 图像表示:数字图像可以被看作是像素值的二维或三维数组(例如,彩色图像通常是高度 x 宽度 x RGB 通道的数组)。NumPy 数组非常适合存储和操作这些像素数据。
  • 图像操作:NumPy 使得对图像进行各种操作变得简单高效,如图像的裁剪、旋转、缩放、颜色通道分离、滤镜应用等。

4. 科学计算与工程

  • 物理学与工程模拟:在物理、化学、工程学等领域,NumPy 用于解决复杂的数学方程、进行模拟、处理实验数据。例如,有限元分析、信号处理等。
  • 金融建模:在金融领域,NumPy 被用于构建复杂的金融模型、进行风险评估、计算期权价格等,其高效的数值计算能力是关键。
  • 地球科学:处理地理空间数据、气象数据等通常涉及大型多维数组,NumPy 在这些领域提供了强大的工具。

5. 数据可视化

  • Matplotlib 与 Seaborn 的数据源:Matplotlib 和 Seaborn 是 Python 中最常用的数据可视化库,它们接受 NumPy 数组作为输入,并基于这些数据生成各种图表和图形。NumPy 数组的高效组织使得这些库能够快速有效地渲染数据。

总而言之,NumPy 不仅仅是一个独立的库,它更是整个 Python 科学计算栈的支柱,为众多高级库和应用提供了高性能的数值运算基础。掌握 NumPy,意味着打开了通往这些强大工具的大门。

进一步学习资源

学习 NumPy 是一个持续的过程。以下是一些推荐的资源,可以帮助你进一步深化理解和掌握 NumPy:

  1. 官方 NumPy 文档

    • NumPy 官网:最权威、最全面的资料来源。包含用户指南、API 参考、教程和常见问题解答。这是深入理解 NumPy 任何特定功能的首选。
  2. 在线教程与课程

    • DataCamp, Coursera, Udacity 等平台:提供了大量的交互式课程,从入门到高级都有涵盖,通常包含练习和项目。
    • Real Python 网站:提供了许多高质量的 NumPy 教程和文章,内容深入浅出,适合初学者和有经验的开发者。
    • Tutorials Point NumPy 教程:一个简洁的在线教程,可以快速了解 NumPy 的核心概念和功能。
    • NumPy: The Absolute Basics for Beginners:一篇适合完全初学者的指南,帮助你快速上手。
  3. 书籍

    • 《Python 数据科学手册》(Python Data Science Handbook by Jake VanderPlas):这本书的 NumPy 章节是极佳的学习资源,它不仅详细解释了 NumPy,还展示了其在数据科学中的应用。
    • 《利用 Python 进行数据分析》(Python for Data Analysis by Wes McKinney):Pandas 库的作者所著,书中对 NumPy 的介绍同样非常实用。
  4. 交互式学习环境

    • Jupyter Notebook / JupyterLab:这些工具提供了交互式编程环境,非常适合边学边练,即时查看代码运行结果。
    • Google Colab:一个免费的基于云的 Jupyter Notebook 环境,无需本地安装即可运行 Python 代码,并可以轻松共享。
  5. 实践项目

    • 最好的学习方式是实践。尝试在 Kaggle 等平台上寻找一些数据科学项目,或自己构思一些小项目,利用 NumPy 解决实际问题。
    • 参加一些编程挑战或黑客马拉松,将所学知识付诸实践。

通过利用这些资源并坚持不懈地练习,你将能够熟练地运用 NumPy,在数据科学和科学计算领域取得更大的成就。

滚动至顶部