为什么使用 NumPy
Python 列表是出色的通用容器。它们可以是“异构”的,这意味着它们可以包含各种类型的元素。当用于对少量元素执行单个操作时,它们的速度非常快。
根据数据的特征和需要执行的操作类型,其他容器可能更合适。通过利用这些特性,我们可以提高速度,减少内存消耗,并提供用于执行各种常见处理任务的高级语法。当存在大量“同质”(相同类型)数据时,NumPy 会在 CPU 上处理时大放异彩。
你可以使用 Markdown 来表示这些数组而不依赖于 LaTeX。以下是使用 Markdown 表示二维和三维数组的方式:
什么是数组(ndarray)
在计算机中,数组是一种用于存储和检索数据的结构。我们经常将数组视为网络,每个消息都是数据元素。例如列表:

二维数组类似一个表:

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

大多数 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)。张量在多维数据处理中非常常见,特别是在机器学习和深度学习中。
数组的属性
-
ndim
描述:表示数组的维度数,也就是数组有多少层嵌套。对一维数组ndim
返回 1,二维数组返回 2,以此类推。
示例:a = np.array([[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]]) print(a.ndim) # 输出: 3
-
shape
描述:返回一个包含每个维度的元素个数的元组,从外层到内层依次列出。它表示数组的形状,每个维度有多少元素。
示例:print(a.shape) # 输出: (2, 2, 3)
-
size
描述:返回数组中元素的总数,即数组所有维度中元素个数的乘积。
示例:print(a.size) # 输出: 12
-
dtype
描述:返回数组中元素的数据类型。NumPy 数组中的所有元素必须是相同的数据类型,dtype
属性记录了这种类型。
示例:print(a.dtype) # 输出: dtype('int64')
如何创建基本数组
NumPy 提供了多种简便的方式来创建特定类型的数组:
-
创建一个全为 0 的数组
np.zeros(2) # array([0., 0.])
-
创建一个全为 1 的数组
np.ones(2) # array([1., 1.])
-
创建一个空数组
np.empty(2) # 输出: array([1., 1.]) # 内容可能会有所不同
-
创建一个包含一系列元素的数组
np.arange(4) # array([0, 1, 2, 3])
-
创建一个带有步长的数组
np.arange(2, 9, 2) # array([2, 4, 6, 8])
-
创建一个线性间隔的数组
np.linspace(0, 10, num=5) # array([ 0. , 2.5, 5. , 7.5, 10. ])
-
指定数据类型
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.]])
数组排序与连接
-
排序数组
arr = np.array([2, 1, 5, 3, 7, 4, 6, 8]) np.sort(arr) # array([1, 2, 3, 4, 5, 6, 7, 8])
-
连接数组
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.newaxis
和 np.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.newaxis
和np.expand_dims
都可以用来增加数组的维度。np.newaxis
通过索引添加新维度,而np.expand_dims
允许在指定的维度插入新轴。