
簡介DEMATEL決策試驗與評價實驗室方法是一套用于復雜系統因果關系分析的決策工具常應用于系統工程、供應鏈管理、風險評估等場景這份壓縮包提供該方法在MATLAB與Python兩種環境下的完整實現源碼面向高校師生、科研人員及需要開展因素分析或決策支持的實踐者。資源共100個文件以34個Python腳本、28個MATLAB腳本和29個Python編譯緩存文件為主體另含Excel表格、Markdown說明及MAT數據文件整體僅227KB輕量便攜。代碼覆蓋初始評價矩陣構建、總影響度與依賴度計算、因果關系圖繪制、敏感性分析等核心環節并帶示例數據可直接運行或修改復用兩種語言實現便于對照學習不同編程生態下的算法差異。目前已有2385人學習/下載適合正在研究多準則決策、系統因素分析或希望快速上手DEMATEL編程實現的讀者參考。1. DEMATEL方法為什么值得用MATLAB和Python各寫一遍做過多準則決策分析的人都知道DEMATELDecision Making Trial and Evaluation Laboratory決策試驗與評價實驗室是少數能把專家主觀判斷轉成結構化因果關系的工具。它不要求大樣本數據也不需要復雜的統計假設只要一張因素間兩兩影響程度的打分表就能算出每個因素的影響度、被影響度、中心度和原因度最終畫出一張因果圖。這套邏輯在供應鏈風險識別、數字化轉型障礙分析、工程項目安全評價里反復出現屬于那種“原理不難、算起來瑣碎、不親自跑一遍就總覺得不踏實”的方法。于是問題就變成了用MATLAB還是用Python實現答案是都值得寫一遍。MATLAB在矩陣運算和可視化上天然順手適合快速驗證Python在數據清洗、工程集成和后續復用上更靈活。把兩套實現放進同一個zip壓縮包本質上是在為同一個算法維護雙語言接口。本文按“輸入數據 → 核心計算 → 參數調優 → 工程打包”的順序把這條鏈路完整跑通。2. 直接影響矩陣構建與規范化DEMATEL的數據輸入關2.1 專家打分表如何組織成n階方陣DEMATEL的第一步是構造直接影響矩陣習慣記作A。假設你評估8個影響因素那么A就是一個8×8矩陣元素a_ij表示因素i對因素j的直接影響強度對角線a_ii恒為0。打分通常采用0—4量表0代表無影響1代表低影響2代表中影響3代表高影響4代表極高影響。常見的組織方式是拿一張Excel或CSV表行是影響源因素列是被影響因素單元格填打分值。多專家打分時一般取算術平均或幾何平均合并成一份矩陣。這里有一個容易被忽略的細節平均之前必須先做一致性篩查。比如同一個矩陣里出現“i對j打4分j對i也打4分”就需要警惕——兩個因素互相極強影響在現實里極少見多半是問卷理解偏差。我通常的做法是先把所有打分矩陣的事例逐個做Check凡是雙向都大于3的回到原始問卷核對備注。2.1.1 打分數據的CSV格式約定一份可以直接喂給代碼的打分矩陣CSV長這樣factor,f1,f2,f3,f4,f5,f6,f7,f8 f1,0,3,2,1,4,2,1,0 f2,2,0,3,1,2,1,0,1 f3,1,2,0,3,2,1,1,0 f4,0,1,2,0,3,2,1,1 f5,3,2,1,2,0,3,2,0 f6,2,1,1,3,2,0,2,1 f7,1,0,2,1,1,2,0,3 f8,0,1,1,2,0,1,3,0第一列是行索引第一行是列索引中間區域必須全部是數值。如果只有打分矩陣沒有因素名也可以用數字索引。需要注意CSV編碼統一用UTF-8避免中文字段名在MATLAB的readtable和Python的pandas之間來回切換時出現亂碼。我不建議在因素名里帶空格或特殊符號這些字符會在后續繪圖時變成莫名其妙的圖例標簽。2.2 規范化處理的兩種策略與選型理由直接影響矩陣A不能直接做綜合影響計算因為A的元素累加可能超過1會導致后續級數不收斂。規范化的目標就是讓矩陣的某種范數收縮到1以內。常用的有兩種行和最大值規范化先對A的每一行求和取所有行和的最大值max_row_sum令X A / max_row_sum。這種策略保留了行方向上的相對強度適合因素i對整體影響差異顯著的場景。列和最大值規范化對每列求和取最大值max_col_sum令X A / max_col_sum。它更關注被影響側的差異適合被影響程度懸殊的場景。大多數論文默認采用行和最大值法因為它的計算形式簡單而且綜合影響矩陣T的數值范圍恰好落在0到1附近閾值設定比較好描述。值得注意的是如果打分矩陣極度不平衡例如某一行全是4、其他行全是0和1行和最大值法會把這一行的數值壓得過低導致該因素的綜合影響被低估。這種情況下我傾向于先做列規范化對比一下結果哪個穩定用哪個。2.3 Python側的數據預處理pandas讀入與校驗Python實現里數據讀入和校驗通常用pandas完成。下面這段代碼會讀取CSV、剝離索引列、執行三個基本校驗import pandas as pd import numpy as np df pd.read_csv(impact_matrix.csv, index_col0) A df.to_numpy(dtypenp.float64) n A.shape[0] assert A.shape[1] n, 矩陣不是方陣 # 校驗對角線必須為0 diag np.diag(A) if not np.allclose(diag, 0): print(警告: 對角線存在非零值自動置零) np.fill_diagonal(A, 0) # 校驗取值在0-4量表內 if (A 0).any() or (A 4).any(): print(警告: 存在超出0-4打分范圍的元素請檢查原始數據)這段代碼在正式計算之前把所有輸入問題暴露出來比跑完算法再回頭排查高效得多。矩陣不是方陣這一條看似低級實際在從Excel手工復制數據時非常容易發生定位方法就是打印A.shape看行列數。打分范圍校驗也值得保留因為有些問卷設計用的是1—5量表混用0—4和1—5會導致計算結果徹底失真。2.3.1 CSV導入MATLAB的等價做法MATLAB側的讀入用readmatrix或readtable都行readmatrix更直觀A readmatrix(impact_matrix.csv); A A(:, 2:end); % 去掉第一列因素名 A(1, :) []; % 去掉第一行列名readmatrix會把純數值區域讀成矩陣但遇到第一行第一列是文本時會產生NaN。所以去掉行列名之后還要檢查一次assert(~any(isnan(A(:))), 矩陣中存在NaN請檢查CSV格式);MATLAB的矩陣索引從1開始這里的切片操作在Python里對應的是iloc[1:, 1:]兩種語言在邊界處理上的差異正好是雙語言實現最容易踩的坑。如果你準備把同一份數據在兩邊反復跑建議統一先清洗成純數值CSV不帶行列名的版本作為中間交換格式。3. DEMATEL核心矩陣運算MATLAB與Python雙語言實現3.1 算法主流程的四步運算拆解從規范化矩陣X到綜合影響矩陣T數學上需要計算無窮級數X X2 X3 ...。當X的譜半徑小于1時這個級數收斂于X(I?X)^(?1)。這一步是整個DEMATEL算法的核心也是初學者最容易在“為什么不能直接用inv”上犯迷糊的地方。完整流程分四步規范化X A / max(sum(A, axis1))綜合影響矩陣T X inv(I ? X)行和與列和D_i Σ_j T_ijR_j Σ_i T_ij衍生指標中心度M_i D_i R_i原因度N_i D_i ? R_i3.1.1 綜合影響矩陣的可逆性檢查inv(I ? X)要求I ? X非奇異。實際數據里X的每個元素都小于1且行和不超過1I ? X通常是嚴格對角占優的可逆性基本有保證。但在極端數據下仍建議先求特征值確認譜半徑小于1eigenvalues np.linalg.eigvals(X) radius np.max(np.abs(eigenvalues)) print(f譜半徑: {radius:.4f}) assert radius 1, 譜半徑不小于1規范化和收斂性存在問題如果譜半徑大于等于1就要回頭檢查規范化的分母是否取錯了維度或者原始打分矩陣是否有某個元素異常巨大。3.2 MATLAB實現矩陣運算的天然優勢MATLAB版本非常適合放到函數文件里輸入打分矩陣輸出全部指標。下面這個函數我一般命名為dematel_core.mfunction [T, D, R, M, N] dematel_core(A) % DEMATEL核心計算 % 輸入: A - n*n直接影響矩陣 % 輸出: T - 綜合影響矩陣, D - 影響度, R - 被影響度, % M - 中心度, N - 原因度 n size(A, 1); % 1. 行和最大值規范化 row_sums sum(A, 2); max_row_sum max(row_sums); if max_row_sum 0 error(行和最大值必須大于0請檢查輸入矩陣); end X A ./ max_row_sum; % 2. 綜合影響矩陣 T X * (I - X)^(-1) I eye(n); T X * inv(I - X); % 3. 行和、列和指標 D sum(T, 2); % 影響度轉為行向量 R sum(T, 1); % 被影響度 % 4. 中心度與原因度 M D R; N D - R; end邏輯說明第1步用行和最大值做規范化這是最通用的做法第2步用inv求逆矩陣n比較小時很快但如果n超過200inv的計算代價會明顯上升這時建議改用預先分解例如用X * ((I - X) \ I)的形式。第3步的轉置操作是為了讓D和R保持同樣的行向量形式便于直接用table輸出。調用方式也很直接A readmatrix(impact_matrix_clean.csv); [T, D, R, M, N] dematel_core(A); % 輸出一個結果表格 result_table table((1:size(A,1)), D, R, M, N, ... VariableNames, {Factor, D, R, M, N}); disp(result_table);table輸出的好處是結果能直接復制到論文或報告里不用再手動對齊列。如果你想把結果寫回CSVwritetable一行就能搞定。3.3 Python實現numpy下的等價寫法Python版本的核心邏輯和MATLAB完全一致但幾個語法差異值得注意。numpy的行列向量沒有隱式廣播的矩陣語義所以矩陣乘法用逐元素除法用/求和維度要顯式寫axisimport numpy as np def dematel_core(A: np.ndarray) - dict: DEMATEL核心計算 參數: A: n*n直接影響矩陣 返回: 包含 T, D, R, M, N 的字典 A np.asarray(A, dtypenp.float64) n A.shape[0] # 1. 規范化: 行和最大值 row_sums A.sum(axis1) max_row_sum row_sums.max() if max_row_sum 0: raise ValueError(行和最大值必須大于0) X A / max_row_sum # 2. 綜合影響矩陣 T X inv(I - X) I np.eye(n) T X np.linalg.inv(I - X) # 3. 影響度和被影響度 D T.sum(axis1) # 每行求和形狀 (n,) R T.sum(axis0) # 每列求和形狀 (n,) # 4. 中心度與原因度 M D R N D - R return {T: T, D: D, R: R, M: M, N: N}這里有個numpy細節sum(axis1)得到的是形狀(n,)的一維數組不是(n,1)的列向量。如果后面要拼DataFrame需要顯式reshape或者直接用pandas的Series。3.3.1 用pandas承載結果并導出CSVnumpy算完的數字需要落到可讀的表格上用pandas組織起來最順手import pandas as pd result dematel_core(A) factor_names df.index.tolist() result_df pd.DataFrame({ Factor: factor_names, D: result[D], R: result[R], M: result[M], N: result[N] }) result_df.to_csv(dematel_result.csv, indexFalse, encodingutf-8-sig)注意編碼用utf-8-sig這樣用Excel打開時中文不會亂碼。這一步在MATLAB里對應的是writetable但MATLAB寫CSV默認不帶BOMExcel打開中文因素名時容易亂碼所以MATLAB側輸出結果時我一般會把因素名先手動轉成英文代碼避免跨工具折騰編碼。4. 閾值設定、因果圖繪制與結果解讀的實操細節4.1 閾值怎么選均值加標準差還是主觀經驗綜合影響矩陣T算出來后問題就變成“哪些關系值得畫進因果圖”。直接全畫的話連線密集到沒法看。常見做法是設定閾值α只保留T_ij α的關系。最常用的閾值基準是T所有非零元素的均值加一個標準差也有直接用均值或取排序后前20%分位的。從工程實踐看均值加標準差這個公式在大多數場景下給出的是一個折中的連邊數量但它的適用前提是T的元素分布近似對稱。如果T的分布嚴重右偏用這個閾值會把太多邊裁掉。碰到這種情況我會在0.05到0.15之間做一次敏感性掃描觀察因果圖結構在哪個區間發生突變。突變點往往是網絡從“稀疏表達”轉向“關鍵結構”的邊界選它比純粹按統計公式更靠譜。4.1.1 閾值對結果解讀的影響實驗一個只有8個因素的小矩陣T的均值可能是0.08最大值0.25。閾值取均值0.08時幾乎一半的關系都保留因果圖變成一團亂麻取均值加標準差0.15時只剩關鍵的五六條邊因果結構一目了然。這里邊的核心邏輯是閾值影響的是可視化呈現而不是D/R/M/N四個指標本身。四個指標只依賴T與閾值無關。所以寫論文時閾值要寫清楚并且說明它是用于因果圖篩選而不是用于指標計算。4.2 MATLAB繪圖從四指標到因果圖因果圖的橫軸是中心度M縱軸是原因度N每個因素是一個散點。MATLAB里用scatter畫散點配合text標注因素標簽figure(Color, w); scatter(M, N, 80, filled); hold on; grid on; plot([min(M)-1, max(M)1], [0, 0], k--); % 原因度0參考線 plot([0, 0], [min(N)-1, max(N)1], k--); % 中心度0參考線 for i 1:length(M) text(M(i)0.02, N(i)0.02, factor_names{i}, FontSize, 10); end xlabel(中心度 M); ylabel(原因度 N); title(DEMATEL因果圖);散點大小統一即可不需要映射到第三維。如果想突出核心因素可以把中心度排名前3的點用不同顏色或邊框加粗這樣視覺重心自然落在關鍵因素上。4.3 Python繪圖matplotlib的對應寫法與中文字體坑Python側的等價繪圖用matplotlib完成。最容易踩的坑是中文字體import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 解決負號顯示為方塊的問題 fig, ax plt.subplots(figsize(8, 6)) ax.scatter(M, N, s100, c#2E86C1, edgecolorsblack) ax.axhline(y0, colorgray, linestyle--) ax.axvline(x0, colorgray, linestyle--) for i, name in enumerate(factor_names): ax.annotate(name, (M[i] 0.02, N[i] 0.02), fontsize10) ax.set_xlabel(中心度 M) ax.set_ylabel(原因度 N) plt.tight_layout() plt.savefig(dematel_causal_diagram.png, dpi300) plt.show()如果系統里沒有SimHei或Microsoft YaHei標簽會顯示成空心方塊。解決辦法有兩種一是把字體配置改成系統實際存在的字體名比如Linux上的WenQuanYi Zen Hei二是干脆把因素名改成英文代號圖上全部用英文論文正文里再映射回中文。第二種更省事尤其在Linux服務器上跑批處理時。4.3.1 結果解讀的三個常見誤用原因度N為正的因素構成原因組N為負的因素構成結果組。第一個常見誤用是認為原因組的因素一定更“重要”。這不一定成立。原因度只說明凈影響方向是輸出還是接收重要程度要看中心度M。一個高中心度且N略為負的因素在系統里屬于強反饋節點往往比低中心度的原因因素更值得干預。第二個誤用是把包含自身影響的矩陣當作純粹因果。T的對角線元素T_ii不為零代表因素對自身的間接影響回路這是合法的不要為了“好看”強制清零。第三個誤用是忽略尺度效應。不同專家打分的均值和方差如果差異很大合并后的矩陣可能被打分激進的一方主導。這時需要做一次歸一化檢查對比各專家矩陣的行和均值差異超過2倍的話要考慮用Z-score或極差標準化預處理。4.4 雙語言結果不一致的排查方法MATLAB和Python的浮點運算在IEEE 754框架下結果應該一致但我見過兩遍跑出來的D/R值在小數點后第三位不同。原因通常是求和順序差異導致累加誤差不同。排查方法分別輸出X矩陣的前兩行看規范化矩陣是否逐元素一致分別輸出T矩陣的對角線看最大絕對誤差一般誤差量級在10^-12左右完全不影響結論。如果在第二、第三位小數就出現偏差優先檢查規范化分母是否取了不同的統計口徑比如MATLAB的max(sum(A,2))在Python里要對應row_sums.max()而不是sum(A).max()方向搞反結果完全不一樣。5. 把DEMATEL代碼打包成可復用的zip工程5.1 zip包內部目錄怎么組織才不至于“解壓即棄”很多壓縮包下載下來之后一直躺在下載文件夾里原因在于解壓后沒有清晰的目錄結構也沒有使用說明。一套合格的DEMATEL雙語言工程包目錄組織應該讓一個新同事在5分鐘內知道哪個文件是入口dematel_kit/ ├── README.md ├── data/ │ ├── raw_score.csv # 專家原始打分多表 │ └── impact_matrix_clean.csv # 合并并清洗后的直接影響矩陣 ├── matlab/ │ ├── main_dematel.m # 入口腳本 │ ├── dematel_core.m # 核心函數 │ └── plot_causal.m # 繪圖腳本 ├── python/ │ ├── requirements.txt │ ├── dematel/ │ │ ├── __init__.py │ │ ├── core.py # 核心算法 │ │ ├── io_utils.py # 數據讀寫 │ │ └── visualize.py # 繪圖 │ └── run_dematel.py # 命令行入口 └── output/ └── .gitkeepREADME.md里至少寫三件事數據格式說明、兩套代碼各自的運行命令、輸出結果對應關系。data目錄下保留原始打分和清洗后矩陣的區分很關鍵。很多人只留清洗后的矩陣等想追溯某一格分值來源時原始問卷早就找不到了。5.2 MATLAB與Python環境的依賴準備MATLAB側需要的基本工具箱就是Matrix Operations基礎模塊plot相關功能在標準版里就夠用不需要額外工具箱。運行前打開MATLAB記得先cd到matlab目錄如果路徑含中文或空格某些舊版本會出現找不到函數的報錯。建議全部用英文目錄名。Python側的依賴集中在四個庫numpy1.24.0 pandas2.0.0 matplotlib3.7.0 openpyxl3.1.0這些庫在Python安裝之后通過pip install -r requirements.txt一次裝齊。openpyxl不是必要的但它能讓pandas直接讀寫xlsx格式的原始問卷比讓專家填CSV友好得多。在團隊協作場景里我一般建議專家填Excel模板工程側用pandas讀入后轉成標準CSV。這樣既照顧了非技術人員的操作習慣又保證了后續算法的輸入統一。5.2.1 關于zip壓縮包的密碼與完整性檢查拿到別人發來的zip壓縮包第一步不是解壓而是做完整性檢查。Windows下可以用7-Zip的右鍵菜單檢查壓縮包命令行環境用unzip -t或python的zipfile模塊更可靠unzip -l dematel_kit.zip unzip -t dematel_kit.zip-t參數會逐文件測試CRC校驗值。如果提示failed to copy或invalid zip archive通常是壓縮包在傳輸過程中損壞重新下載一遍比手動修復省時。zip壓縮包如果帶密碼python的zipfile可以直接測試密碼是否正確但不要依賴在線“密碼破解工具”浪費時間不說安全風險也不可控。正規團隊內部分發的工程包通常不帶密碼帶密碼的情況只在明確約定過的外部交付場景出現。5.3 一鍵跑通雙語言從CSV到結果對比最后給一個可落地的驗證方案。用一個固定數據分別在MATLAB和Python里跑然后對比輸出# Python側 python run_dematel.py --input data/impact_matrix_clean.csv \ --output output/dematel_result.csvMATLAB側對應執行main_dematel.m。兩邊的結果文件都寫到output目錄后可以用下面這段Python腳本快速比較關鍵指標import pandas as pd matlab_df pd.read_csv(output/dematel_result_matlab.csv) python_df pd.read_csv(output/dematel_result_python.csv) max_diff (matlab_df[[D, R, M, N]].to_numpy() - python_df[[D, R, M, N]].to_numpy()) print(最大絕對誤差:, np.abs(max_diff).max())誤差在10^-10量級說明兩邊實現完全一致后續用哪套都可以誤差超過10^-6就要回頭查代碼大概率是規范化或求和維度出了問題。這個驗證腳本本身就是zip工程包價值的體現——雙語言不是讓用戶二選一而是讓用戶在兩套實現之間互相校驗數據可信度反而更高。本文還有配套的精品資源點擊獲取