菜单
本页目录

为什么使用 NumPy

Python 列表是出色的通用容器。它们可以是“异构”的,这意味着它们可以包含各种类型的元素。当用于对少量元素执行单个操作时,它们的速度非常快。

根据数据的特征和需要执行的操作类型,其他容器可能更合适。通过利用这些特性,我们可以提高速度,减少内存消耗,并提供用于执行各种常见处理任务的高级语法。当存在大量“同质”(相同类型)数据时,NumPy 会在 CPU 上处理时大放异彩。


你可以使用 Markdown 来表示这些数组而不依赖于 LaTeX。以下是使用 Markdown 表示二维和三维数组的方式:

什么是数组(ndarray)

在计算机中,数组是一种用于存储和检索数据的结构。我们经常将数组视为网络,每个消息都是数据元素。例如列表:

20240903220327.png

二维数组类似一个表:

20240903220331.png

三维数组则像二维数组沿一个方向堆叠在一起的样子:

20240903220508.png

大多数 NumPy 数组都有一些限制,例如:

  • 数组的所有元素必须属于同一类型的数据。
  • 创建后,数组的总大小无法更改。
  • 形状必须是“矩形”的,而不是“锯齿状”的;例如,二维数组的每一行必须具有相同的列数。

当满足这些条件时,NumPy 可以利用这些特性,使数组更快、更节省内存、更方便使用,并减少限制较多的数据结构的需求。


数组基础知识

可以通过 array() 来初始化数组(ndarray):

import numpy as np  
a = np.array([1, 2, 3, 4, 5, 6])
a
# [1, 2, 3, 4, 5, 6]

可以通过多种方式访问数组的元素。例如,我们可以像访问普通列表那样,使用方括号内元素的整数索引来访问单个元素:

a[0]
# 1

这种访问方式还包括切片访问:

a[:3]
# [1, 2, 3]

与原始列表一样,NumPy 数组是可变的:

a[0] = 10
a
# [10, 2, 3, 4, 5, 6]

在 Python 自带的列表容器中,切片操作获取到的是一个新列表,它在物理内存中与原列表不同。然而,NumPy 数组的切片操作获取到的是原数组的视图(view),这类似于 SQL 中的视图查询,区别在于 NumPy 的视图可以修改原数据,而 SQL 的视图不能。

示例:

a = np.array([1, 2, 3, 4, 5, 6])
b = a[:3]
b[0] = 100
a
# [100, 2, 3, 4, 5, 6]

相反,在 Python 的列表中,切片操作不会影响原列表:

a = [1, 2, 3, 4, 5, 6]
b = a[:3]
b[0] = 100
a
# [1, 2, 3, 4, 5, 6]

矩阵元素的引用通常是按照行索引(row index)和列索引(column index)的顺序进行的。这种做法在二维数组中是直观且常用的。但当处理多维数组(不仅仅是二维)时,需要一种更通用的方式来理解和操作这些数据。


标量 (Scalar):在编程或科学计算中,标量是没有维度的数值,即零维数组 (0-D array)。这是最简单的类型,代表单个值。

向量 (Vector):一维数组 (1-D array) 通常被称为向量。它可以看作是一系列按顺序排列的数字。

矩阵 (Matrix):二维数组 (2-D array) 通常称为矩阵。这类似于一个表格或网格,包含行和列。

张量 (Tensor):当数组的维度大于二维(例如 3-D、4-D 或更高),通常称为张量 (Tensor)。张量在多维数据处理中非常常见,特别是在机器学习和深度学习中。


数组的属性

  1. ndim
    描述:表示数组的维度数,也就是数组有多少层嵌套。对一维数组 ndim 返回 1,二维数组返回 2,以此类推。
    示例

    a = np.array([[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]])
    print(a.ndim)
    # 输出: 3
    
  2. shape
    描述:返回一个包含每个维度的元素个数的元组,从外层到内层依次列出。它表示数组的形状,每个维度有多少元素。
    示例

    print(a.shape)
    # 输出: (2, 2, 3)
    
  3. size
    描述:返回数组中元素的总数,即数组所有维度中元素个数的乘积。
    示例

    print(a.size)
    # 输出: 12
    
  4. dtype
    描述:返回数组中元素的数据类型。NumPy 数组中的所有元素必须是相同的数据类型,dtype 属性记录了这种类型。
    示例

    print(a.dtype)
    # 输出: dtype('int64')
    

如何创建基本数组

NumPy 提供了多种简便的方式来创建特定类型的数组:

  1. 创建一个全为 0 的数组

    np.zeros(2)
    # array([0., 0.])
    
  2. 创建一个全为 1 的数组

    np.ones(2)
    # array([1., 1.])
    
  3. 创建一个空数组

    np.empty(2)
    # 输出: array([1., 1.])  # 内容可能会有所不同
    
  4. 创建一个包含一系列元素的数组

    np.arange(4)
    # array([0, 1, 2, 3])
    
  5. 创建一个带有步长的数组

    np.arange(2, 9, 2)
    # array([2, 4, 6, 8])
    
  6. 创建一个线性间隔的数组

    np.linspace(0, 10, num=5)
    # array([ 0. ,  2.5,  5. ,  7.5, 10. ])
    
  7. 指定数据类型

    x = np.ones(2, dtype=np.int64)
    # array([1, 1])
    

自定义函数产生 ndarray

你可以使用 numpy.fromfunction 创建自定义数组。它接收一个计算函数以及数组的形状,生成一个符合条件的 ndarray

示例 1: 一维数组

def funcA(i):
    return (i % 4) + 1

arr1 = np.fromfunction(funcA, (10,))
print(arr1)
# array([1., 2., 3., 4., 1., 2., 3., 4., 1., 2.])

示例 2: 二维数组

def funcB(i, j):
    return i + j

arr2 = np.fromfunction(funcB, (2, 2))
print(arr2)
# array([[0., 1.], [1., 2.]])

数组排序与连接

  1. 排序数组

    arr = np.array([2, 1, 5, 3, 7, 4, 6, 8])
    np.sort(arr)
    # array([1, 2, 3, 4, 5, 6, 7, 8])
    
  2. 连接数组

    a = np.array([1, 2, 3, 4])
    b = np.array([5, 6, 7, 8])
    np.concatenate((a, b))
    # array([1, 2, 3, 4, 5, 6, 7, 8])
    

    对二维数组:

    x = np.array([[1, 2], [3, 4]])
    y = np.array([[5, 6]])
    np.concatenate((x, y), axis=0)
    # array([[1, 2], [3, 4], [5, 6]])
    

数组的形状重塑(reshape()

你可以使用 reshape() 在不改变数组数据的情况下赋予数组新的形状。需要注意的是,使用 reshape 时,新数组的元素数量必须与原始数组相同。

示例:

a = np.arange(6)
b = a.reshape(3, 2)
print(b)
# 输出:
# [[0 1]
#  [2 3]
#  [4 5]]

如何将 1D 数组转换为 2D 数组

你可以使用 np.newaxisnp.expand_dims 来增加现有数组的维度。

使用 np.newaxis

a = np.array([1, 2, 3, 4, 5, 6])
a2 = a[np.newaxis, :]
print(a2.shape)
# (1, 6)

将一维数组转换为列向量:

col_vector = a[:, np.newaxis]
print(col_vector.shape)
# (6, 1)

使用 np.expand_dims

b = np.expand_dims(a, axis=1)
print(b.shape)
# (6, 1)

总结

  • np.newaxisnp.expand_dims 都可以用来增加数组的维度。
  • np.newaxis 通过索引添加新维度,而 np.expand_dims 允许在指定的维度插入新轴。