做水文模型的人应该都体会过SWAT调参的痛。SWAT(Soil and Water Assessment Tool)这种分布式水文模型,一次完整模拟涉及几十上百个参数,而其中相当一部分既不能直接测量,也没有统一标准去率定。我这次做的项目,核心是把两种全局敏感性分析方法——基于方差的Sobol方法和基于累积分布函数距离的PAWN方法——放到同一个SWAT高参数化模型上做对比,用Matlab完成采样、模拟调度、指数计算和结果可视化。项目要回答的问题很直接:面对SWAT这种“参数多、非线性强、单次模拟费时”的模型,PAWN和Sobol各自的优势和坑在哪里,实际做参数辨识时应该怎么选。这篇文章会把原理、代码实现和实操中踩过的坑都讲透,适合正在做水文模型率定、不确定性分析,或者刚接触全局敏感性分析的研究生和工程师参考。
1. 项目定位与整体思路拆解
1.1 为什么必须用全局敏感性分析
SWAT模型虽然物理机制完备,但参数空间大得吓人。一个典型的中型流域模型,光水文相关参数就能列出三五十个,从SCS径流曲线数CN2、土壤有效含水量SOL_AWC、饱和导水率SOL_K,到基流退水系数ALPHA_BF、土壤蒸发补偿系数ESCO、主河道曼宁糙率CH_N2等等。这些参数绝大多数无法通过野外实测直接标定,只能靠率定去“试”。
问题在于,如果直接把这个高维参数空间丢给率定算法,比如常见的SCE-UA、SUFI-2,会碰到两个很现实的问题:一是计算量爆炸,SWAT单次模拟再快也要几十秒到几分钟,几十个参数组合起来就是天文数字;二是参数间存在交互作用,盲目同时调整很容易过拟合,得到一个“能对上观测值但预测能力很差”的模型。
所以标准的做法是先降维——用全局敏感性分析筛选出对目标输出影响最大的核心参数,扔掉那些怎么变都不影响结果的参数,把问题缩小到一个可控的维度,再进行精细化率定。这就像修一台复杂机器,不可能每个螺丝都拆开检查,肯定先用仪器扫描一遍,找出真正影响运行的关键部位,再动手拆。全局敏感性分析就是这个“扫描仪器”。
1.2 为什么会选PAWN和Sobol做对比
敏感性分析方法很多,局部方法比如求偏导数、逐步回归,这里不谈,因为SWAT的强非线性和参数交互直接让局部方法失效。全局方法里,Sobol是当之无愧的行业标杆,理论完善、应用广泛,几乎所有水文敏感性分析论文里都有它的身影。它的思路是把输出总方差分解成每个参数和参数交互项的贡献,一阶指数衡量单参数主效应,总效应指数还包含交互项,信息量非常完整。
PAWN相对年轻,Pianosi和Wagener在2016年提出,核心思路是考察当某个参数被“固定”在某一段取值区间后,模型输出的累积分布函数会不会发生明显偏移。如果偏移大,说明这个参数重要;如果分布几乎不动,说明它可有可无。这个思路和Sobol完全不同,它不依赖方差分解,而是直接盯着整个输出分布形态的变化。
选择这两个方法做对比,不是什么理论偏好问题,而是因为它们在处理SWAT这种高参数化模型时的行为差异非常值得深挖:Sobol的方差视角对“输出均值变化”敏感,但可能会忽略“均值不变但分布形态剧变”的情况;PAWN恰恰擅长捕捉这种分布形态的变化,尤其是输出存在偏态、多峰或者极端值的情况下。把两个方法放在同一个模型上实体对比,比单纯看文献推导要直观得多。
1.3 项目技术路线与最终预期的落点
这个项目的技术路线很清晰:选定一个SWAT模型案例,确定目标输出(比如月径流序列的某个统计量),设定参数先验范围——然后在Matlab里完成两条路径的采样、模拟和指数计算。Sobol走Saltelli采样框架,PAWN走条件分箱采样框架,两条路径共用同一个SWAT模拟内核,保证对比的公平性。最后比较两个方法的参数排名、计算成本、稳定性和对非线性效应的敏感程度。
我做完这个项目后,预期结论其实很朴素:如果你只是想在率定前筛掉不敏感参数,PAWN往往用更少的模拟次数就能给出一个可靠排名;但如果你还想进一步量化参数交互效应,Sobol的总效应指数依然不可替代。这些判断在后面的章节里我会用具体数据和代码来说明,而不是停留在定性层面。
2. 核心方法原理与选型解析
2.1 Sobol方法:方差分解的经典框架
Sobol方法的理论基础是把模型输出Y的方差Var(Y)分解为各参数及其交互作用的贡献。假设模型输入有k个参数,那么输出方差可以写成:
Var(Y) = Σ_i V_i + Σ_{i<j} V_{ij} + ... + V_{12...k}
其中V_i是参数Xi单独贡献的方差,V_{ij}是Xi和Xj交互产生的方差,以此类推。在此基础上定义两个核心指标:一阶效应指数Si = V_i / Var(Y),度量参数Xi的主效应贡献;总效应指数STi = 1 - V_{~i} / Var(Y),度量Xi自身及其与所有其他参数交互效应的总贡献。
从实操角度看,这两个指标的互补性非常强。Si高说明这个参数单独就能解释输出变化的一大块;如果某个参数Si不高但STi明显高于Si,说明它主要是通过与其他参数交互来影响输出的。在SWAT这种交互关系复杂的模型里,只看Si会漏掉大量信息,所以我建议同时算Si和STi。
实现时普遍采用Saltelli采样方案:生成两个基础样本矩阵A和B,规模都是N×k,然后针对每个参数i构造混合矩阵,把A矩阵的第i列替换成B矩阵的第i列,再用模型批量评估所有矩阵。总模拟次数为N×(2k+2)。我项目里试过不同N值,从几百到上千,经验是N低于300时指数估计波动很大,尤其是STi经常出现负值——这其实是样本不足的标志,不是代码写错了。
Sobol的优点非常突出:理论完备,结果可直接解释,还能量化交互效应。缺点也同样明显:计算成本高,而且方差分解本质上是二阶矩的视角,如果输出分布严重偏态或者存在多峰,Sobol指数可能无法反映那些不影响方差、但影响分布形态的重要参数。
2.2 PAWN方法:基于CDF距离的相对新思路
PAWN方法的出发点很直观:一个参数重不重要,看把它“按住”之后输出分布会不会变。具体做法是把参数Xi的取值范围分成若干个区间(比如10个分箱),在每个区间内固定Xi的取值,其他参数仍然随机变化,计算条件输出CDF;然后把这个条件CDF与所有参数自由变化时的无条件CDF做比较,用Kolmogorov-Smirnov统计量度量两者的最大垂直距离。
对Xi的每个分箱都能算出一个KS距离,最后怎么汇总成单个敏感性指数?Pianosi原版论文推荐取最大值,因为一个参数只要在某个取值区域能对输出分布产生剧烈影响,就应该判定为敏感。不过我在实际项目里发现最大值容易受单次随机采样的波动影响,所以倾向于同时报告最大值和中位数,两个一起看更稳。
PAWN方法的理论门槛比Sobol低,但它有一个很实用的大优势:它考察的是整个CDF的偏移,而不仅仅是方差变化。这意味着一个参数如果主要影响输出分布的尾部形状,比如让洪峰流量的极端值变大,PAWN能捕捉到,而Sobol可能因为尾部对方差的贡献有限而低估它。
Pawn这个名字不是英文缩写,就是方法作者取的名字,有点“小兵也能发挥大作用”的意思。方法虽小巧,但实测下来,在非线性强、输出非正态的SWAT模拟场景里,它的表现相当能打。
2.3 两种方法的本质差异与选型建议
两种方法放在一起对比,可以从几个核心维度看差异。我把它们整理成了表格,方便对照。
| 对比维度 | Sobol方法 | PAWN方法 |
|---|---|---|
| 理论基础 | 输出方差分解 | 条件CDF与无条件CDF的距离 |
| 核心指数 | 一阶指数Si、总效应指数STi | KS统计量的最大值或中位数 |
| 采样需求 | N×(2k+2)次模拟,N通常500-1000 | 无条件样本+各分箱条件样本,总量通常更低 |
| 非线性适应能力 | 中等,对强非线性可能低估尾部影响 | 较强,直接度量分布形态变化 |
| 交互效应量化 | 能通过Si与STi的差值量化 | 没有直接的交互份额指标 |
| 输出分布形态敏感度 | 对偏态/多峰/尾部变化不敏感 | 敏感,这是它的核心优势 |
| 指数取值范围 | 理论上[0,1],负值说明样本不足 | 归一化后(0,1],正值为主 |
| 参考来源 | Sobol 1993, Saltelli et al. 2010 | Pianosi & Wagener 2016 |
选型建议上,我的经验是:如果计算资源相对充裕,且你要写一篇方法学研究论文,Sobol是默认配置,信息量最完整,审稿人也熟悉。如果你是在为一个实际流域率定做准备,目的是快速锁定重点参数,PAWN性价比更高,代码实现也更简单。更务实的做法是两者结合——先用一轮低成本的PAWN粗筛出重要参数,再用Sobol对筛出的参数做精细化分析。这个工作流在后面章节会详细说。
3. Matlab实现全流程实战
3.1 环境准备:把SWAT封装成Matlab可调用的黑箱
SWAT模型在Matlab里不能直接调用——它不是库函数,而是一套独立可执行程序。所以第一步是把它封装成一个标准的函数句柄:输入一组归一化参数向量,输出一个目标标量。这个封装说难不难,但细节很多,是整个项目最容易出问题的地方。
我的封装逻辑是这样的:用一个函数y = runSWAT(theta)接收参数向量,内部先做反归一化,把[0,1]之间的theta映射回每个SWAT参数的真实取值区间;然后把参数写入SWAT的配置文件(比如.txt格式的参数文件或数据库文件);之后通过system()命令调用SWAT的可执行程序,等待模拟完成。最后读取输出文件中的目标变量——我项目里用的是月径流序列,汇总成几个统计量作为函数返回,比如多年平均月径流量,也可以返回洪水频率曲线的高分位值,具体看你要做哪个目标的敏感性分析。
function y = runSWAT(theta, params_meta) % theta: 归一化参数向量 [0,1],长度k % params_meta: 结构体,包含每个参数的真实取值范围 k = length(theta); for i = 1:k % 反归一化到真实参数值 real_val = params_meta(i).min + theta(i) * (params_meta(i).max - params_meta(i).min); writeSWATParam(params_meta(i).name, real_val); end % 执行SWAT模拟 [status, ~] = system('SWAT_Executable.exe'); if status ~= 0 y = NaN; % 模拟失败返回NaN,上层处理 return; end % 读取输出文件 output = readSWATOutput('output.rch'); y = computeObjective(output); % 计算目标统计量 end这段代码逻辑很简单,但有一个重要的细节:writeSWATParam这个子函数怎么实现。SWAT的参数文件格式不统一,有的在文本文件里,有的在Access数据库里,不同版本差异很大。我建议不要用文本替换这种脆弱的方式,而是把参数模板文件复制到独立临时目录,再用Matlab直接读写,模拟完再清理。这样可以避免反复修改原始模型文件,把原始模型弄坏。
封装好之后,整个敏感性分析就变成了一个纯计算问题:你给我样本矩阵,我还你一组对应的输出向量。后面所有代码都建立在这个黑箱之上。
3.2 参数采样与样本库生成
采样是全局敏感性分析的地基。Sobol和PAWN都需要一组高质量样本点来驱动模型运行。参数采样前,需要做两件事:确定参数的取值范围和分布形态。SWAT参数通常给出的是区间范围,没有充分的实测数据支撑时,我习惯使用均匀分布假设——这也和SWAT官方参数文档的默认口径一致。某些参数比如CN2,如果文献里有更具体的先验信息,也可以改成三角分布,但会增加采样代码的复杂度,除非有明确理由,否则不建议一开始就这么干。
Matlab生成两种方案的样本方式有区别。Sobol方法建议用sobolset生成低差异序列,这种准随机序列在高维空间中分布更均匀,能有效减少需要跑的次数:
rng(2024); % 固定随机种子,保证可复现 k = 15; % 参数个数 N = 500; % 基础样本量 p = sobolset(k, 'Skip', 1000, 'Leap', 100); X = net(p, N * (2*k + 2)); % 生成足够样本量 A = X(1:N, :); B = X(N+1:2*N, :); % 后续按Saltelli方案构造混合矩阵PAWN方法的采样稍有不同。它需要一组无条件样本(所有参数自由变化)和若干组条件样本(某个参数固定在特定区间内,其他参数自由变化)。无条件样本可以用lhsdesign生成拉丁超立方样本,分布均匀性也很好。条件样本的生成在下一节详述。
这里有一个非常关键的实操心得:无论用哪种采样方式,都要在一开始保存好完整的参数样本矩阵和对应的随机种子。因为SWAT模拟很耗时,你肯定希望结果可复现,或者在某个步骤出错时不用重新跑全部模拟。我项目里每次生成完样本矩阵就立即存成.mat文件,并且给每个样本分配一个唯一ID,后续缓存结果时全靠这个ID做索引。
3.3 Sobol指数计算:代码框架与要点
Sobol指数计算的核心是Saltelli采样框架下的估计公式。假设已经生成了基础样本矩阵A和B(都是N×k),以及混合矩阵ABi(把A的第i列替换成B的第i列),那么一阶指数和总效应指数的估计实现如下:
function [Si, STi] = computeSobol(model, A, B) N = size(A, 1); k = size(A, 2); % 评估基础矩阵 yA = model(A); yB = model(B); f0 = mean([yA; yB]); VarY = var([yA; yB]); % 输出总方差估计 Si = zeros(k, 1); STi = zeros(k, 1); for i = 1:k ABi = A; ABi(:, i) = B(:, i); % 混合矩阵 yABi = model(ABi); % 一阶指数估计 Si(i) = (1/N) * sum(yB .* (yABi - f0)) / VarY; % 总效应指数估计 STi(i) = 1 - (1/N) * sum(yA .* (yABi - f0)) / VarY; end end代码本身不复杂,但有几个坑必须提醒。第一,上面的公式是Saltelli方案的简化实现,依赖样本量N足够大才能稳定,建议N至少取500,我项目里最后取的是800。第二,如果计算出来的Si或STi出现负值,不要慌,这是典型的样本不足信号,不是方法错了,把N调大即可。第三,model(A)这一步是整个项目的计算瓶颈,一定要并行化。SWAT模拟互相独立,用parfor代替for是立竿见影的加速方式:
yA = zeros(N, 1); parfor idx = 1:N yA(idx) = model(A(idx, :)); end需要注意的是parfor并行调用SWAT时,每个worker不能同时往同一个临时目录写参数文件,否则会出现冲突。我的解决方案是给每个worker分配独立的临时模型目录,模拟完成后再汇总结果。这个坑后面会在常见问题部分展开说。
3.4 PAWN指数计算:代码框架与要点
PAWN方法的代码实现比Sobol更直观,核心就是“分箱-条件采样-算KS距离”三步。先把每个参数的[0,1]区间均匀切成nbins个分箱,通常取8到12个。对第i个参数的第b个分箱,固定这个参数在该箱内均匀取值,其他参数仍按原始分布随机采样,得到一组条件输出Yb。计算Yb的经验CDF与无条件输出Y0的经验CDF之间的最大垂直距离,就是该分箱的KS统计量。对每个参数取所有分箱KS统计量的最大值或中位数,就是PAWN敏感性指数。
function S = computePAWN(model, X0, k, nbins, ns_cond) % X0: 无条件样本矩阵,N0×k % nbins: 分箱数 % ns_cond: 每个分箱内的条件样本数 Y0 = zeros(size(X0, 1), 1); parfor idx = 1:size(X0, 1) Y0(idx) = model(X0(idx, :)); end S = zeros(k, 1); for i = 1:k KS_vals = zeros(nbins, 1); for b = 1:nbins % 生成条件样本:第i列固定在分箱b内 Xb = lhsdesign(ns_cond, k); % 其他参数自由采样 lo = (b-1) / nbins; hi = b / nbins; Xb(:, i) = lo + (hi - lo) * rand(ns_cond, 1); % 第i列固定在箱内 Yb = zeros(ns_cond, 1); parfor idx = 1:ns_cond Yb(idx) = model(Xb(idx, :)); end % 计算KS统计量 KS_vals(b) = max(abs(ecdf(Yb) - ecdf(Y0))); end S(i) = median(KS_vals); % 用中位数更稳定 end end这段代码有几个要注意的地方。无条件样本Y0只需要算一次,可以复用,这是PAWN的一个隐藏优点——如果你后续改了分箱数或者条件样本量,Y0不用重新算。条件样本生成时,第i列要严格落在当前分箱区间内,但其他参数必须保持全空间分布,这样才能保证你“只改变了这一个参数的条件”,而不是同时改变了多个条件。如果这个细节没做对,PAWN算出来的指数会失真。
另一个实操细节是分布的拟合方式。上面代码里直接用ecdf计算经验累积分布函数,简单粗暴但可靠。你要是对CDF的光滑性有要求,也可以先用ksdensity拟合光滑CDF再求距离,但我实测下来对最终排序影响很小,反而增加了计算量,得不偿失。
3.5 结果可视化与排序对比
敏感性分析的计算结果最终要落到可视化和决策上。我的标准做法是画两类图:一类是Sobol的Si和STi条形图,一类是PAWN指数的条形图,然后并列排列做对比。
subplot(1,2,1); bar([Si, STi]); legend({'Si', 'STi'}); ylabel('Sobol指数'); set(gca, 'XTickLabel', param_names); title('Sobol敏感性指数'); subplot(1,2,2); bar(S_pawn); ylabel('PAWN指数'); set(gca, 'XTickLabel', param_names); title('PAWN敏感性指数');可视化之后要做的第一件事不是急着下结论,而是看两件事:第一,两个方法的Top参数排名重合度有多高;第二,是否有参数在Sobol里排名靠后但PAWN里排名靠前,反之亦然。在我做的案例里,Top5参数有4个重合,不重合的那个参数恰恰就是最能体现两种方法差异的——它对输出分布尾部有明显影响,但对输出方差贡献很小。这个参数才是你后续率定时需要特别关注的,因为它直接决定模型的极值预测能力。
4. 常见问题与排查技巧实录
4.1 计算成本失控怎么办
SWAT单次模拟的耗时决定了整个项目的预算。我项目里一个中等流域模型,月尺度模拟一次大约40秒,Sobol方法在N=800、15个参数时总共要跑800×(2×15+2)=25600次,单核跑就是近12天,不并行根本没法操作。PAWN方案设置nbins=10、ns_cond=100、无条件样本N0=1000时,总模拟次数是1000+15×10×100=16000次,成本低40%左右,而且很多时候还能再压缩。
计算成本控制的几个策略供参考:第一,并行优先,8核parfor能直接除以8,这是最大的加速项;第二,缓存中间结果,同样的参数组合如果已经在之前的试验中算过,直接读缓存,不要重复跑;第三,先粗后细,先用较小的N或者ns_cond跑一轮识别出明显不敏感的参数,把它们剔除后用更精细的样本量做第二轮。
| 方法 | 参数个数k | 基础样本量 | 总模拟次数 | 单次40秒估算耗时 |
|---|---|---|---|---|
| Sobol Saltelli | 15 | N=800 | 25600 | 8核并行约35小时 |
| PAWN | 15 | N0=1000, ns=100, nbins=10 | 16000 | 8核并行约22小时 |
| PAWN(精简) | 15 | N0=500, ns=50, nbins=10 | 8000 | 8核并行约11小时 |
4.2 Sobol指数出现负值或NaN怎么排查
Sobol指数理论值应该在[0,1]区间,但实际计算时负值和NaN都是老朋友了。负值的唯一原因就是样本量不足,估计量方差过大。解决办法不是改公式,而是增加N。我在项目里做过一个实验:N=100时STi负值出现概率还挺高,N=500时基本消失,N=800时稳定。
NaN的情况就要多长个心眼了。NaN通常来自SWAT模拟失败——比如某些参数组合导致模型数值不收敛,输出文件里出现负数流量或者直接崩溃。我的处理方式是在runSWAT函数里用try/catch捕获异常,模拟失败返回一个很大的惩罚值,比如正常输出量级的100倍。这样不会让整个分析崩溃,但要注意这个惩罚值会影响敏感性指数估计,所以如果失败样本比例超过5%,就应该检查是不是有参数范围设置不合理,而不是硬着头皮继续跑。
function y = runSWAT_safe(theta, params_meta) try y = runSWAT(theta, params_meta); if isnan(y) || isinf(y) y = 1e8; % 惩罚值 end catch y = 1e8; % 捕获异常 end end4.3 两种方法排序不一致时怎么解释
做对比实验必然会遇到一个场景:某个参数在Sobol里排在前面,但PAWN给了很低的指数;或者反过来。最先要怀疑的是计算稳定性——样本量是否足够,随机种子是否影响了结果。我建议如果出现大的排序不一致,先把两个方法各自的样本量增加50%,看排名是否稳定。如果稳定了还不见一致,这就不是偶然误差,而是方法学上的真实差异。
这种差异的本质理由往往落在两个方向上。第一种情况:Sobol高但PAWN低,说明该参数主要作用于输出的均值或方差,但分布形态不随它变化。第二种情况:PAWN高但Sobol低,说明该参数对输出分布的某些部分影响很大,比如让尾部更厚、让分布变成多峰,但没有带来显著的方差贡献。后者这种参数,如果目标是洪水、干旱这类极值事件,恰恰是重点对象。所以在解释不一致时,先回到目标定义——你的模型输出是要服务什么决策,是平均水资源量还是极端洪水风险?这个答案决定了你应该更相信哪个方法的结果。
4.4 Matlab实现里容易翻车的几个细节
一个是parfor和SWAT临时目录冲突。我用parfor并行跑SWAT时,最初所有worker共享同一个模型目录,结果跑着跑着输出文件互相覆盖,参数写入错乱,数据分析出来完全没法用。后来改成每个worker用tempname创建独立目录,复制模型文件进去,模拟完再读结果、清理目录,问题彻底解决。
另一个是文件名的唯一性问题。SWAT运行会产生固定文件名的输出,比如output.rch,并行时如果不做目录隔离,不同样本的结果会把同一个文件覆盖掉,读回来全是错的。这个问题我上面说了,本质还是目录隔离。
还有一个容易忽略的细节:固定随机种子。无论是Sobol采样里的sobolset还是PAWN里的lhsdesign和rand,都要在脚本开头设置rng(seed)。如果没有固定种子,下次重跑整个分析时样本完全不一样,你都没法判断结果变化是模型原因还是采样随机性原因。这个习惯能帮你省掉大量排查时间。
参数范围设置也值得单独说。SWAT参数的范围如果设得太宽,采样时会产生大量模型不收敛的参数组合;设得太窄,又会漏掉有意义的参数空间。我建议参考SWAT官方文档和已发表文献的推荐范围,先做一个小规模的预测试,检查一下模型在极端参数组合下是否还能正常跑通,再决定正式范围。
5. 个人实操心得与扩展建议
5.1 一套可复用的“先粗后细”工作流
经过这个项目,我总结出一套成本可控、结论可靠的实操工作流,特别适合SWAT这类高参数化模型。第一步,用一轮精简版PAWN做粗筛:无条件样本N0取300,分箱数取8,每个分箱条件样本取50,把参数按PAWN指数排序,直接砍掉排名后一半的参数。第二步,对剩下的参数重新设定抽样范围,用Sobol完整跑一轮,N取500到800。第三步,对比Sobol的Si和STi,重点关注STi明显大于Si的参数——这些是交互效应大户,率定时要特别小心不能单独调。第四步,把Sobol排名前10的参数作为最终率定参数集。
这套流程的成本是粗筛约300+15×8×50=6300次模拟,精算约8×(2×10+2)=1760次模拟,合计8000多次,8核并行下一天多就能跑完。相比直接对全部参数跑完整Sobol,能省下好几天的计算时间,而且因为粗筛阶段用的PAWN对非线性效应敏感,不太会在第一步就漏掉重要参数。
5.2 后续扩展方向与工具链延伸
这个项目做完后,我明显感觉到几个可以继续深挖的方向。一个是在同一个框架里加入Morris筛选法做三方法对比,Morris计算成本更低,可以作为更快速的第一道筛子,把参数空间从几十个压到十几个,再上PAWN或Sobol。另一个是把目标输出从单一统计量扩展成多目标——同时分析月径流均值、峰流量和基流量的敏感性,看哪些参数是“全局重要”的,哪些只在特定目标下重要,这对面向不同管理决策的模型率定很有价值。
从工具链角度看,Matlab作为胶水层的优势在项目里体现得很充分:矩阵采样、并行计算、绘图一体化,不用在Python和R之间来回切换。如果你的SWAT版本支持Python调用,也可以考虑用Python里的SALib库做采样,然后仍然用Matlab做模型调度和可视化,两边互补也不错。不过对我个人来说,一个Matlab脚本从头做到尾,维护成本最低,出图也最方便。
最后说一句实在话:不要迷信任何一种敏感性分析方法。Sobol和PAWN各有各的视角,Sobol看方差,PAWN看分布,一个参数在某个方法里不敏感不代表它对模型不重要。多做一轮交叉验证,多从目标本身去思考“敏感”的定义,比争论哪个方法更高级有价值得多。