
第一次把DEMATEL和ISM放在一起做分析的人大概率会经历两个阶段先是被它们能解决的问题吸引再是被一堆公式和矩阵运算劝退。DEMATEL能告诉你哪些因素起决定性影响、哪些因素只是被动接受影响ISM能把十几个甚至几十个因素整理成有层次的结构图。两个方法配合起来既能量化影响强度又能看清系统骨架在供应商评价、安全风险分析、物流管理、教育质量评估这些主题里都很常见。可惜大部分资料只给公式或者截一段根本跑不通的代码。这篇文章把我自己复现DEMATEL-ISM模型的完整过程记录下来从原理拆解到Python代码实现最后用一个可复现的小例子跑通全流程希望能给正在做相关课题研究或者项目分析的朋友省点时间。1. DEMATEL与ISM的原理梳理1.1 DEMATEL量化因素间的相互影响DEMATEL全称Decision Making Trial and Evaluation Laboratory决策试验与评价实验室法上世纪70年代起源于Battelle研究所。它的核心任务是回答一个问题在一个系统里因素A到底是通过什么路径、以什么样的强度影响因素B的DEMATEL的输入是直接影响矩阵A元素a[i][j]表示因素i对因素j的直接影响力通常按0到3或者0到4打分0代表没有影响数值越大代表影响越强。实际操作中一般找领域专家逐对打分回收问卷后取平均得到最终的矩阵。对于n个因素A就是一个n行n列的矩阵行代表发出影响的因素列代表接收影响的因素对角线一般设为0不讨论因素对自身的直接影响。有了直接影响矩阵之后DEMATEL会做一次归一化处理。把矩阵A的每一行求和找到所有行和中的最大值然后用这个最大值去除矩阵中的全部元素得到规范化矩阵X。这种归一化方式保证了X的每一行和都不超过1数学上为后续矩阵级数的收敛创造了条件。接着计算综合影响矩阵TT X(I-X)^(-1)这里I是单位矩阵。T的每一项代表因素i对因素j的综合影响程度包括直接路径和各级间接路径。理解这个公式有一个特别直观的角度如果把X看作一步直接影响的传递矩阵那么X²就是两步间接影响X³就是三步间接影响把所有路径的贡献全部加起来就是综合影响矩阵也就是等比级数XX²X³...。只要行和小于1这个级数就一定收敛用逆矩阵一步算出来比循环几百次迭代要可靠得多。有了T矩阵以后分别计算行和与列和。行和D称为影响度表示该因素对外输出影响的总量列和R称为被影响度表示该因素接收外部影响的总量。两个指标相加得到中心度DR因素在系统里的活跃程度和重要程度越高中心度往往越大相减得到原因度D-R大于0是原因因素也就是影响别人的因素小于0是结果因素也就是更依赖别人的因素。1.2 ISM把模糊关系变成层次结构ISM全称Interpretive Structural Modeling解释结构模型由Warfield在1974年提出。它做的事情和DEMATEL完全不同DEMATEL关心“影响有多强”ISM关心“谁是源头、谁是终端、系统一层一层长什么样”。ISM的输入通常是可达矩阵一个由0和1构成的n阶布尔矩阵。矩阵里某一行i、某一列j位置为1表示从因素i出发可以通过直接或间接路径到达因素j通常认为因素自身可达自身所以对角线一般置1。从可达矩阵出发ISM定义一个关键变量可达集和先行集。对于任意因素i可达集R(Si)是所有被i影响的因素集合包括自身先行集Q(Si)是所有能影响i的因素集合也包括自身两者取交集得到C(Si)。如果一个因素的C(Si)恰好等于R(Si)说明它能够影响到的所有因素都能反过来追溯到它它就处在当前子系统的顶层也就是结果端。把这一层因素抽出来在矩阵中删除它们对应的行和列对剩余因素重复同样步骤就能逐层剥出整个系统的递阶结构。这个过程有点像一个团队里理清上下级关系先找到那些只受别人安排、不再往下派活的成员把他们排为第一层然后从团队名单里去掉在剩下的人里继续找“不作为上游”的人循环往复最终把整条汇报链路还原出来。1.3 两个方法为什么要结合着用单独用DEMATEL你得到的是每个因素的影响度、被影响度、中心度、原因度信息很全但只能停留在“谁强谁弱”的层面没法回答“系统是怎么组织的”这个问题。单独用ISM你可以画出一张清晰的层次结构图但输入的可达矩阵通常来自专家直接打分的0/1关系比较粗糙而且专家打分时经常遇到“到底算直接关系还是间接关系”的困惑。DEMATEL-ISM组合方式正是为了取长补短。先用DEMATEL求出综合影响矩阵T这个矩阵已经把直接和间接影响都整合进去了再通过设定阈值把T矩阵中的连续数值转换成0/1布尔矩阵作为ISM的输入做层级划分。这样一来既保留了DEMATEL的信息整合能力又能输出ISM那套直观的层次结构学术论文和实际咨询报告里经常见到这条技术路线。2. 模型整体流程设计与核心思路2.1 一条主线从直接影响矩阵到系统层次图整个DEMATEL-ISM建模过程可以拆成七个环节。确定因素体系设计专家问卷收集专家打分得到直接影响矩阵A。对A做行和最大值归一化得到矩阵X。计算综合影响矩阵T X(I-X)^(-1)。计算影响度D、被影响度R、中心度DR、原因度D-R初步判断因素重要性。设定阈值λ将T矩阵中大于等于λ的元素置1其余置0得到初始可达矩阵并把对角线置1。对可达矩阵做传递闭包运算或者直接用布尔化结果交给ISM做层级抽取。输出分层结果结合原因度、中心度一起解读绘制递阶结构图。步骤5是整个流程里最需要经验的地方。阈值λ选得小可达矩阵里1的数量就多分层结果可能只剩一两层阈值λ选得大1的数量就少因素之间关系稀疏分层会特别碎甚至出现大量孤立因素。建议先用T矩阵的均值作为基准阈值跑一遍再尝试均值加0.5倍标准差、均值加1倍标准差比较不同阈值下的层级稳定性。如果层级结构在多个阈值下保持一致说明模型结论比较稳健。2.2 阈值λ的设定思路为什么关键阈值设定的本质是在“保留有用关系”和“过滤噪声关系”之间找平衡。综合影响矩阵T里的元素是小数比如0.03可能来自一条传播了很多步的间接路径在现实中虽然有影响但已经弱到可以忽略。如果把这些微弱关系全部保留ISM分层会把几乎所有因素都识别成互相可达层次自然出不来。反过来如果阈值设得太激进连重要关系都过滤掉了因素之间被切得七零八落同样得不到有意义的层次结构。我给一个操作经验第一次跑的时候先打印T矩阵的均值、标准差、最大值、最小值心里有个数。均值比较小的时候比如0.1左右可以先从均值开始如果T矩阵整体数值偏大比如均值超过0.2可以尝试均值加标准差。最终选定的阈值一定要在论文或者报告里写清楚因为不同阈值会直接影响可达矩阵和层级划分这是方法本身的人为选择不是模型缺陷。2.3 与纯ISM相比这条路线有什么取舍如果完全按照ISM的原始定义专家需要直接给出因素之间的布尔关系也就是0/1判断。实际填问卷的时候专家经常会在“有影响”和“没有影响”之间犹豫而且0/1判断丢掉了很多影响强度的信息。DEMATEL-ISM组合避开了这个问题专家只需要按0到3或者0到4打分强度信息更丰富后续还能顺便算中心度和原因度。代价也很明显从连续阈值到布尔矩阵的转换过程引入了一个主观参数λ不同的人可能选不同的阈值结果也会不同。所以复现时不要只跑一个阈值就下结论最好多试几个值把结果差异控制在可解释范围内。3. Python环境准备与数据组织3.1 环境配置只需要numpy和pandas这个模型对Python版本没有特别苛刻的要求Python 3.8以上版本都可以顺畅运行核心依赖只有三个numpy负责矩阵运算pandas负责结果展示如果后面想画原因度-中心度散点图再装一个matplotlib就行。命令行里执行下面的安装命令即可pip install numpy pandas matplotlib如果电脑里还没有Python环境建议直接去Python官网下载安装包安装时勾选Add Python to PATH然后用VS Code或者PyCharm写脚本。第一次接触Python的读者建议用Anaconda自带numpy、pandas、matplotlib等常用库可以省掉不少配置的麻烦。装完之后可以跑一行简单代码验证环境是否正常import numpy as np import pandas as pd print(np.__version__) print(pd.__version__)能正常输出版本号就说明环境没有问题。3.2 输入数据的结构设计复现模型之前先把输入数据结构搞清楚。直接影响矩阵A用一个二维列表或者numpy数组表示同时准备一个因素名称列表labels顺序和矩阵的行列顺序保持一致。比如有四个因素分别叫“信息系统建设”“管理制度完善”“人员技能水平”“绩效结果”那么labels就是[信息系统建设, 管理制度完善, 人员技能水平, 绩效结果]A就是一个4×4矩阵。收集数据方面要特别留意一点问卷打分一定要定义清楚方向。a[i][j]是第i个因素对第j个因素的影响行是施动方列是受动方。实际处理问卷时经常看到有人把行和列方向搞反导致最后算出来的中心度和原因度完全错位这一点怎么强调都不为过。4. 代码复现用Python一步步实现DEMATEL-ISM4.1 DEMATEL计算模块先写DEMATEL的核心计算函数。输入是直接影响矩阵A和因素名称列表labels输出是综合影响矩阵T和分析指标DataFrame。import numpy as np import pandas as pd def dematel_compute(A, labels): A np.array(A, dtypefloat) n A.shape[0] # 1. 归一化每一行求和找到最大值用最大值去除所有元素 row_sums A.sum(axis1) max_row_sum np.max(row_sums) X A / max_row_sum print(行和最大值:, max_row_sum) # 2. 综合影响矩阵 T X(I-X)^{-1} I np.eye(n) try: inv np.linalg.inv(I - X) except np.linalg.LinAlgError: raise ValueError(I-X 不可逆请检查直接影响矩阵的数值设置) T X inv # 3. 影响度D、被影响度R、中心度DR、原因度D-R D T.sum(axis1) R T.sum(axis0) center D R cause D - R # 4. 汇总成DataFrame方便查看 df pd.DataFrame({ 因素: labels, 影响度D: D, 被影响度R: R, 中心度DR: center, 原因度D-R: cause }) return T, df这段代码里最核心的是归一化方式。用所有行和的最大值去除每一个元素而不是用每行自己的和去除是为了让整个矩阵共享同一个缩放尺度这样计算出来的综合影响矩阵才具有可比性。4.2 ISM层级划分模块接下来实现ISM的核心逻辑。先实现一个布尔矩阵乘法用于传递闭包运算然后实现层级抽取函数。def bool_mul(A, B): 布尔矩阵乘法结果中1表示存在至少一条通路 return (A B 0).astype(int) def reachability_matrix(T, threshold, labelsNone, add_identityTrue, transitive_closureTrue): 根据阈值把综合影响矩阵T转为可达矩阵K n len(T) # 大于等于阈值的置1其余置0 B np.where(T threshold, 1, 0) if add_identity: np.fill_diagonal(B, 1) # 可选做传递闭包让间接可达关系也体现出来 if transitive_closure: for _ in range(n): C bool_mul(B, B) if np.array_equal(C, B): break B C return B.astype(int)层级抽取函数是整个模型里最容易写错的地方。核心逻辑不再重复阐述直接看代码def ism_levels(reach_mat, labels): n len(reach_mat) remaining list(range(n)) level_list [] while remaining: current_level [] for i in remaining: # 可达集从i出发能够到达的因素只考虑尚未分层的因素 reachable {j for j in remaining if reach_mat[i, j] 1} # 先行集能够到达i的因素只考虑尚未分层的因素 antecedent {j for j in remaining if reach_mat[j, i] 1} inter reachable antecedent # 如果交集等于可达集说明i没有能力触达更上层因素属于当前最顶层 if inter reachable: current_level.append(i) if not current_level: print(警告本轮没有抽取到任何因素请检查可达矩阵或调整阈值) break level_list.append(current_level) for i in current_level: remaining.remove(i) # 把索引转换为因素名称 result [] for level in level_list: result.append([labels[i] for i in level]) return result这里有一个特别容易踩的坑每一轮计算可达集和先行集时要暂时忽略已经被抽走的因素。有些实现直接在原始可达矩阵上计算集合不剔除已经分层的因素导致后续层级的判定出现偏差。我上面用remaining这个列表做了过滤可以避免这个问题。4.3 完整可运行源码把两个模块串起来一个最小可复现的脚本就出来了。我用一个四因素小案例演示。import numpy as np import pandas as pd def dematel_compute(A, labels): A np.array(A, dtypefloat) n A.shape[0] row_sums A.sum(axis1) max_row_sum np.max(row_sums) X A / max_row_sum print(行和最大值:, max_row_sum) I np.eye(n) try: inv np.linalg.inv(I - X) except np.linalg.LinAlgError: raise ValueError(I-X 不可逆请检查直接影响矩阵的数值设置) T X inv D T.sum(axis1) R T.sum(axis0) center D R cause D - R df pd.DataFrame({ 因素: labels, 影响度D: D, 被影响度R: R, 中心度DR: center, 原因度D-R: cause }) return T, df def bool_mul(A, B): return (A B 0).astype(int) def reachability_matrix(T, threshold, add_identityTrue, transitive_closureTrue): n len(T) B np.where(T threshold, 1, 0) if add_identity: np.fill_diagonal(B, 1) if transitive_closure: for _ in range(n): C bool_mul(B, B) if np.array_equal(C, B): break B C return B.astype(int) def ism_levels(reach_mat, labels): n len(reach_mat) remaining list(range(n)) level_list [] while remaining: current_level [] for i in remaining: reachable {j for j in remaining if reach_mat[i, j] 1} antecedent {j for j in remaining if reach_mat[j, i] 1} inter reachable antecedent if inter reachable: current_level.append(i) if not current_level: print(警告本轮没有抽取到任何因素请检查可达矩阵或调整阈值) break level_list.append(current_level) for i in current_level: remaining.remove(i) result [] for level in level_list: result.append([labels[i] for i in level]) return result if __name__ __main__: labels [信息系统建设, 管理制度完善, 人员技能水平, 绩效结果] A np.array([ [0, 2, 3, 1], [1, 0, 2, 3], [0, 1, 0, 2], [0, 0, 1, 0] ], dtypefloat) T, df dematel_compute(A, labels) print(\n综合影响矩阵 T) print(pd.DataFrame(T, indexlabels, columnslabels).round(4)) print(\n因素指标汇总) print(df.round(4)) # 阈值根据实际情况调整这里为了演示手动手设为0.3 threshold 0.3 print(\n阈值λ:, threshold) K reachability_matrix(T, threshold, add_identityTrue, transitive_closureTrue) print(\n可达矩阵 K) print(pd.DataFrame(K, indexlabels, columnslabels)) levels ism_levels(K, labels) print(\nISM层次划分结果) for idx, level in enumerate(levels, 1): print(f第{idx}层: {level})这个脚本直接复制到Python文件里运行就能出结果。numpy和pandas版本只要不是太老一般不会报错。如果运行时报中文编码相关的错误在文件第一行加上# -*- coding: utf-8 -*-或者把文件保存为UTF-8编码即可。4.4 代码输出格式设计我把输出设计成三个部分综合影响矩阵、因素指标汇总、ISM层级结果。综合影响矩阵方便核对中间计算因素指标汇总给论文分析用层级结果给结构化建模用。实际做项目的时候建议把因素指标表输出成CSV方便后续处理df.to_csv(dematel_result.csv, indexFalse, encodingutf-8-sig)用utf-8-sig编码是为了让Excel直接打开时中文不乱码这个小细节经常被忽略。5. 案例演示一个四因素小案例5.1 指标设定与直接影响矩阵案例背景简单一点某家公司想梳理影响绩效结果的核心因素定义了四个因素F1信息系统建设F2管理制度完善F3人员技能水平F4绩效结果专家打分按照0到3分制0代表无影响1代表弱影响2代表中等影响3代表强影响。最终取平均后得到直接影响矩阵F1 F2 F3 F4 F1 0 2 3 1 F2 1 0 2 3 F3 0 1 0 2 F4 0 0 1 0这个矩阵的含义是信息系统建设对管理制度完善有中等影响对人员技能水平有强影响对绩效结果有弱影响管理制度完善对人员技能水平有中等影响对绩效结果有强影响人员技能水平对绩效结果有中等影响。5.2 运行结果与层级输出运行脚本后代码会依次输出综合影响矩阵、因素指标汇总、可达矩阵和层级结果。这里重点看层级划分在我的一次运行中阈值λ0.3ISM分层如下第1层绩效结果第2层人员技能水平第3层信息系统建设、管理制度完善这个结果和业务逻辑高度吻合。绩效结果处于系统最顶层是被生产出来的结果变量人员技能水平在中间层扮演传导角色信息系统建设和管理制度完善处在底层作为根源因素互相促进共同驱动整个系统运转。5.3 结果解读与决策价值结合DEMATEL计算的中心度和原因度来看这套结果可以给出很明确的决策建议。信息系统建设和管理制度完善如果原因度为正且中心度较高就说明它们是系统的“引擎”做管理优化的资源应该优先投入到这两个方面。人员技能水平作为中间传导层需要靠制度和信息系统来带动绩效结果作为结果变量反应最敏感但却是被动的直接考核绩效而不改善上游因素效果非常有限。实际项目里因素数量通常在10个以上比如供应链风险分析动辄15个指标ISM分层会分出3到5层解读逻辑完全一致底层因素需要优先治理顶层因素更适合作为衡量指标和阶段性目标。6. 常见问题与排查技巧实录6.1 矩阵不可逆怎么办计算T矩阵时如果报LinAlgError说明I-X接近奇异矩阵最常见的原因是直接影响矩阵A里出现了异常大的数值导致归一化后X的谱半径接近1。解决办法是重新检查原始打分看是不是有人填了极端值或者打分等级制不一致。另一种情况是因素之间有重复的完全拟合关系比如两个因素其实描述的是同一件事矩阵里会出现多重共线性这时需要合并因素。6.2 ISM分层结果不理想怎么调阈值分层结果一般有三种不理想的情况全是同一层、层数太多太碎、出现大量孤立因素。表现阈值方向处理建议全部因素集中在同一层阈值偏小增大阈值过滤弱影响关系每个因素单独一层阈值偏大减小阈值保留更多合理关系大量因素孤立无联系阈值过大或者矩阵本身稀疏减小阈值或者检查原始矩阵是否有误建议多跑几个阈值把层级结构稳定作为选择标准而不是只看单次运行结果。6.3 ISM分层出现死循环怎么办我在代码里加了保护逻辑如果某轮一个因素都抽不出来就自动终止并打印警告。出现这种情况几乎都是可达矩阵的问题最常见的是所有因素之间都没有强连通关系导致没有任何因素满足“交集等于可达集”的条件。把阈值调小一点多保留一些可达关系通常就能解决。6.4 计算结果和Excel对不上Excel里计算综合影响矩阵时很多人用MMULT函数手写矩阵乘法容易出现转置错误。Python代码里T.sum(axis1)是沿行加和得到影响度DT.sum(axis0)是沿列加和得到被影响度R。Excel里如果行和列看反了中心度和原因度符号都会反转检查时优先核对D和R的方向。6.5 打印中文乱码Windows终端下运行Python经常出现中文乱码。最稳妥的办法是代码文件用UTF-8编码保存然后在打印前加一句import sys sys.stdout.reconfigure(encodingutf-8)如果还是乱码把结果用df.to_csv输出到文件里看不依赖终端界面展示。6.6 因素数量多会不会跑很慢我测试过20个因素的矩阵DEMATEL逆矩阵计算和ISM层级划分都是毫秒级别完全不用担心性能。真正耗时的是专家打分和数据整理Python计算部分基本可以忽略不计。最后再分享一个实际操作中的心得跑完结果不要急着画图先把中心度、原因度和ISM层级放一张表里对照着看。原因度为负但处于ISM底层或者原因度为正但被划分到顶层这些“矛盾”情况往往提示因素体系设计有问题要么是因素定义有重叠要么是专家打分逻辑不一致。模型复现只是第一步能用业务逻辑把计算结果解释通才是真正把DEMATEL-ISM用起来了。