Python使用NumPy处理多维数组
1. NumPy 基础:多维数组概述
1.1 什么是多维数组
在 Python 中,NumPy 库引入了 ndarray
(N 维数组)对象,它是一个高效存储和处理多维数据的结构。多维数组本质上是一个由相同数据类型元素组成的网格,可以是一维、二维甚至更高维度。例如,一维数组类似于列表,只不过它要求所有元素类型一致;二维数组就像常见的表格形式,有行和列;更高维度数组则在此基础上进一步拓展维度概念。
1.2 多维数组的优势
与 Python 内置的列表相比,NumPy 的多维数组在性能和功能上有显著优势。性能方面,NumPy 数组是在 C 语言层面实现的,其存储和操作数据更加高效,尤其是对于大规模数据的计算。例如,进行简单的数组元素加法运算,NumPy 数组的速度比普通列表快很多倍。功能上,NumPy 提供了丰富的函数和方法来对多维数组进行各种操作,如数学运算、统计分析、索引和切片等,这些操作可以直接应用到整个数组,无需像列表那样使用循环逐个处理元素。
2. 创建 NumPy 多维数组
2.1 使用 numpy.array()
创建
最常见的创建 NumPy 多维数组的方式是使用 numpy.array()
函数。该函数接受一个序列(如列表或元组)作为参数,并将其转换为 NumPy 数组。例如:
import numpy as np
# 创建一维数组
arr1d = np.array([1, 2, 3, 4])
print(arr1d)
# 创建二维数组
arr2d = np.array([[1, 2], [3, 4]])
print(arr2d)
在上述代码中,通过传递不同维度的列表,我们分别创建了一维和二维的 NumPy 数组。numpy.array()
函数会自动推断数组的数据类型,当然也可以通过 dtype
参数显式指定,例如:
arr = np.array([1, 2, 3], dtype='float64')
print(arr.dtype)
这里我们将数组的数据类型指定为 float64
。
2.2 使用专门的创建函数
除了 numpy.array()
,NumPy 还提供了一些专门用于创建特定类型数组的函数。
numpy.zeros()
:创建一个全零的数组。可以指定数组的形状,例如:
zeros_1d = np.zeros(5)
print(zeros_1d)
zeros_2d = np.zeros((3, 4))
print(zeros_2d)
这里分别创建了长度为 5 的一维全零数组和形状为 3 行 4 列的二维全零数组。
numpy.ones()
:与numpy.zeros()
类似,不过创建的是全一的数组。
ones_1d = np.ones(4)
print(ones_1d)
ones_2d = np.ones((2, 3))
print(ones_2d)
numpy.arange()
:类似于 Python 内置的range()
函数,但返回的是 NumPy 数组。它可以接受起始值、结束值(不包含)和步长作为参数。
arr_range = np.arange(1, 10, 2)
print(arr_range)
此代码创建了从 1 开始,到 10 结束(不包含 10),步长为 2 的一维数组。
3. 多维数组的数据类型
3.1 常见数据类型
NumPy 支持多种数据类型,常见的有:
- 整数类型:如
int8
(8 位有符号整数)、int16
(16 位有符号整数)、int32
(32 位有符号整数)、int64
(64 位有符号整数)。还有无符号整数类型,如uint8
、uint16
等。 - 浮点类型:
float16
、float32
、float64
等,其中float64
是默认的浮点类型。 - 布尔类型:
bool
,用于存储布尔值True
和False
。
不同的数据类型在内存占用和精度上有所不同。例如,float16
占用的内存比 float64
少,但精度也相对较低。
3.2 数据类型转换
可以使用 astype()
方法对 NumPy 数组的数据类型进行转换。例如:
arr = np.array([1, 2, 3])
float_arr = arr.astype('float64')
print(float_arr.dtype)
这里将整数类型的数组转换为了 float64
类型。需要注意的是,在进行类型转换时,如果目标类型无法准确表示原数据,可能会发生数据截断或精度损失。例如,将浮点数转换为整数时,小数部分会被截断。
4. 多维数组的基本操作
4.1 索引
4.1.1 一维数组索引
对于一维 NumPy 数组,索引方式与 Python 列表类似,从 0 开始计数。例如:
arr = np.array([10, 20, 30, 40, 50])
print(arr[0])
print(arr[3])
通过索引可以获取数组中指定位置的元素。
4.1.2 二维数组索引
二维数组需要两个索引来指定元素位置,第一个索引表示行,第二个索引表示列。例如:
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr_2d[1, 2])
这里 arr_2d[1, 2]
表示获取第 2 行(索引为 1)第 3 列(索引为 2)的元素。
4.1.3 高维数组索引
更高维度数组的索引方式类似,按照维度顺序依次指定每个维度的索引值。例如,对于三维数组 arr_3d
,arr_3d[i, j, k]
表示获取第 i
个三维块中的第 j
行第 k
列的元素。
4.2 切片
4.2.1 一维数组切片
一维数组的切片与列表切片类似,可以使用 start:stop:step
的语法。例如:
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
sub_arr = arr[2:7:2]
print(sub_arr)
这里从索引 2 开始(包含),到索引 7 结束(不包含),步长为 2 进行切片。
4.2.2 二维数组切片
二维数组切片时,对每个维度都可以进行切片操作。例如:
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
sub_arr_2d = arr_2d[1:3, 0:2]
print(sub_arr_2d)
这里对行进行从索引 1 到 3(不包含)的切片,对列进行从索引 0 到 2(不包含)的切片,得到一个 2 行 2 列的子数组。
4.2.3 高维数组切片
高维数组切片也是对每个维度进行相应的切片操作,通过合理组合不同维度的切片范围,可以获取到特定的子数组。
4.3 数学运算
NumPy 多维数组支持各种数学运算,这些运算会自动应用到数组的每个元素上。
- 基本算术运算:可以进行加(
+
)、减(-
)、乘(*
)、除(/
)等运算。例如:
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
add_result = arr1 + arr2
print(add_result)
这里将两个数组对应位置的元素相加。
- 三角函数运算:如
np.sin()
、np.cos()
、np.tan()
等。这些函数会对数组中的每个元素进行相应的三角函数计算。
arr = np.array([0, np.pi/2, np.pi])
sin_result = np.sin(arr)
print(sin_result)
- 指数和对数运算:
np.exp()
计算指数,np.log()
计算自然对数等。
arr = np.array([1, 2, 3])
exp_result = np.exp(arr)
log_result = np.log(arr)
print(exp_result)
print(log_result)
5. 多维数组的统计方法
5.1 求和
可以使用 sum()
方法对 NumPy 多维数组进行求和操作。对于一维数组,直接计算所有元素的和;对于多维数组,可以通过指定 axis
参数来沿着特定维度求和。例如:
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
total_sum = arr_2d.sum()
row_sum = arr_2d.sum(axis = 1)
col_sum = arr_2d.sum(axis = 0)
print(total_sum)
print(row_sum)
print(col_sum)
这里 total_sum
计算整个二维数组所有元素的和,row_sum
沿着列方向(axis = 1
)计算每行的和,col_sum
沿着行方向(axis = 0
)计算每列的和。
5.2 求均值
mean()
方法用于计算数组的均值。同样,对于多维数组可以通过 axis
参数指定计算方向。
arr = np.array([[1, 2, 3], [4, 5, 6]])
mean_all = arr.mean()
mean_row = arr.mean(axis = 1)
mean_col = arr.mean(axis = 0)
print(mean_all)
print(mean_row)
print(mean_col)
5.3 求最大值和最小值
max()
和 min()
方法分别用于求数组的最大值和最小值。多维数组同样可通过 axis
参数沿特定维度查找。
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
max_all = arr_2d.max()
max_row = arr_2d.max(axis = 1)
max_col = arr_2d.max(axis = 0)
print(max_all)
print(max_row)
print(max_col)
最小值操作类似,使用 min()
方法。
6. 多维数组的形状操作
6.1 改变数组形状
可以使用 reshape()
方法改变 NumPy 多维数组的形状。例如,将一个一维数组转换为二维数组:
arr = np.arange(1, 7)
reshaped_arr = arr.reshape((2, 3))
print(reshaped_arr)
这里将长度为 6 的一维数组转换为了 2 行 3 列的二维数组。需要注意的是,reshape()
方法要求新形状的元素总数与原数组相同。
6.2 数组的转置
对于二维数组,转置操作是将行和列进行交换,可以使用 transpose()
方法或 T
属性。例如:
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
transposed_arr = arr_2d.transpose()
print(transposed_arr)
# 或者使用 T 属性
transposed_arr_2 = arr_2d.T
print(transposed_arr_2)
这两种方式都实现了二维数组的转置。
6.3 拼接数组
6.3.1 水平拼接
使用 np.hstack()
函数可以水平拼接两个或多个数组。例如:
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
hstack_result = np.hstack((arr1, arr2))
print(hstack_result)
这里将两个二维数组水平拼接在一起,要求拼接的数组在垂直方向(行数)上维度一致。
6.3.2 垂直拼接
np.vstack()
函数用于垂直拼接数组。例如:
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
vstack_result = np.vstack((arr1, arr2))
print(vstack_result)
垂直拼接要求数组在水平方向(列数)上维度一致。
7. 广播机制
7.1 广播的概念
广播是 NumPy 中一个强大的特性,它允许在形状不同的数组之间进行算术运算。当进行运算时,如果两个数组的形状不完全相同,NumPy 会尝试通过广播机制调整数组形状,使运算能够顺利进行。广播机制的基本原则是从右到左比较两个数组的形状,如果维度不匹配,且其中一个数组的维度为 1,则将该维度扩展为另一个数组对应维度的大小。
7.2 广播示例
例如,一个形状为 (3, 1)
的数组与一个形状为 (1, 4)
的数组相加:
arr1 = np.array([[1], [2], [3]])
arr2 = np.array([[4, 5, 6, 7]])
result = arr1 + arr2
print(result)
这里 arr1
会在列方向上进行广播扩展,arr2
会在行方向上进行广播扩展,最终两个数组都扩展为形状 (3, 4)
后进行加法运算。
8. 存储和读取多维数组
8.1 使用 numpy.save()
和 numpy.load()
NumPy 提供了 numpy.save()
函数用于将数组保存到文件中,文件扩展名为 .npy
。例如:
arr = np.array([[1, 2, 3], [4, 5, 6]])
np.save('my_array.npy', arr)
要读取保存的数组,可以使用 numpy.load()
函数:
loaded_arr = np.load('my_array.npy')
print(loaded_arr)
8.2 使用文本文件存储
也可以将 NumPy 数组保存为文本文件,使用 numpy.savetxt()
函数。例如:
arr = np.array([[1, 2, 3], [4, 5, 6]])
np.savetxt('my_array.txt', arr, fmt='%d', delimiter=' ')
这里 fmt='%d'
表示以整数格式保存,delimiter=' '
表示使用空格作为分隔符。读取文本文件可以使用 numpy.loadtxt()
函数:
loaded_arr = np.loadtxt('my_array.txt', dtype='int')
print(loaded_arr)
通过以上内容,我们全面地了解了如何使用 NumPy 处理多维数组,从基础概念到各种操作和应用场景,NumPy 为 Python 在数据处理和科学计算领域提供了强大的支持。