NumPy教程:从基础到高级,玩转数组操作
NumPy(Numerical Python)是Python中用于科学计算的核心库。它提供了强大的多维数组对象(ndarray),以及用于处理这些数组的各种函数。与Python内置的列表相比,NumPy数组在存储效率和计算性能上都有显著优势,尤其是在处理大规模数值数据时。这使得NumPy成为数据科学、机器学习和工程领域不可或缺的工具。
本教程将引导您从NumPy的基础知识,逐步深入到高级数组操作,帮助您全面掌握这一强大工具。
一、安装NumPy
在开始之前,您需要安装NumPy。最常用的安装方式有两种:
1. 使用pip安装 (推荐)
打开您的终端或命令行工具,运行以下命令:
bash
pip install numpy
2. 使用Conda安装 (推荐环境管理)
如果您使用Anaconda或Miniconda,可以通过conda进行安装。通常,建议为项目创建一个新的虚拟环境:
bash
conda create -n my_numpy_env python=3.9 # 创建一个名为my_numpy_env的Python 3.9环境
conda activate my_numpy_env # 激活环境
conda install numpy # 在激活的环境中安装NumPy
二、NumPy数组基础
NumPy的核心是其N维数组对象,即ndarray。它是一个同构的数据容器,意味着数组中的所有元素都必须是相同类型。
1. 创建数组
有多种方法可以创建NumPy数组:
-
从Python列表或元组创建:
“`python
import numpy as np从列表创建一维数组
arr_1d = np.array([1, 2, 3, 4, 5])
print(“一维数组:”, arr_1d)
print(“数据类型:”, type(arr_1d)) # 输出:从嵌套列表创建二维数组
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“二维数组:\n”, arr_2d)从元组创建数组
arr_from_tuple = np.array((10, 20, 30))
print(“从元组创建的数组:”, arr_from_tuple)
“` -
创建全零、全一或空数组:
“`python
创建2×3的全零数组
zeros_array = np.zeros((2, 3))
print(“全零数组:\n”, zeros_array)创建3×2的全一数组
ones_array = np.ones((3, 2))
print(“全一数组:\n”, ones_array)创建2×2的空数组(内容随机)
empty_array = np.empty((2, 2))
print(“空数组:\n”, empty_array)
“` -
创建序列数组:
“`python
使用arange创建等差序列(类似于Python的range)
arange(start, stop_exclusive, step)
range_array = np.arange(0, 10, 2) # 0, 2, 4, 6, 8
print(“arange数组:”, range_array)使用linspace创建等间隔序列
linspace(start, stop_inclusive, num_elements)
linspace_array = np.linspace(0, 1, 5) # 0.0, 0.25, 0.5, 0.75, 1.0
print(“linspace数组:”, linspace_array)
“`
2. 数组属性
NumPy数组有许多有用的属性,可以帮助我们了解数组的结构和内容:
“`python
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(“数组:\n”, arr)
print(“维度数量 (ndim):”, arr.ndim) # 数组的轴(维度)的数量,例如:2表示二维数组
print(“数组形状 (shape):”, arr.shape) # 每个维度的大小,例如:(2, 3)表示2行3列
print(“元素总数 (size):”, arr.size) # 数组中元素的总数量
print(“数据类型 (dtype):”, arr.dtype) # 数组元素的类型,例如:int64
print(“单个元素字节大小 (itemsize):”, arr.itemsize) # 数组中每个元素占用的字节数
print(“数组总字节数 (nbytes):”, arr.nbytes) # 整个数组占用的总字节数
“`
三、数组索引与切片
NumPy数组的索引和切片功能强大且灵活,允许我们高效地访问和修改数组中的元素。
1. 单元素索引
通过指定索引来访问数组中的单个元素。对于多维数组,需要为每个维度提供一个索引。
“`python
arr = np.array([10, 20, 30, 40, 50])
print(“第一个元素:”, arr[0]) # 输出: 10
print(“最后一个元素 (负索引):”, arr[-1]) # 输出: 50
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“数组:\n”, arr_2d)
print(“位于 (0, 1) 的元素:”, arr_2d[0, 1]) # 第一行第二列,输出: 2
print(“位于 (2, 0) 的元素:”, arr_2d[2, 0]) # 第三行第一列,输出: 7
“`
2. 切片 ([start:stop:step])
切片用于提取数组的子部分。其语法与Python列表切片类似,但可以应用于多个维度。
“`python
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(“前5个元素:”, arr[:5]) # 输出: [0 1 2 3 4]
print(“从索引5开始到结束:”, arr[5:]) # 输出: [5 6 7 8 9]
print(“从索引2到7,步长为2:”, arr[2:8:2]) # 输出: [2 4 6]
print(“反转数组:”, arr[::-1]) # 输出: [9 8 7 6 5 4 3 2 1 0]
多维数组切片
arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print(“二维数组:\n”, arr_2d)
print(“所有行,前两列:\n”, arr_2d[:, :2]) # 输出: [[ 1 2], [ 5 6], [ 9 10]]
print(“前两行,所有列:\n”, arr_2d[:2, :]) # 输出: [[1 2 3 4], [5 6 7 8]]
print(“特定行和列 (从索引1开始的行,从索引1到2的列):\n”, arr_2d[1:, 1:3])
输出: [[ 6 7], [10 11]]
``.copy()`方法。
**重要提示:** NumPy切片通常返回原始数组的**视图 (view)**,而不是副本。这意味着如果您修改切片,原始数组也会随之改变。如果需要独立副本,请使用
四、数组操作与重塑
NumPy提供了丰富的函数来改变数组的形状、转置以及合并/拆分数组。
1. 重塑 (reshape(), flatten(), ravel())
-
reshape():改变数组的形状,而不改变其数据。
“`python
arr = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9]
print(“原始数组:”, arr)重塑为3×3矩阵
reshaped_arr = arr.reshape((3, 3))
print(“重塑为3×3:\n”, reshaped_arr)使用-1自动推断维度
reshaped_arr_auto = arr.reshape(-1, 3) # 自动计算行数
print(“自动推断行数重塑:\n”, reshaped_arr_auto)
“` -
flatten():返回数组的扁平化(一维)副本。 -
ravel():返回数组的扁平化视图(如果可能)。python
print("扁平化 (副本):", reshaped_arr.flatten())
print("扁平化 (视图):", reshaped_arr.ravel())
2. 转置 (.T, np.transpose())
转置操作会交换数组的轴。对于二维数组,行变成列,列变成行。
“`python
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“原始二维数组:\n”, arr_2d)
transposed_arr = arr_2d.T # 或者 np.transpose(arr_2d)
print(“转置数组:\n”, transposed_arr)
“`
3. 堆叠/合并 (np.vstack(), np.hstack(), np.concatenate())
np.vstack():垂直堆叠(按行)。np.hstack():水平堆叠(按列)。-
np.concatenate():沿指定轴合并数组。“`python
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])v_stack = np.vstack((arr1, arr2))
print(“垂直堆叠:\n”, v_stack)h_stack = np.hstack((arr1, arr2))
print(“水平堆叠:”, h_stack)concatenate要求所有数组除了连接轴以外的维度都相同
所以需要先将一维数组重塑为二维数组
concat_arr = np.concatenate((arr1.reshape(1, -1), arr2.reshape(1, -1)), axis=0)
print(“沿轴0合并 (行合并):\n”, concat_arr)
“`
4. 拆分 (np.hsplit(), np.vsplit(), np.split())
np.hsplit():水平拆分(按列)。np.vsplit():垂直拆分(按行)。-
np.split():沿指定轴拆分数组。“`python
arr = np.arange(1, 10).reshape(3, 3)
print(“原始数组:\n”, arr)h_split = np.hsplit(arr, 3) # 水平拆分为3个等宽数组
print(“水平拆分:”, h_split)v_split = np.vsplit(arr, 3) # 垂直拆分为3个等高数组
print(“垂直拆分:”, v_split)沿轴1(列)在索引1和2处进行拆分
split_arr = np.split(arr, [1, 2], axis=1)
print(“在特定索引处拆分:\n”, split_arr)
“`
五、基本算术运算
NumPy数组支持所有标准的算术运算符,这些运算都是元素级的。
1. 元素级运算
“`python
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):”, arr1 ** 2) # [1 4 9]
“`
2. 比较运算
比较运算符也作用于元素级,并返回一个布尔数组。
python
print("比较 (arr1 > 2):", arr1 > 2) # [False False True]
print("比较 (arr1 == arr2):", arr1 == arr2) # [False False False]
3. 通用函数 (Ufuncs)
NumPy提供了大量的通用函数(Universal Functions,ufuncs),它们是作用于ndarray对象的元素级数学函数。
“`python
arr = np.array([0, np.pi/2, np.pi]) # pi是NumPy中定义的圆周率常量
print(“正弦 (sin):”, np.sin(arr))
print(“余弦 (cos):”, np.cos(arr))
print(“指数 (exp):”, np.exp(arr))
print(“平方根 (sqrt):”, np.sqrt(arr))
print(“自然对数 (log):”, np.log(arr + 1e-9)) # 添加小值防止log(0)
“`
六、高级数组操作
NumPy的强大之处还在于其高级功能,如广播、线性代数、随机数生成等。
1. 广播 (Broadcasting)
广播是NumPy在不同形状的数组上执行算术运算的一种机制。它允许较小的数组在较大的数组上“广播”其值,而无需显式复制数据。
广播规则:
当对两个数组进行操作时,NumPy会逐元素地比较它们的形状(从末尾维度开始)。如果满足以下条件之一,则两个维度兼容:
1. 维度相等。
2. 其中一个维度为1。
如果不兼容,则会引发 ValueError。
“`python
arr = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
scalar = 10 # 形状 ()
标量被广播到数组的每个元素
print(“数组 + 标量:\n”, arr + scalar)
vec = np.array([10, 20, 30]) # 形状 (3,)
向量被广播到数组的每一行
print(“数组 + 向量:\n”, arr + vec)
形状不兼容的示例(会引发错误)
vec_incompatible = np.array([10, 20]) # 形状 (2,)
print(arr + vec_incompatible)
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
“`
2. 线性代数
NumPy的linalg模块提供了执行线性代数操作的函数。
“`python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
矩阵乘法 (点积)
print(“矩阵乘法 (A @ B):\n”, A @ B) # 或者 np.dot(A, B)
元素级乘法
print(“元素级乘法 (A * B):\n”, A * B)
行列式
print(“A的行列式:”, np.linalg.det(A))
逆矩阵
print(“A的逆矩阵:\n”, np.linalg.inv(A))
特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
print(“A的特征值:”, eigenvalues)
print(“A的特征向量:\n”, eigenvectors)
“`
3. 随机数生成 (np.random)
np.random模块提供了各种生成随机数的函数。
“`python
生成0到1之间的随机浮点数
print(“单个随机浮点数 (0-1):”, np.random.rand())
print(“2×2随机浮点数 (0-1):\n”, np.random.rand(2, 2))
从标准正态分布(均值0,标准差1)生成随机数
print(“单个标准正态分布随机数:”, np.random.randn())
print(“2×2标准正态分布随机数:\n”, np.random.randn(2, 2))
生成指定范围的随机整数
print(“单个随机整数 (0-9):”, np.random.randint(10)) # 不包含10
print(“2×2随机整数 (0-9):\n”, np.random.randint(0, 10, size=(2, 2)))
“`
4. 排序 (np.sort(), np.argsort())
np.sort():返回数组的排序副本。可以指定沿哪个轴进行排序。np.argsort():返回对数组进行排序的索引。
“`python
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
sorted_arr = np.sort(arr)
print(“排序后的数组:”, sorted_arr)
arr_2d = np.array([[3, 2, 1], [6, 5, 4]])
print(“原始二维数组:\n”, arr_2d)
print(“沿行排序 (axis=1):\n”, np.sort(arr_2d, axis=1))
print(“沿列排序 (axis=0):\n”, np.sort(arr_2d, axis=0))
indices = np.argsort(arr)
print(“排序索引:”, indices)
print(“根据索引排序的数组:”, arr[indices])
“`
5. 统计操作
NumPy提供了丰富的统计函数,用于计算数组的均值、中位数、标准差、和、最大值、最小值等。
“`python
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(“均值 (Mean):”, np.mean(arr))
print(“中位数 (Median):”, np.median(arr))
print(“标准差 (Standard Deviation):”, np.std(arr))
print(“方差 (Variance):”, np.var(arr))
print(“总和 (Sum):”, np.sum(arr))
print(“最小值 (Minimum):”, np.min(arr))
print(“最大值 (Maximum):”, np.max(arr))
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“所有元素的均值:”, np.mean(arr_2d))
print(“沿列的均值 (axis=0):”, np.mean(arr_2d, axis=0)) # [2.5 3.5 4.5]
print(“沿行的均值 (axis=1):”, np.mean(arr_2d, axis=1)) # [2. 5.]
“`
6. 布尔索引 (Boolean Indexing)
布尔索引使用布尔数组(掩码)来选择满足特定条件的元素。
“`python
arr = np.array([10, 20, 30, 40, 50, 60])
mask = arr > 30 # 创建一个布尔掩码
print(“布尔掩码:”, mask) # [False False False True True True]
selected_elements = arr[mask] # 使用掩码选择元素
print(“选择大于30的元素:”, selected_elements) # [40 50 60]
直接使用条件进行布尔索引
print(“选择偶数元素:”, arr[arr % 2 == 0]) # [10 20 30 40 50 60]
二维数组的布尔索引
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“选择大于5的二维数组元素:”, arr_2d[arr_2d > 5]) # 返回一维数组: [6 7 8 9]
“`
7. 花式索引 (Fancy Indexing)
花式索引使用整数数组来选择特定行、列或元素。
“`python
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
indices = np.array([0, 3, 5])
print(“选择特定索引的元素:”, arr[indices]) # [10 40 60]
二维数组的花式索引 (选择特定行)
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
row_indices = np.array([0, 2])
print(“选择特定行:\n”, arr_2d[row_indices]) # 选择第0行和第2行
使用坐标对选择特定元素
row_coords = np.array([0, 1, 2])
col_coords = np.array([0, 1, 0])
print(“选择 (0,0), (1,1), (2,0) 处的元素:”, arr_2d[row_coords, col_coords]) # [1 5 7]
“`
注意: 与切片不同,花式索引总是返回数据的副本,而不是视图。
七、数组的保存与加载
NumPy提供了方便的函数来将数组保存到磁盘,以及从磁盘加载数组。
1. 保存和加载单个数组
np.save():将单个数组保存到二进制.npy格式文件。np.load():从.npy文件加载数组。
“`python
data = np.arange(100).reshape(10, 10)
np.save(‘my_array.npy’, data)
print(“数组已保存到 my_array.npy”)
loaded_data = np.load(‘my_array.npy’)
print(“从 my_array.npy 加载的数组:\n”, loaded_data)
“`
2. 保存和加载多个数组
np.savez():将多个数组保存到未压缩的.npz文件中。您可以使用关键字参数为每个数组命名。np.savez_compressed():将多个数组保存到压缩的.npz文件中,节省磁盘空间。np.load():加载.npz文件时,它返回一个类似于字典的对象,可以通过键(即保存时的关键字参数)访问各个数组。
“`python
array1 = np.array([1, 2, 3])
array2 = np.array([[10, 20], [30, 40]])
np.savez(‘multiple_arrays.npz’, a=array1, b=array2)
print(“多个数组已保存到 multiple_arrays.npz”)
np.savez_compressed(‘compressed_arrays.npz’, x=array1, y=array2)
print(“多个数组已保存到 compressed_arrays.npz (压缩格式)”)
loaded_multi = np.load(‘multiple_arrays.npz’)
print(“加载的数组 ‘a’:”, loaded_multi[‘a’])
print(“加载的数组 ‘b’:\n”, loaded_multi[‘b’])
loaded_compressed = np.load(‘compressed_arrays.npz’)
print(“从压缩文件加载的数组 ‘x’:”, loaded_compressed[‘x’])
“`
3. 保存和加载文本文件
对于人类可读的格式,如CSV(逗号分隔值),可以使用 np.savetxt() 和 np.loadtxt()。
“`python
text_data = np.array([[1.1, 2.2], [3.3, 4.4]])
np.savetxt(‘my_data.csv’, text_data, delimiter=’,’)
print(“数组已保存到 my_data.csv”)
loaded_text_data = np.loadtxt(‘my_data.csv’, delimiter=’,’)
print(“从 my_data.csv 加载的数组:\n”, loaded_text_data)
“`
结语
NumPy是Python科学计算的基石,掌握其数组操作对于进行高效的数据处理和分析至关重要。从基本的数组创建、索引切片到高级的广播、线性代数和统计功能,NumPy都提供了强大且优化的工具。
本教程为您提供了NumPy从基础到高级的全面概览。鼓励您通过实践和探索NumPy的官方文档来进一步深入学习,充分发挥其在数据科学和机器学习项目中的巨大潜力。