Numpy能处理数据吗?

摘要:NumPy 从基础到高级完整讲解 NumPy(Numerical Python)是Python科学计算的核心库,其核心是多维数组(ndarray),提供了高效的数值计算、向量化操作、广播机制等功能,相比Python原生列表,NumPy数组在
NumPy 从基础到高级完整讲解 NumPy(Numerical Python)是Python科学计算的核心库,其核心是多维数组(ndarray),提供了高效的数值计算、向量化操作、广播机制等功能,相比Python原生列表,NumPy数组在内存占用、计算速度上有数量级的提升。 本文将分「基础篇」「进阶篇」「高级篇」,结合可运行代码、数据示例和核心结论,全面讲解NumPy的使用。 一、基础篇:核心概念与基本操作 1.1 安装与导入 首先确保安装NumPy,然后导入(约定俗成用np作为别名): # 安装(终端执行) # pip install numpy # 导入 import numpy as np 1.2 核心数据结构:ndarray ndarray是NumPy的核心,是同质的多维数组(所有元素类型相同),以下是常见创建方式: (1)从Python列表/元组创建 # 一维数组 arr1d = np.array([1, 2, 3, 4, 5]) print("一维数组:", arr1d) # 输出:一维数组: [1 2 3 4 5] # 二维数组(矩阵) arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print("二维数组:\n", arr2d) # 指定数据类型(dtype) arr_float = np.array([1, 2, 3], dtype=np.float64) print("浮点型数组:", arr_float, "dtype:", arr_float.dtype) 数据说明: 一维数组arr1d:[1,2,3,4,5](int32默认) 二维数组arr2d:3行3列的矩阵 arr_float:强制转为float64类型 结论: np.array()是创建数组的基础方法,支持嵌套列表创建多维数组; dtype可指定元素类型(int8/16/32/64、float16/32/64、bool等),避免类型不一致导致的计算问题。 (2)内置函数创建数组(常用) # 全0数组 zeros_arr = np.zeros((2, 3)) # 2行3列 print("全0数组:\n", zeros_arr) # 全1数组 ones_arr = np.ones((3, 2), dtype=np.int32) # 3行2列,int32类型 print("全1数组:\n", ones_arr) # 自定义填充值 full_arr = np.full((2, 2), 99) # 2行2列,填充99 print("自定义填充数组:\n", full_arr) # 等差数列(arange:start, stop, step) arange_arr = np.arange(0, 10, 2) # 0到10(不含),步长2 print("等差数列:", arange_arr) # 等间隔数列(linspace:start, stop, num) linspace_arr = np.linspace(0, 1, 5) # 0到1,生成5个等间隔数 print("等间隔数列:", linspace_arr) # 随机数数组 np.random.seed(42) # 固定随机种子,保证结果可复现 rand_arr = np.random.rand(2, 3) # 2行3列,0-1均匀分布随机数 randn_arr = np.random.randn(2, 3) # 2行3列,标准正态分布(均值0,方差1) randint_arr = np.random.randint(0, 10, (2, 3)) # 0-10(不含)整数随机数 print("均匀分布随机数:\n", rand_arr) print("标准正态分布随机数:\n", randn_arr) print("整数随机数:\n", randint_arr) # 单位矩阵(对角线为1,其余为0) eye_arr = np.eye(3) # 3阶单位矩阵 print("单位矩阵:\n", eye_arr) 数据说明: 内置函数创建的数组维度由参数shape(元组)指定,如(2,3)表示2行3列; 随机数数组通过seed固定种子,确保每次运行结果一致。 结论: 内置函数比手动创建列表更高效,尤其适合大规模数组; arange侧重“步长”,linspace侧重“点数”,按需选择; 随机数生成需注意种子(seed),保证实验可复现。 1.3 ndarray的核心属性 arr = np.random.randint(0, 10, (3, 4)) # 3行4列随机整数数组 print("数组本身:\n", arr) print("维度(ndim):", arr.ndim) # 维度数(二维=2) print("形状(shape):", arr.shape) # 形状(行,列)=(3,4) print("元素总数(size):", arr.size) # 3*4=12 print("数据类型(dtype):", arr.dtype) # 元素类型 print("每个元素字节数(itemsize):", arr.itemsize) # int32=4字节 数据说明:3行4列的随机整数数组(0-9) 结论: shape是最常用的属性,可通过arr.shape = (m,n)或reshape修改数组形状; size = 各维度长度乘积,反映数组的总元素数; itemsize × size = 数组占用的总内存(字节)。 1.4 索引与切片(核心操作) (1)基本索引(类似Python列表) # 一维数组索引 arr1d = np.arange(10) print("一维数组:", arr1d) print("索引5的元素:", arr1d[5]) # 单个元素 print("索引2-7(不含):", arr1d[2:7]) # 切片 print("逆序:", arr1d[::-1]) # 步长-1,反转 # 二维数组索引(行,列) arr2d = np.array([[1,2,3], [4,5,6], [7,8,9]]) print("二维数组:\n", arr2d) print("第2行(索引1):", arr2d[1]) # 整行 print("第1行第2列(索引0,1):", arr2d[0,1]) # 单个元素 print("前2行,后2列:\n", arr2d[:2, 1:]) # 切片:行[:2],列[1:] 数据说明: 一维数组arr1d:[0,1,2,3,4,5,6,7,8,9] 二维数组arr2d:3×3矩阵 结论: 一维数组索引/切片与Python列表完全一致; 二维数组索引格式为arr[行索引, 列索引],支持切片组合(如[:2,1:]),灵活选取子数组。 (2)布尔索引(按条件筛选) arr = np.random.randint(0, 20, (3, 4)) print("原始数组:\n", arr) # 筛选大于10的元素 mask = arr > 10 # 布尔掩码(True/False数组) print("布尔掩码:\n", mask) print("大于10的元素:", arr[mask]) # 返回一维数组 # 复合条件(与:&,或:|,非:~) mask_complex = (arr > 5) & (arr < 15) print("5<元素<15的子数组:\n", arr[mask_complex]) 数据说明:3×4随机数组(0-19),通过布尔掩码筛选元素 结论: 布尔索引是NumPy筛选数据的核心方式,返回满足条件的元素(一维数组); 复合条件需用&(and)、|(or)、~(not),且条件需用括号包裹。 (3)花式索引(整数数组索引) arr = np.arange(12).reshape(3, 4) # 3行4列,0-11 print("原始数组:\n", arr) # 按行索引选取 rows = [0, 2] # 选第0行和第2行 print("选第0、2行:\n", arr[rows]) # 按行列索引选取((行索引列表, 列索引列表)) cols = [1, 3] print("选(0,1)、(2,3)元素:", arr[rows, cols]) # 选指定行的所有列 print("选第1行和第2行的所有列:\n", arr[[1,2], :]) 数据说明:3×4数组[[0,1,2,3],[4,5,6,7],[8,9,10,11]] 结论: 花式索引通过整数列表/数组指定索引,可任意选取离散的行/列/元素; 花式索引返回的是原数组的副本(而非视图),修改不会影响原数组。 二、进阶篇:数组操作与运算 2.1 数组形状操作 arr = np.arange(12) # 一维数组:[0,1,...,11] print("原始数组:", arr, "shape:", arr.shape) # 重塑(reshape):不改变数据,仅改变形状 arr_reshape = arr.reshape(3, 4) print("重塑为3×4:\n", arr_reshape, "shape:", arr_reshape.shape) # 展平(flatten):返回副本;ravel:返回视图(更高效) arr_flatten = arr_reshape.flatten() arr_ravel = arr_reshape.ravel() print("flatten展平:", arr_flatten) print("ravel展平:", arr_ravel) # 转置(T):行列互换 arr_transpose = arr_reshape.T print("3×4转置为4×3:\n", arr_transpose) # 调整形状(resize):直接修改原数组 arr.resize((2, 6)) print("原数组resize为2×6:\n", arr) 数据说明:从一维数组[0-11]重塑为3×4,再转置/展平/调整形状 结论: reshape是只读操作,不修改原数组;resize直接修改原数组; flatten返回副本(修改不影响原数组),ravel返回视图(修改影响原数组),优先用ravel提升效率; 转置T是二维数组常用操作,高维数组需用transpose指定轴。 2.2 数组拼接与分割 # 拼接 arr1 = np.array([[1,2], [3,4]]) arr2 = np.array([[5,6], [7,8]]) # 垂直拼接(vstack):行增加 vstack_arr = np.vstack((arr1, arr2)) print("垂直拼接:\n", vstack_arr) # 水平拼接(hstack):列增加 hstack_arr = np.hstack((arr1, arr2)) print("水平拼接:\n", hstack_arr) # 通用拼接(concatenate):指定轴(axis) concat_axis0 = np.concatenate((arr1, arr2), axis=0) # 等价vstack concat_axis1 = np.concatenate((arr1, arr2), axis=1) # 等价hstack print("axis=0拼接:\n", concat_axis0) print("axis=1拼接:\n", concat_axis1) # 分割 arr = np.arange(12).reshape(3, 4) print("待分割数组:\n", arr) # 垂直分割(vsplit):按行分割 vsplit_arr = np.vsplit(arr, 3) # 分成3个1×4数组 print("垂直分割为3份:", vsplit_arr) # 水平分割(hsplit):按列分割 hsplit_arr = np.hsplit(arr, 2) # 分成2个3×2数组 print("水平分割为2份:\n", hsplit_arr[0], "\n", hsplit_arr[1]) 数据说明: 拼接:两个2×2数组拼接为4×2(垂直)或2×4(水平); 分割:3×4数组按行分割为3个1×4,按列分割为2个3×2。 结论: 拼接需保证除拼接轴外的其他维度长度一致(如垂直拼接要求列数相同); vstack/hstack是concatenate的简化版,axis=0为垂直,axis=1为水平; 分割是拼接的逆操作,vsplit/hsplit对应vstack/hstack。 2.3 广播机制(Broadcasting) 广播是NumPy的核心特性,允许不同形状的数组进行算术运算,规则: 维度少的数组自动补1(扩展维度); 各维度长度要么相同,要么有一个为1; 扩展后维度匹配,逐元素运算。 # 示例1:标量与数组运算(最常见) arr = np.array([[1,2,3], [4,5,6]]) scalar_add = arr + 10 # 标量10广播为2×3数组 print("数组+标量:\n", scalar_add) # 示例2:一维数组与二维数组广播 arr1d = np.array([1,2,3]) broadcast_add = arr + arr1d # arr1d补维度为(1,3),再扩展为(2,3) print("二维+一维广播:\n", broadcast_add) # 示例3:不同维度广播((3,1) + (1,4) → (3,4)) arr_a = np.arange(3).reshape(3, 1) # 3×1 arr_b = np.arange(4).reshape(1, 4) # 1×4 broadcast_mul = arr_a * arr_b print("3×1 × 1×4广播:\n", broadcast_mul) 数据说明: 示例1:2×3数组 + 标量10 → 每个元素+10; 示例2:2×3数组 + 1×3数组 → 每行都加[1,2,3]; 示例3:3×1 × 1×4 → 广播为3×4数组,逐元素相乘。 结论: 广播避免了手动扩展数组,大幅提升运算效率; 广播的核心是“维度补1 → 扩展匹配”,不满足规则会报错(如2×3与3×2数组无法直接广播)。 2.4 向量化运算(替代循环) NumPy的向量化运算比Python原生循环快10~100倍,核心是直接对数组运算,而非逐元素循环。 # 示例:计算数组每个元素的平方 import time # 原生循环 arr = np.random.rand(1000000) # 100万元素 start = time.time() result_loop = [] for x in arr: result_loop.append(x**2) result_loop = np.array(result_loop) end = time.time() print("循环耗时:", end - start, "秒") # 向量化运算 start = time.time() result_vector = arr **2 end = time.time() print("向量化耗时:", end - start, "秒") # 验证结果一致 print("结果是否一致:", np.allclose(result_loop, result_vector)) 数据说明:100万个0-1随机数的数组,分别用循环和向量化计算平方 输出示例: 循环耗时:0.123456 秒 向量化耗时:0.001234 秒 结果是否一致:True 结论: 向量化运算(如arr**2、np.add、np.multiply)完全替代循环,效率提升100倍以上; NumPy的算术运算符(+、-、*、/、**、%)均支持向量化,对应np.add、np.subtract、np.multiply等函数。 2.5 统计函数 NumPy提供丰富的统计函数,支持按整体/指定轴计算: arr = np.array([[1,2,3], [4,5,6], [7,8,9]]) print("原始数组:\n", arr) # 整体统计 print("总和:", np.sum(arr)) # 45 print("均值:", np.mean(arr)) # 5.0 print("标准差:", np.std(arr)) # 2.581988897471611 print("最小值:", np.min(arr)) # 1 print("最大值:", np.max(arr)) # 9 print("最小值索引:", np.argmin(arr)) # 0(一维索引) print("最大值索引:", np.argmax(arr)) # 8(一维索引) # 按轴统计(axis=0:列,axis=1:行) print("按列求和:", np.sum(arr, axis=0)) # [12 15 18] print("按行求均值:", np.mean(arr, axis=1)) # [2. 5. 8.] # 累积和/累积积 print("累积和(按行):\n", np.cumsum(arr, axis=1)) print("累积积(按列):\n", np.cumprod(arr, axis=0)) 数据说明:3×3矩阵,计算整体/按轴的统计量 结论: 统计函数默认对整个数组计算,指定axis可按行/列计算; argmin/argmax返回最小值/最大值的一维索引,高维数组可结合unravel_index转为多维索引; cumsum/cumprod用于计算累积和/积,是时序分析、累积概率计算的常用函数。 三、高级篇:线性代数与进阶技巧 3.1 线性代数(linalg模块) NumPy的np.linalg模块提供线性代数核心功能,等价于MATLAB的线性代数操作: # 矩阵乘法(@ 或 np.dot 或 np.matmul) A = np.array([[1,2], [3,4]]) B = np.array([[5,6], [7,8]]) mat_mul = A @ B # 矩阵乘法 dot_mul = np.dot(A, B) # 等价@(二维数组) print("矩阵乘法A@B:\n", mat_mul) # 逆矩阵(仅方阵且非奇异) A_inv = np.linalg.inv(A) print("A的逆矩阵:\n", A_inv) # 验证:A × A_inv = 单位矩阵(浮点误差可忽略) print("A × A_inv:\n", np.round(A @ A_inv, 0)) # 行列式 det_A = np.linalg.det(A) print("A的行列式:", det_A) # -2.0 # 特征值与特征向量 eigenvalues, eigenvectors = np.linalg.eig(A) print("特征值:", eigenvalues) print("特征向量:\n", eigenvectors) # 解线性方程组 Ax = b b = np.array([1, 2]) x = np.linalg.solve(A, b) print("方程组Ax=b的解:", x) # 验证:A@x ≈ b print("验证A@x:", A @ x) # 奇异值分解(SVD) svd_U, svd_S, svd_Vt = np.linalg.svd(A) print("SVD的U矩阵:\n", svd_U) print("SVD的奇异值:", svd_S) print("SVD的Vt矩阵:\n", svd_Vt) 数据说明: 矩阵A:[[1,2],[3,4]],矩阵B:[[5,6],[7,8]] 线性方程组:$\begin{cases}1x+2y=1 \ 3x+4y=2\end{cases}$ 结论: 矩阵乘法优先用@(Python3.5+),np.dot对一维数组是点积,二维数组是矩阵乘法; 逆矩阵仅适用于方阵且行列式≠0,否则报错; np.linalg.solve是解线性方程组的高效方法,比求逆矩阵再相乘更稳定; SVD是降维(PCA)、矩阵分解的核心算法,返回U(左奇异向量)、S(奇异值)、Vt(右奇异向量转置)。 3.2 高级索引与技巧 (1)where函数(条件替换) arr = np.random.randint(-5, 5, (3, 3)) print("原始数组:\n", arr) # where(条件, 满足时的值, 不满足时的值) arr_where = np.where(arr > 0, arr, 0) # 正数保留,负数/0置0 print("where替换后:\n", arr_where) # 多条件where arr_where2 = np.where((arr > 0) & (arr < 3), 1, np.where(arr >=3, 2, 0)) print("多条件where:\n", arr_where2) 结论:np.where是向量化的三元运算符,替代if-else循环,支持嵌套实现多条件判断。 (2)掩码数组(masked array) 处理缺失值/异常值时,掩码数组可标记无效元素,避免参与计算: # 创建掩码数组 arr = np.array([1, 2, np.nan, 4, 5]) masked_arr = np.ma.masked_invalid(arr) # 标记nan为无效 print("掩码数组:", masked_arr) print("掩码数组的均值:", masked_arr.mean()) # 忽略nan,均值=(1+2+4+5)/4=3.0 # 自定义掩码 arr2 = np.array([10, 20, 30, 40]) mask = [False, True, False, True] # 标记第2、4个元素为无效 masked_arr2 = np.ma.array(arr2, mask=mask) print("自定义掩码数组:", masked_arr2) print("掩码数组的和:", masked_arr2.sum()) # 10+30=40 结论: 掩码数组(np.ma)是处理缺失值的高效方式,避免手动过滤; masked_invalid可自动标记nan/inf为无效,统计函数会忽略掩码元素。 (3)视图(view)vs 副本(copy) arr = np.array([1,2,3,4,5]) # 视图(浅拷贝):共享内存 view_arr = arr.view() view_arr[0] = 99 print("原数组(视图修改后):", arr) # [99,2,3,4,5] # 副本(深拷贝):独立内存 copy_arr = arr.copy() copy_arr[1] = 88 print("原数组(副本修改后):", arr) # [99,2,3,4,5] print("副本数组:", copy_arr) # [99,88,3,4,5] 结论: 视图(如arr.view()、切片arr[:3])共享原数组内存,修改视图会影响原数组; 副本(如arr.copy()、花式索引)独立内存,修改不影响原数组; 避免无意识修改原数组,需明确使用copy()创建副本。 3.3 结构化数组(处理异构数据) 结构化数组支持存储不同类型的数据(如字符串、整数、浮点数),类似Excel表格: # 定义数据类型(dtype) dtype = [('name', 'U10'), ('age', np.int32), ('score', np.float64)] # 创建结构化数组 students = np.array([ ('Alice', 20, 95.5), ('Bob', 19, 88.0), ('Charlie', 21, 92.3) ], dtype=dtype) print("结构化数组:\n", students) # 按字段访问 print("所有姓名:", students['name']) print("所有年龄:", students['age']) print("Bob的成绩:", students[1]['score']) # 按条件筛选 high_score = students[students['score'] > 90] print("成绩>90的学生:\n", high_score) 数据说明: 字段name:字符串(U10表示最多10个Unicode字符); 字段age:int32;字段score:float64。 结论: 结构化数组是NumPy处理异构数据的方式,可替代Pandas的DataFrame(轻量级场景); 按字段名访问数据,支持布尔索引筛选,适合小规模异构数据处理。 3.4 性能优化建议 优先向量化:避免所有显式循环(for/while),用NumPy内置函数/向量化运算替代; 减少副本:尽量使用视图(如切片),避免频繁copy(); 指定dtype:提前指定合适的dtype(如int32而非int64),减少内存占用; 广播合理使用:避免过度广播导致的内存爆炸(如(1e6,1)与(1,1e6)广播为(1e6,1e6)); 结合Numba:极复杂的运算可通过numba.jit装饰器加速(编译为机器码)。 四、总结 NumPy是Python数值计算的基石,核心优势在于: 高效的ndarray:连续内存存储,同质类型,支持多维操作; 向量化+广播:替代循环,大幅提升计算效率; 丰富的内置函数:覆盖数组创建、操作、统计、线性代数等场景; 与其他库兼容:Pandas、Matplotlib、Scikit-learn等均基于NumPy构建。 学习路径建议: 基础篇:掌握数组创建、属性、索引切片; 进阶篇:熟练数组操作、广播、向量化运算; 高级篇:深入线性代数、掩码数组、结构化数组,结合实际场景(如数据分析、机器学习)落地。 NumPy的核心是“向量化思维”,摒弃Python原生的循环习惯,用数组整体操作替代逐元素处理,才能发挥其最大优势。