ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

NumPy核心函数全解析:数组操作、广播机制与线性代数实战

NumPy核心函数全解析:数组操作、广播机制与线性代数实战 NumPy 是 Python 科学计算生态里绕不开的基石不管你是做数据分析、机器学习还是信号处理第一个 import 的库十有八九就是它。老实说我最早接触 NumPy 的时候也以为它就是个高级列表后来踩过一堆关于广播、维度的坑才意识到真正理解核心函数和它们的内部逻辑才是高效使用 Python 做计算的分水岭。这篇文章我把 NumPy 的核心函数从头到尾整理一遍从环境搭建、数组创建、基础操作到广播机制、聚合函数、索引切片、线性代数再到高频踩坑的排查实录。每个函数都配了理论说明、可直接运行的代码示例和实际输出结果。适合刚入门想系统学 NumPy 的新手也适合已经用过 pandas 但对底层数组逻辑一知半解、想补上这块短板的朋友。文章会比较长建议先收藏再慢慢看。1. 搭建 NumPy 环境 - 别在第一步就卡住1.1 安装方法与版本选择的血泪教训很多教程会直接把pip install numpy甩给你但实际上安装这一步就能劝退不少人。我见过最典型的几个场景pip 装到一半报错、安装了以后 import 失败、和 pandas 版本不兼容甚至还有 conda 环境和 pip 环境互相覆盖的混乱局面。先说最基础的安装。Python 3.x 环境下直接用 pip 安装一般就够了pip install numpy如果你用的是 Anaconda 发行版通常 NumPy 已经预装好了可以用conda list | grep numpy确认版本。需要指定版本时pip install numpy1.26.4这里有一个非常关键的细节NumPy 的版本和 Python 版本有严格的对应关系。比如 NumPy 1.x 最高支持到 Python 3.11 附近Python 3.12 及以上版本再装 NumPy 1.x 就会直接报错常见的报错提示是找不到对应的 wheel 文件。遇到这种情况要么把 NumPy 升级到 2.x要么换回 Python 3.11 及以下版本。装完之后立刻验证一下import numpy as np print(np.__version__)能正常打印出版本号说明环境没问题。如果你是新手我强烈建议在虚拟环境里操作python -m venv myenv source myenv/bin/activate # Windows 下是 myenv\Scripts\activate pip install numpy这样做的目的是隔离依赖避免不同项目的包互相干扰。我早期图省事直接往全局环境里怼包结果某次升级把另一个项目的 scipy 搞崩了白白浪费了半天时间排查。提示如果你遇到numpy 版本不匹配这类报错多半是某个依赖库比如 pandas、scikit-learn对 NumPy 版本有硬性要求。先不要盲目升级 NumPy用pip list看看现有版本再决定怎么调整。1.2 从 Python 列表到 Ndarray - 为什么要用 NumPy很多人会问Python 原生列表明明也能存数、也能遍历为什么非要 NumPy答案就三个字性能和向量化。Python 原生列表里每个元素都是一个 Python 对象内存分散类型也不固定解释器每次操作都要做类型检查。而 NumPy 的 ndarrayN 维数组在内存里是连续存储的所有元素类型一致操作直接落到 C 语言层面执行。这意味着同样一个求和操作数据量越大NumPy 的优势越明显。我做过一个简单测试对 1000 万个随机数求和纯 Python 循环耗时大约 2 秒多而np.sum()耗时只有几十毫秒差距接近几十倍。根本原因是纯 Python 循环在解释器层面逐元素迭代而 NumPy 把循环下沉到了编译好的 C 代码里还利用了底层的高效内存访问模式。另外NumPy 的向量化写法还能让代码更简洁。同样计算 每个元素加 1 再取平方用 Python 列表写法需要写循环用 NumPy 只需要一行x np.array([1, 2, 3, 4]) result (x 1) ** 2这背后就是后面要讲的 ufunc通用函数机制它天然支持逐元素操作同时还支持广播。理解了这个区别你就能明白为什么所有科学计算库都要拿 NumPy 当底层——不是因为它好用而是因为它算得快且写得省。2. 数组创建与基础操作 - 一切计算的起点2.1 最常用的创建函数与参数细节NumPy 的数组创建函数有好几十个但真正日常高频使用的就那几个。我把它们按用途分了个类搭配示例和输出结果一起说。从已有数据创建import numpy as np # 从列表创建dtype 会自动推断 a np.array([1, 2, 3]) print(a, a.dtype) # 输出: [1 2 3] int64 # 显式指定类型 b np.array([1.5, 2.5, 3.5], dtypenp.float32) print(b, b.dtype) # 输出: [1.5 2.5 3.5] float32 # 二维数组 c np.array([[1, 2], [3, 4]]) print(c.shape) # 输出: (2, 2)np.array()是万物之源。注意一个坑如果你传入的嵌套列表每行长度不一致NumPy 在旧版本会给出一个不规则数组的警告在更严格的版本里可能直接报错。所以创建二维数组前务必保证子列表长度相同。按规则生成序列# np.arange: 类似 range但支持浮点步长 d np.arange(0, 10, 2) print(d) # 输出: [0 2 4 6 8] # np.linspace: 在闭区间内生成等间隔的 n 个数 e np.linspace(0, 1, 5) print(e) # 输出: [0. 0.25 0.5 0.75 1. ]np.arange和np.linspace很容易混淆。简单说arange指定的是步长终点是到不到无所谓的开区间逻辑类似 rangelinspace指定的是数量终点必然包含在内。做坐标轴、采样点这类任务linspace更常用因为它能精确控制点的数量不会因为浮点误差导致最后一个点丢失。全零、全一、单位矩阵zeros np.zeros((3, 4)) ones np.ones((2, 3)) eye np.eye(3) full np.full((2, 2), 7) print(zeros) # 输出: # [[0. 0. 0. 0.] # [0. 0. 0. 0.] # [0. 0. 0. 0.]] print(eye) # 输出: # [[1. 0. 0.] # [0. 1. 0.] # [0. 0. 1.]]这三个函数在初始化权重矩阵、构造 one-hot 编码、生成掩码矩阵时几乎是标配。np.full可能用得少一些但它能生成任意填充值的数组比zeros之后再全部赋值要高效得多。随机数生成# 标准正态分布形状 (2, 3) r np.random.randn(2, 3) print(r) # 输出示例每次运行不同: # [[ 0.124 0.203 -0.455] # [ 1.012 -0.876 0.334]] # 均匀分布 [0, 1)形状 (2, 2) u np.random.rand(2, 2) # 固定种子保证可复现 np.random.seed(42) r2 np.random.randn(3)注意np.random.randn()是生成标准正态分布均值为 0方差为 1如果需要其他均值和方差要自己做变换均值 标准差 * np.random.randn(...)。另外现代 NumPy 推荐用np.random.default_rng()这种新式随机数生成器但在大多数教程和老项目里np.random.seed()依然随处可见理解两者区别即可日常写代码用哪个都不影响功能。2.2 数据类型与形状管理 - 这两个概念搞不清就处处碰壁数组的dtype数据类型决定了每个元素占多少内存以及运算时的精度。NumPy 的类型体系比 Python 原生类型更精细最常见的几个dtype说明取值范围int8 / int16 / int32 / int64有符号整数位数不同范围不同uint8无符号整数0 到 255float16 / float32 / float64浮点数半精度、单精度、双精度complex64 / complex128复数实部虚部各占一半bool布尔值True / False有个日常很容易踩的坑整数除法精度损失。两个整数数组相除结果会被强制向下取整为整数a np.array([1, 2, 3]) b np.array([2, 2, 2]) print(a / b) # 输出: [0 1 1]注意是整数除法但实际上 NumPy 的/运算符执行的是真除法结果应该是浮点数[0.5 1. 1.5]。如果你看到整数结果大概率是数组本身是 int 类型而运算符或函数把它按整数处理了。需要浮点结果时先做astype(float)转换a_float a.astype(np.float64) print(a_float / b) # 输出: [0.5 1. 1.5]形状管理上reshape是最常用的。关键在于reshape 不改变数据在内存中的顺序只是重新解释维度。例如arr np.arange(6) print(arr) # 输出: [0 1 2 3 4 5] print(arr.reshape(2, 3)) # 输出: # [[0 1 2] # [3 4 5]]reshape(-1, n)这种写法很实用-1 表示这个维度由 NumPy 自动推导。比如不知道有多少行但确定要 4 列直接写arr.reshape(-1, 4)就行。ravel()和flatten()都能把多维数组展平区别是flatten()永远返回原数组的副本ravel()在可能的情况下返回视图不复制数据。涉及大规模数组时这个区别直接影响内存占用。判断是视图还是副本可以用np.shares_memory()检查。提示对视图的修改会同步影响到原数组。如果你只是想临时展平做操作而不想污染原始数据直接用flatten()更安全。3. 核心运算函数 - 向量化才是灵魂3.1 广播机制 - 理解了这个就理解了 NumPy 的一半广播broadcasting是 NumPy 里最核心也最容易被误解的机制。它的本质是当两个数组形状不一致时NumPy 自动把较小的数组拉伸到和较大数组相同的形状再进行逐元素运算。具体规则可以概括为三点从尾部维度开始比较两个数组的形状维度相等或者其中一个为 1或者其中一个缺失都视为兼容兼容的维度按较大的那个作为输出维度维度为 1 的数组会沿该方向扩展。看个最经典的例子一维数组加标量。a np.array([1, 2, 3]) print(a 10) # 输出: [11 12 13]这里的 10 被广播成了[10, 10, 10]再逐元素相加。这就是前面提到的向量化体验——没有循环没有列表推导式一行搞定。再看二维数组和一维数组相加matrix np.array([[1, 2, 3], [4, 5, 6]]) row np.array([10, 20, 30]) print(matrix row) # 输出: # [[11 22 33] # [14 25 36]]这里row形状是 (3,)和matrix的 (2, 3) 从尾部对齐第一维缺失视为 1于是扩展成 (2, 3) 再逐行相加。这个操作在数据预处理里极其常用——比如给特征矩阵的每一列减去该列的均值就是data np.random.randn(100, 5) mean data.mean(axis0) # 形状 (5,) centered data - mean一次减法完成所有列的均值去除没有任何循环。但广播也有翻车的时候。最典型的报错就是ValueError: operands could not be broadcast together。比如一个形状 (3, 2) 的数组和另一个形状 (3,) 的数组相加从尾部对齐第一个维度 2 和 3 不相等且没有一个是 1直接报错。遇到这种报错排查思路很固定打印两个数组的.shape从尾部开始逐个维度对比找有没有维度为 1 的或者维度缺失的情况如果确实需要让它们对齐用reshape手动加一个长度为 1 的维度比如arr[:, np.newaxis]。这里有个实战技巧。假设你要用一个形状为 (n,) 的一维权重数组去乘一个 (n, m) 的矩阵期望每列乘以对应的权重。直接matrix * weights会报错或产生错误结果取决于形状是否碰巧兼容。正确做法是把weights变成列向量weights np.array([1, 2, 3]) matrix np.array([[1, 2, 3], [4, 5, 6]]) result matrix * weights[:, np.newaxis] print(result) # 输出: # [[1 4 9] # [4 10 18]]如果不加np.newaxis广播会按行操作结果完全不一样。这种维度对齐的细节是大量广播 bug 的根源写代码时一定要养成检查形状的习惯。3.2 聚合函数与通用函数通用函数ufunc是 NumPy 对数组逐元素执行运算的函数集合比如np.add、np.multiply、np.exp、np.sqrt、np.sin等。它们的共同特征是输入一个或多个数组输出一个数组且逐元素独立计算。我一贯的经验是能用一个 ufunc 解决的绝不写 Python 循环。因为 ufunc 直接操作连续内存而且避免了 Python 层级的逐元素开销。举几个高频使用场景x np.array([1, 4, 9, 16]) print(np.sqrt(x)) # 输出: [1. 2. 3. 4.] print(np.exp(x)) # 输出: [2.71828183e00 ... 8.88611052e06] print(np.log(x)) # 对 0 和负数会警告输出 nan print(np.sin(x))注意np.log对非正数会输出nan或-inf并附带警告。处理真实数据时要先做清洗或使用np.where过滤掉非正值。聚合函数是把整个数组或某个方向归约为一个值。日常最常用的包括arr np.array([[1, 2, 3], [4, 5, 6]]) print(arr.sum()) # 输出: 21全部求和 print(arr.sum(axis0)) # 输出: [5 7 9]沿行方向压缩得到每列的和 print(arr.sum(axis1)) # 输出: [6 15]沿列方向压缩得到每行的和 print(arr.mean()) # 输出: 3.5 print(arr.max(axis1)) # 输出: [3 6] print(arr.argmax(axis0)) # 输出: [1 1 1]每列最大值所在的行下标axis参数是初学者最容易懵的地方。我的理解方式是axis 指定的是要消掉的维度。axis0就是把第 0 维行方向合并掉结果里剩下的是每一列的信息axis1就是把第 1 维列方向合并掉剩下的是每一行的信息。这个理解在任意维度上都成立。argmax/argmin返回的是最值所在的索引这在很多场景下比直接取最值更有用。比如找验证集上准确率最高的 epoch就是np.argmax(val_accuracies)。另外推荐一个容易被忽略的函数np.clip它把数组值裁剪到指定范围内data np.array([0.1, 2.5, -1.3, 4.0]) print(np.clip(data, 0, 1)) # 输出: [0.1 1. 0. 1. ]这个函数在处理梯度裁剪、图像像素范围限制时极其常用一行代码解决边界约束这种需求。3.3 线性代数函数 - 科学计算的硬核内容NumPy 的线性代数模块放在np.linalg下是科学计算里最常被调用的部分。包括矩阵乘法、行列式、逆矩阵、特征值分解等。先说最基础的矩阵乘法很多人会混淆*和np.dotA np.array([[1, 2], [3, 4]]) B np.array([[5, 6], [7, 8]]) print(A * B) # 逐元素相乘Hadamard 积 # 输出: # [[ 5 12] # [21 32]] print(A.dot(B)) # 矩阵乘法 # 输出: # [[19 22] # [43 50]] print(np.matmul(A, B)) # 等价于 A.dot(B)逐元素相乘和矩阵乘法的区别是很多新手认识 NumPy 运算的一道坎。简单说*是广播后的逐元素对应相乘要求两个数组形状完全一致或可广播.dot()和才是真正的线性代数意义上的矩阵乘法要求 A 的列数等于 B 的行数。Python 3.5 之后推荐直接用运算符更直观C A B行列式和逆矩阵的应用场景非常集中。解线性方程组、判断矩阵是否可逆、计算变换的缩放比例都离不开它们M np.array([[1, 2], [3, 4]]) det np.linalg.det(M) print(det) # 输出: -2.0000000000000004 inv np.linalg.inv(M) print(inv) # 输出: # [[-2. 1. ] # [ 1.5 -0.5]]注意两点。第一det接近 0 的矩阵是奇异矩阵求逆会失败或产生巨大数值误差所以工程上先算行列式或条件数再决定是否求逆。第二NumPy 的浮点运算会有极小的舍入误差比如行列式 -2 在输出时变成了 -2.0000000000000004这是正常的不要以为是 bug。特征值和特征向量的计算eigenvalues, eigenvectors np.linalg.eig(M) print(eigenvalues) # 输出: [-0.37228132 5.37228132] print(eigenvectors) # 输出: # [[-0.82456484 -0.41597356] # [ 0.56576746 -0.90937671]]在机器学习里PCA 降维就是靠np.linalg.eig或np.linalg.svd实现的。SVD奇异值分解是更数值稳定的选择U, S, Vt np.linalg.svd(M)还有解线性方程组直接用np.linalg.solveA np.array([[2, 1], [1, 1]]) b np.array([3, 2]) x np.linalg.solve(A, b) print(x) # 输出: [1. 1.]手动验证一下2×1 1×1 31×1 1×1 2完全正确。尽量不用inv(A).dot(b)解方程solve底层用的是 LU 分解数值稳定性更好、速度更快。顺带提一句热搜词里那个python 行列式计算不使用 numpy的需求很多时候是编程练习或作业要求手写高斯消元但工程上我强烈建议直接用np.linalg.det稳定性远好过自己用公式展开。4. 索引与切片 - 玩转数据操控的高阶技巧4.1 基础索引与切片操作NumPy 的切片语法和 Python 列表很像但维度更多规则也有一点不同。一维切片完全一致a np.arange(10) print(a[2:5]) # 输出: [2 3 4] print(a[:4]) # 输出: [0 1 2 3] print(a[::2]) # 输出: [0 2 4 6 8]二维数组的索引和切片要同时考虑两个维度arr np.arange(12).reshape(3, 4) print(arr) # 输出: # [[ 0 1 2 3] # [ 4 5 6 7] # [ 8 9 10 11]] print(arr[1, 2]) # 输出: 6第 1 行第 2 列 print(arr[0]) # 输出: [0 1 2 3]第 0 行 print(arr[:, 1]) # 输出: [1 5 9]第 1 列 print(arr[1:, :2]) # 输出: 第 1 行到最后第 0 到 1 列 # [[4 5] # [8 9]]切片返回的是视图而不是副本这是 NumPy 和 Python 列表最大的区别之一。修改切片结果原数组也会跟着变sub arr[0, :] sub[0] 99 print(arr[0, 0]) # 输出: 99原数组被改了这既是便利也是陷阱。便利之处在于不用复制数据就能高效操作大数组。陷阱在于如果你没意识到这是视图可能无意中污染原始数据。要拿到独立副本用.copy()方法sub arr[0, :].copy() sub[0] 100 print(arr[0, 0]) # 输出: 99原数组不受影响在处理图像数据时这个特性尤其重要。图像本质是 (H, W, C) 的数组很多人会切出某个通道后修改像素结果原图也跟着变了排查半天才发现是视图的锅。4.2 布尔索引与花式索引布尔索引是 NumPy 最强大的特性之一它直接用条件掩码来筛选数据data np.array([12, 5, 18, 21, 3]) mask data 10 print(mask) # 输出: [ True False True True False] print(data[mask]) # 输出: [12 18 21]更常见的写法是直接写条件print(data[data 10]) # 输出: [12 18 21]布尔索引可以组合多个条件但要注意用和|而不是 Python 的and和orprint(data[(data 5) (data 20)]) # 输出: [12 18]为什么不能用and因为and会尝试把整个数组转成布尔值而数组的布尔值判断是元素是否全为 True语义完全不同。这种语法细节让无数人报过错记住数组条件组合用位运算符。布尔索引在数据清洗里价值巨大。比如替换异常值values np.array([1.2, 3.4, 99.9, 5.6, 99.9]) values[values 90] np.nan # 把超过 90 的标记为缺失 print(values) # 输出: [ 1.2 3.4 nan 5.6 nan]花式索引fancy indexing是使用整数数组作为索引可以按任意顺序取特定位置的数据a np.arange(10) idx np.array([0, 0, 3, 3, 7]) print(a[idx]) # 输出: [0 0 3 3 7]这种操作在做数据重采样、打乱样本顺序时很好用。比如机器学习训练前打乱数据最常见的就是生成随机索引然后按索引取数据indices np.random.permutation(len(x_train)) x_shuffled x_train[indices] y_shuffled y_train[indices]np.where也是个高频函数它既可以用作条件筛选也可以根据条件从两个数组中选择cond np.array([True, False, True, False]) print(np.where(cond, 1, 0)) # 输出: [1 0 1 0] x np.array([10, 20, 30, 40]) print(np.where(x 25, 高, 低)) # 输出: [低 低 高 高]这个函数在处理满足条件则取 A否则取 B这类逻辑时比循环快出几个数量级。5. 排序、搜索与集合操作 - 数据整理必备5.1 排序与去重排序看起来简单但 NumPy 的sort有几个变体需要注意。最关键的区别是np.sort(arr)返回排序后的新数组不修改原数组arr.sort()是原地排序直接修改原数组。arr np.array([3, 1, 2, 5, 4]) print(np.sort(arr)) # 输出: [1 2 3 4 5] print(arr) # 输出: [3 1 2 5 4]原数组没变 arr.sort() print(arr) # 输出: [1 2 3 4 5]原数组被修改二维数组排序需要指定axis参数按行排序还是按列排序取决于你的需求m np.array([[3, 1], [2, 4]]) print(np.sort(m, axis0)) # 每列独立排序 # 输出: # [[2 1] # [3 4]] print(np.sort(m, axis1)) # 每行独立排序 # 输出: # [[1 3] # [2 4]]argsort返回的是排序后的索引这个比sort本身更常见因为它能同时用于多个有关联的数组。例如按分数排序但需要保留对应的 IDscores np.array([88, 92, 75, 99]) ids np.array([101, 102, 103, 104]) sorted_ids ids[np.argsort(scores)] print(sorted_ids) # 输出: [103 101 102 104]按分数从低到高对应的 IDnp.unique做去重和统计非常方便labels np.array([0, 1, 0, 2, 1, 0]) unique_labels, counts np.unique(labels, return_countsTrue) print(unique_labels) # 输出: [0 1 2] print(counts) # 输出: [3 2 1]这个函数在统计类别分布时是首选比手动collections.Counter快很多而且自带排序。5.2 条件逻辑与数组集合操作np.isin和集合类操作也是数据筛选的高频工具arr np.array([1, 2, 3, 4, 5]) print(np.isin(arr, [2, 4])) # 输出: [False True False True False] print(arr[np.isin(arr, [2, 4])]) # 输出: [2 4]集合操作np.intersect1d、np.union1d、np.setdiff1d在比较两个数据集的交集、差集时很直观a np.array([1, 2, 3, 4]) b np.array([3, 4, 5, 6]) print(np.intersect1d(a, b)) # 输出: [3 4] print(np.setdiff1d(a, b)) # 输出: [1 2]在 a 中但不在 b 中这些操作在数据清洗场景里应用极广。比如有一份用户 ID 列表和一份活跃用户 ID 列表要找出非活跃用户就是np.setdiff1d(all_users, active_users)。6. 常见问题排查 - 那些年踩过的坑6.1 版本不匹配与导入报错numpy 版本不匹配是最常见的环境问题而且报错信息往往让人一头雾水。典型场景是你装了最新版 NumPy但某个旧库比如某年前的 scikit-learn 版本还在用已经被移除的 API。解决方案有两种第一查看哪个库依赖了 NumPypip show numpy conda list | grep numpy第二根据报错信息里的库名去它的官方文档查支持的 NumPy 版本范围。比如 pandas 2.x 对 NumPy 的要求通常是1.22.4且2或兼容 2.x具体以官方为准。还有一类问题是安装成功但 import 报错常见原因是 pip 装的 NumPy 和系统里另一个 Python 环境不匹配。用which python和which pip确认是不是同一个环境which python which pip python -c import numpy; print(numpy.__file__)如果发现 import 的路径不对大概率是环境混乱建议直接创建干净的新虚拟环境重来。6.2 广播错误与维度地狱广播错误是新手和老手都会遇到的。我总结了一份速查表报错场景常见原因解决方式operands could not be broadcast together两个数组形状不兼容打印.shape从尾部逐维度对比用reshape或np.newaxis增加维度IndexError: too many indices用高维索引访问低维数组牢记数组的维度打印.ndim确认AxisError: axis N is out of boundsaxis 参数超过了ndim - 1axis 的范围是 0 到ndim - 1负索引从尾部数cannot reshape array of size X into shape Yreshape 前后元素总数不一致用reshape(-1, n)让 NumPy 自动推导一个维度最有效的排查工具就是打印形状print(arr.shape)。不要嫌麻烦我见过太多人花半小时查一个本来一眼就能看出来的维度问题。6.3 性能优化与内存管理的实战心得最后分享一些我实际项目里验证过的性能经验。第一避免在循环里调用 NumPy 函数。向量化不是摆设把循环改成 ufunc 操作数据量越大收益越明显。第二注意视图与副本的内存开销。flatten()会复制数据大数据场景下要么用ravel()要么直接用reshape(-1)。如果你不确定自己操作的是视图还是副本可以用np.shares_memory()检查。第三用对数据类型能省一半内存。一张 4096×4096 的 float64 矩阵占用 128 MB同样的数据用 float32 只占 64 MB。如果精度要求允许优先使用 float32。第四小心axis参数的效果。np.concatenate、np.stack、np.vstack、np.hstack这几个函数容易混。vstack按行拼接hstack按列拼接stack会新增一个维度concatenate则需要你明确指定 axis。手动构造数据试一次比死记文档要牢靠得多。还有一个常常被忽视的点NumPy 数组在内存中的布局有两种——C 顺序按行优先和 F 顺序按列优先。大多数时候你不需要管它但如果做大规模矩阵运算或图像处理存储顺序对性能的影响能达到数倍差距。用np.ascontiguousarray()可以确保数组是 C 顺序存储的这在给底层 C 库传数据时尤其重要。我在实际项目里用得最多的组合套路是np.arange生成索引np.random.permutation打乱顺序reshape调整形状布尔索引过滤数据np.linalg.solve解方程。这套组合拳几乎覆盖了从数据处理到模型求解的完整链路。最后再分享一个小技巧当你调试代码时可以用np.set_printoptions(threshold5, edgeitems2)让特大数组只打印首尾几个元素避免终端被几千行数字刷屏。这个小设置我在调试高维张量时几乎每次都用能让你把注意力集中在形状和边界值上而不是被海量数据淹没。
返回列表