第二章:NumPy 核心数据结构:ndarray (N-dimensional Array) 第二章:NumPy 核心数据结构:ndarray (N-dimensional Array) NumPy,作为 Python 中科学计算的核心库,其最重要的组成部分就是 (N 维数组)。理解 的概念、属性和操作是掌握 NumPy 的关键。本章将深入探讨 ,包括其定义、创建、属性、索引、切片、形状操作、广播机制以及一些常用的函数。 2.1 什么是 ndarray? 是 NumPy 提供的多维数组对象,它是一个同质数据的多维容器,这意味着 中的所有元素必须具有相同的数据类型。 可以表示向量、矩阵、张量等多种形式的数据,使其成为处理数值计算、图像处理、机器学习等领域数据的理想选择。
NumPy,作为 Python 中科学计算的核心库,其最重要的组成部分就是 ndarray(N 维数组)。理解 ndarray 的概念、属性和操作是掌握 NumPy 的关键。本章将深入探讨 ndarray,包括其定义、创建、属性、索引、切片、形状操作、广播机制以及一些常用的函数。
ndarray 是 NumPy 提供的多维数组对象,它是一个同质数据的多维容器,这意味着 ndarray 中的所有元素必须具有相同的数据类型。ndarray 可以表示向量、矩阵、张量等多种形式的数据,使其成为处理数值计算、图像处理、机器学习等领域数据的理想选择。
与 Python 列表的区别:
同质性: ndarray 只能存储相同类型的数据,而 Python 列表可以存储不同类型的数据。
性能: ndarray 在存储和计算方面进行了优化,因此在处理大量数值数据时,性能远优于 Python 列表。
功能: ndarray 提供了丰富的数学函数和操作,方便进行各种数值计算。
NumPy 提供了多种创建 ndarray 的方法:
2.2.1 从 Python 列表或元组创建:
import numpy as np # 从列表创建 list_data = [1, 2, 3, 4, 5] arr1 = np.array(list_data) print(arr1) # 输出: [1 2 3 4 5] print(arr1.dtype) # 输出: int64 (取决于系统) # 从元组创建 tuple_data = (6, 7, 8, 9, 10) arr2 = np.array(tuple_data) print(arr2) # 输出: [ 6 7 8 9 10] # 创建二维数组 matrix_data = [[1, 2, 3], [4, 5, 6]] arr3 = np.array(matrix_data) print(arr3) # 输出: # [[1 2 3] # [4 5 6]]
2.2.2 使用 NumPy 内置函数创建:
np.zeros(shape): 创建一个指定形状的数组,所有元素都为 0。
np.ones(shape): 创建一个指定形状的数组,所有元素都为 1。
np.empty(shape): 创建一个指定形状的数组,元素值未初始化(可能包含垃圾值)。
np.full(shape, fill_value): 创建一个指定形状的数组,所有元素都为 fill_value。
np.arange(start, stop, step): 创建一个从 start 到 stop (不包含 stop),步长为 step 的等差数组。
np.linspace(start, stop, num): 创建一个从 start 到 stop (包含 stop),均匀分割为 num 个元素的等差数组。
np.eye(N): 创建一个 N x N 的单位矩阵。
np.random.rand(shape): 创建一个指定形状的数组,元素值为 0 到 1 之间的随机数。
np.random.randn(shape): 创建一个指定形状的数组,元素值为服从标准正态分布的随机数。
np.random.randint(low, high, size): 创建一个指定形状的数组,元素值为 low (包含) 到 high (不包含) 之间的随机整数。
# 创建全 0 数组 zeros_arr = np.zeros((2, 3)) # 2行3列 print(zeros_arr) # 输出: # [[0. 0. 0.] # [0. 0. 0.]] # 创建全 1 数组 ones_arr = np.ones((3, 2)) print(ones_arr) # 输出: # [[1. 1.] # [1. 1.] # [1. 1.]] # 创建未初始化数组 empty_arr = np.empty((2, 2)) print(empty_arr) # 输出: (可能包含垃圾值) # [[6.9e-310 6.9e-310] # [6.9e-310 6.9e-310]] # 创建填充特定值的数组 full_arr = np.full((2, 2), 7) print(full_arr) # 输出: # [[7 7] # [7 7]] # 创建等差数组 arange_arr = np.arange(0, 10, 2) print(arange_arr) # 输出: [0 2 4 6 8] # 创建均匀分割的数组 linspace_arr = np.linspace(0, 1, 5) print(linspace_arr) # 输出: [0. 0.25 0.5 0.75 1. ] # 创建单位矩阵 eye_arr = np.eye(3) print(eye_arr) # 输出: # [[1. 0. 0.] # [0. 1. 0.] # [0. 0. 1.]] # 创建随机数组 rand_arr = np.random.rand(2, 2) print(rand_arr) # 输出: (随机值) # [[0.123 0.456] # [0.789 0.012]] randint_arr = np.random.randint(0, 10, (3,3)) print(randint_arr) # 输出: (随机整数) # [[5 8 2] # [9 1 7] # [3 0 4]]
2.2.3 指定数据类型:
在创建 ndarray 时,可以使用 dtype 参数指定数据类型。
arr_int = np.array([1, 2, 3], dtype=np.int32) print(arr_int.dtype) # 输出: int32 arr_float = np.array([1, 2, 3], dtype=np.float64) print(arr_float.dtype) # 输出: float64 arr_bool = np.array([0, 1, 0], dtype=np.bool_) print(arr_bool) # 输出: [False True False] print(arr_bool.dtype) # 输出: bool
常用的数据类型包括:
np.int8, np.int16, np.int32, np.int64: 有符号整数
np.uint8, np.uint16, np.uint32, np.uint64: 无符号整数
np.float16, np.float32, np.float64: 浮点数
np.complex64, np.complex128: 复数
np.bool_: 布尔值
np.object_: Python 对象
np.string_: 字符串
np.unicode_: Unicode 字符串
ndarray 具有一些重要的属性,用于描述其结构和内容:
ndim: 数组的维度数量。
shape: 数组的形状,是一个元组,表示每个维度的大小。
size: 数组中元素的总数量。
dtype: 数组中元素的数据类型。
itemsize: 每个元素占用的字节数。
data: 指向数组数据的内存缓冲区的指针。
arr = np.array([[1, 2, 3], [4, 5, 6]]) print("ndim:", arr.ndim) # 输出: ndim: 2 print("shape:", arr.shape) # 输出: shape: (2, 3) print("size:", arr.size) # 输出: size: 6 print("dtype:", arr.dtype) # 输出: dtype: int64 (取决于系统) print("itemsize:", arr.itemsize) # 输出: itemsize: 8 (int64 占 8 个字节) print("data:", arr.data) # 输出: <memory at 0x...> (内存地址)
ndarray 的索引和切片操作与 Python 列表类似,但更加灵活。
2.4.1 索引:
可以使用整数索引访问 ndarray 中的单个元素。
arr = np.array([1, 2, 3, 4, 5]) print(arr[0]) # 输出: 1 print(arr[3]) # 输出: 4 arr2d = np.array([[1, 2, 3], [4, 5, 6]]) print(arr2d[0, 0]) # 输出: 1 print(arr2d[1, 2]) # 输出: 6 print(arr2d[0][1]) # 输出: 2 (等价于 arr2d[0, 1])
2.4.2 切片:
可以使用切片访问 ndarray 中的一部分元素。切片语法为 [start:stop:step]。
arr = np.arange(10) print(arr[2:5]) # 输出: [2 3 4] print(arr[:4]) # 输出: [0 1 2 3] print(arr[5:]) # 输出: [5 6 7 8 9] print(arr[::2]) # 输出: [0 2 4 6 8] arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(arr2d[:2, 1:]) # 输出: # [[2 3] # [5 6]] print(arr2d[1:, :2]) # 输出: # [[4 5] # [7 8]] print(arr2d[:, 2]) # 获取所有行的第三列 # 输出: [3 6 9]
重要提示: ndarray 的切片是视图,这意味着对切片所做的修改会反映到原始数组中。如果需要复制切片,可以使用 copy() 方法。
arr = np.arange(10) slice_arr = arr[2:5] slice_arr[0] = 99 print(arr) # 输出: [ 0 1 99 3 4 5 6 7 8 9] (原始数组也被修改了) copy_arr = arr[2:5].copy() copy_arr[0] = 100 print(arr) # 输出: [ 0 1 99 3 4 5 6 7 8 9] (原始数组没有被修改) print(copy_arr) # 输出: [100 3 4]
2.4.3 布尔索引:
可以使用布尔数组作为索引,选择 ndarray 中满足条件的元素。
arr = np.array([5, 2, 8, 1, 9, 3]) bool_arr = arr > 4 print(bool_arr) # 输出: [ True False True False True False] print(arr[bool_arr]) # 输出: [5 8 9] print(arr[arr > 4]) # 更简洁的写法
2.4.4 花式索引:
可以使用整数数组作为索引,选择 ndarray 中特定位置的元素。
arr = np.array([10, 20, 30, 40, 50, 60]) index_arr = [0, 2, 4] print(arr[index_arr]) # 输出: [10 30 50] arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) row_indices = [0, 2] col_indices = [1, 2] print(arr2d[row_indices, col_indices]) # 输出 [2 9] (选取(0,1)和(2,2)的元素)
NumPy 提供了多种方法来改变 ndarray 的形状。
2.5.1 reshape():
reshape() 方法可以改变数组的形状,但不改变数组中的数据。
arr = np.arange(12) reshaped_arr = arr.reshape(3, 4) # 3行4列 print(reshaped_arr) # 输出: # [[ 0 1 2 3] # [ 4 5 6 7] # [ 8 9 10 11]] # 使用 -1 自动计算维度 reshaped_arr2 = arr.reshape(4, -1) # 4行,列数自动计算 print(reshaped_arr2) # 输出: # [[ 0 1 2] # [ 3 4 5] # [ 6 7 8] # [ 9 10 11]]
2.5.2 ravel() 和 flatten():
ravel() 和 flatten() 方法可以将多维数组展平为一维数组。ravel() 返回的是视图,而 flatten() 返回的是副本。
arr = np.array([[1, 2, 3], [4, 5, 6]]) raveled_arr = arr.ravel() print(raveled_arr) # 输出: [1 2 3 4 5 6] flattened_arr = arr.flatten() print(flattened_arr) # 输出: [1 2 3 4 5 6] raveled_arr[0] = 99 print(arr) # 输出: [[99 2 3] [ 4 5 6]] (原始数组被修改) flattened_arr[0] = 100 print(arr) # 输出: [[99 2 3] [ 4 5 6]] (原始数组没有被修改) print(flattened_arr) # 输出: [100 2 3 4 5 6]
2.5.3 transpose() 和 T 属性:
transpose() 方法和 T 属性可以交换数组的维度。
arr = np.array([[1, 2, 3], [4, 5, 6]]) transposed_arr = arr.transpose() print(transposed_arr) # 输出: # [[1 4] # [2 5] # [3 6]] transposed_arr2 = arr.T print(transposed_arr2) # 输出: # [[1 4] # [2 5] # [3 6]]
广播机制是 NumPy 的一个强大特性,它允许不同形状的数组进行算术运算。当两个数组的形状不完全相同时,NumPy 会自动扩展较小数组的形状,使其与较大数组的形状兼容,然后再进行运算。
广播的规则:
如果两个数组的维度数不同,则较小维度的数组会在其形状前补 1,直到维度数与较大数组相同。
如果两个数组的形状在任何维度上都不匹配,并且其中一个数组在该维度上的大小为 1,则该数组在该维度上会被广播以匹配另一个数组的形状。
如果在任何维度上,数组的形状既不相等,也没有一个等于 1,则会引发错误。
arr1 = np.array([1, 2, 3]) arr2 = np.array([[4, 5, 6], [7, 8, 9]]) # arr1 的形状会被扩展为 [[1, 2, 3], [1, 2, 3]] result = arr1 + arr2 print(result) # 输出: # [[ 5 7 9] # [ 8 10 12]] arr3 = np.array([1,2]) # arr4 = np.array([[1,2,3],[4,5,6]]) # arr3 + arr4 # 报错,无法广播
以下是一个使用 Mermaid 绘制的广播机制示意图:
NumPy 提供了大量的函数来操作 ndarray。以下是一些常用的函数:
np.sum(arr, axis): 计算数组中元素的和。axis 参数指定沿哪个轴进行计算。
np.mean(arr, axis): 计算数组中元素的平均值。
np.max(arr, axis): 找到数组中元素的最大值。
np.min(arr, axis): 找到数组中元素的最小值。
np.argmax(arr, axis): 找到数组中最大值的索引。
np.argmin(arr, axis): 找到数组中最小值的索引。
np.std(arr, axis): 计算数组中元素的标准差。
np.var(arr, axis): 计算数组中元素的方差。
np.sort(arr, axis): 对数组中的元素进行排序。
np.unique(arr): 找到数组中的唯一值。
np.concatenate((arr1, arr2), axis): 连接两个或多个数组。
np.vstack((arr1, arr2)): 垂直堆叠数组。
np.hstack((arr1, arr2)): 水平堆叠数组。
arr = np.array([[1, 2, 3], [4, 5, 6]]) print("Sum:", np.sum(arr)) # 输出: Sum: 21 print("Sum along axis 0:", np.sum(arr, axis=0)) # 输出: Sum along axis 0: [5 7 9] print("Mean:", np.mean(arr)) # 输出: Mean: 3.5 print("Max:", np.max(arr)) # 输出: Max: 6 print("Min:", np.min(arr)) # 输出: Min: 1 print("Argmax:", np.argmax(arr)) # 输出: Argmax: 5 print("Argmin:", np.argmin(arr)) # 输出: Argmin: 0 arr2 = np.array([3,1,4,1,5,9,2,6]) print("Sorted:", np.sort(arr2)) # 输出: Sorted: [1 1 2 3 4 5 6 9] print("Unique:", np.unique(arr2)) # 输出: Unique: [1 2 3 4 5 6 9] arr3 = np.array([[7,8,9]]) print("Concatenate:", np.concatenate((arr, arr3), axis=0)) # 输出: # [[1 2 3] # [4 5 6] # [7 8 9]] print("Vstack:", np.vstack((arr, arr3))) # 输出: # [[1 2 3] # [4 5 6] # [7 8 9]] arr4 = np.array([[10],[11]]) print("Hstack:", np.hstack((arr, arr4))) # 输出: # [[ 1 2 3 10] # [ 4 5 6 11]]