MATLAB实现DEA数据包络分析:CCR与BCC模型完整教程
2026/9/3 2:49:06 网站建设 项目流程

简介:这是一份基于MATLAB实现的数据包络分析(DEA)资源包,内含DEA-Solver工具箱及配套示例,面向经济管理、工程评估等方向的师生和企业分析人员,适用于企业、医院、学校等组织的相对效率评价,也可作为运筹学相关课程的辅助教学材料。压缩包共21个文件,体积仅2.54MB,以xls示例/结果数据表为主,并包含PDF方法文献、M脚本、MAT数据文件、使用说明txt及doc文档等,既有可直接运行的模型代码,也有理论参考资料。目前已有442人学习。资源覆盖DEA建模全流程:从CCR与BCC模型定义、输入输出变量选择,到数据预处理、线性规划求解、效率排序及结果输出;同时附有区域创新政策排名、地方政府效率内在决定因素等应用案例文献,便于读者理解模型的实际使用方式。借助DEA-Solver的主函数库、示例数据和说明文档,使用者既可系统掌握DEA原理,也可以替换数据完成自定义的效率评价,还可用于教学演示与课题验证。 做项目绩效评价和效率分析时,数据包络分析(DEA)是我几乎绕不开的工具。前阵子有个课题需要评估一组同类型机构的运营效率,指标多、样本量不算大、又不想强行假设生产函数形式,我第一反应就是拿MATLAB写一个DEA求解程序,顺带把手头的DEA Solver流程也统一跑了一遍。这篇就基于这个项目,完整拆一下DEA的MATLAB实现思路、核心代码、常见坑位,以及怎么用solver配套交叉验证,给准备做效率评价的朋友一份能直接参考的实操稿。

1. DEA要解决什么问题,为什么用MATLAB

1.1 一个场景先立起来

假设你有20家医院,每家都有几个投入指标(床位数、医生数、年度运营成本)和几个产出指标(门诊量、出院人数、满意度评分)。现在要判断哪几家医院“更有效率”,哪几家还有改进空间。这个问题难就难在:多个投入多个产出,量纲不同,无法用单指标直接算综合效率。

DEA的做法很聪明,它不预设任何函数关系,而是把所有被评价对象(叫决策单元,DMU)放在一起,用“数据包络”的方式勾勒出一条最优前沿面。落在前沿面上的DMU效率为1,没落上去的,根据它与前沿面的距离得到一个0到1之间的效率值。这本质上就是一个线性规划问题,而MATLAB的linprog就是干这个的。

1.2 为什么选MATLAB而不是其他工具

如果你只是做一次简单的两投入两产出分析,用Excel插件也没问题。但项目但凡有一点扩展需求——比如要批量跑数百个DMU、要做Malmquist指数、要嵌入到自己的算法循环里做灵敏度分析——Excel就非常吃力了。MATLAB的优势在于:

  • 线性规划求解器成熟,linprog支持多种算法,处理小规模DEA绰绰有余
  • 数据预处理、结果统计、可视化在一个环境里完成
  • 可以封装成函数批量调用,复用到不同数据集上

我在这套程序里也内置了DEA Solver的验证逻辑,说白了就是用两个不同实现互相核对结果,防止代码写错导致整个分析白做。

2. DEA模型怎么选:CCR、BCC和导向问题

2.1 CCR模型:规模报酬不变下的效率

DEA最早、最经典的模型是CCR(Charnes-Cooper-Rhodes)。它假设被评价对象处于规模报酬不变的状态,也就是投入翻倍、产出也应该翻倍。对于制造业生产线这类场景,这个假设还算合理,但放到医院、高校、银行网点,规模效应往往是递减或递增的,这时候继续用CCR就会把规模效率也混进纯技术效率里,得到的结果会产生误导。

CCR投入导向模型的线性规划形式,数学上可以写成这样:

min θ

s.t. Σⱼ xᵢⱼ λⱼ ≤ θ xᵢ₀,(i = 1,…,m)

Σⱼ yᵣⱼ λⱼ ≥ yᵣ₀,(r = 1,…,s)

λⱼ ≥ 0

其中θ就是要算的效率值,xᵢ₀和yᵣ₀是被评价DMU的投入产出,λ为权重变量。

2.2 BCC模型:规模报酬可变

实际项目里我用得更多的是BCC(Banker-Charnes-Cooper)。它只比CCR多了一个约束:Σλⱼ = 1,相当于要求被评价DMU只和规模相近的DMU比较。这样算出来的效率值剔除了规模因素,专业叫法叫“纯技术效率”。

当我对同一组数据同时跑CCR和BCC时,两者相减还能算出规模效率。具体判断标准是:

  • 若CCR效率 = BCC效率,说明DMU处于最优规模状态
  • 若两者不等,再看Σλ的取值趋势,可以判断是规模报酬递增还是递减

这个信息对管理决策极其有用,因为规模报酬递增的DMU应适当扩大规模,递减的则应控制扩张。

2.3 投入导向还是产出导向

这个选择并不复杂:如果经营管理者手上主要能控制投入(降本、优化资源),选投入导向;如果投入相对固定、要想办法提升产出(比如医院床位已经满负荷,该想的是提高周转率),选产出导向。两种导向求解出来的效率值略有差异,严格说只有规模报酬不变时才等同。我在代码里默认做了投入导向,同时在函数里预留了导向切换的参数,避免后期返工。

3. MATLAB代码实现:从零写一个DEA求解函数

3.1 核心函数代码

我实现的是基于linprog的对偶模型,变量顺序为θ、λ向量、投入松弛变量、产出松弛变量。由于linprog默认求解min f'x,所以目标函数里只有θ的系数为1,其余全是0。

function [theta, lambda, slack_minus, slack_plus] = dea_ccr_io(X, Y, j0) % dea_ccr_io CCR投入导向DEA求解 % X: n×m 矩阵,每行为一个DMU的m个投入指标 % Y: n×s 矩阵,每行为一个DMU的s个产出指标 % j0: 被评价DMU的行号 % theta: 效率值 % lambda: 参照权重 % slack_minus: 投入松弛变量 % slack_plus: 产出松弛变量 n = size(X, 1); m = size(X, 2); s = size(Y, 2); x0 = X(j0, :)'; y0 = Y(j0, :)'; % 变量顺序: theta, lambda(1:n), s_minus(1:m), s_plus(1:s) numVar = 1 + n + m + s; f = [1; zeros(numVar - 1, 1)]; Aeq = zeros(m + s, numVar); beq = zeros(m + s, 1); % 投入约束: sum_j X(i,j)*lambda_j - theta*x0(i) + s_minus(i) = 0 for i = 1:m Aeq(i, 1) = -x0(i); Aeq(i, 2:n+1) = X(:, i)'; Aeq(i, n+1+i) = 1; end % 产出约束: sum_j Y(r,j)*lambda_j - s_plus(r) = y0(r) for r = 1:s Aeq(m+r, 2:n+1) = Y(:, r)'; Aeq(m+r, n+1+m+r) = -1; beq(m+r) = y0(r); end lb = [0; zeros(n + m + s, 1)]; ub = inf(numVar, 1); options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'dual-simplex'); [x, fval, exitflag] = linprog(f, [], [], Aeq, beq, lb, ub, options); if exitflag <= 0 theta = NaN; lambda = []; slack_minus = []; slack_plus = []; warning('DMU %d 求解失败', j0); return; end theta = fval; lambda = x(2:n+1); slack_minus = x(n+2:n+m+1); slack_plus = x(n+m+2:end); end

这里有个比较关键的细节:把θ放在变量向量首位,投入约束里θ的系数就是-x0。一开始我写的时候把符号搞反了,算出来的效率值全都大于1,折腾了半天才发现是约束写成了等式右边的常量,没把θ移到左侧。

3.2 批量求解全部DMU

有了单个函数,批量求解就很简单了:

n = size(X, 1); theta_all = zeros(n, 1); for k = 1:n [theta_all(k), ~, ~, ~] = dea_ccr_io(X, Y, k); end

需要注意,DEA的效率值是按DMU逐个求的,每个DMU本质上是一个独立的线性规划问题,求解顺序互不影响。如果数据集比较大,可以先用parfor并行,但要注意每个工作进程都要能访问dea_ccr_io函数。

3.3 BCC模型的扩展

把CCR改成BCC,只需要多加一行约束。决策变量尾部加一个变量,专门用来做Σλ=1的等式约束:

% 在Aeq和beq末尾追加一行 Aeq(end+1, 2:n+1) = 1; beq(end+1) = 1;

变量数量不用变,因为Σλ=1不需要新的变量。但要把ub里λ部分的上限设为1?不需要,λ可以大于1,规模报酬可变的含义是允许权重和等于1,而不是λ本身受限。

4. 用DEA Solver交叉验证

4.1 DEA Solver是什么

DEA Solver是一套专门做DEA分析的工具,很多版本以Excel加载项的形式存在,也支持常见的CCR、BCC、超效率、SBM模型。项目里我把它作为MATLAB程序的交叉验证工具:先让MATLAB跑一遍,再用DEA Solver对同一份数据跑一遍,两者差距在1e-6以内才算代码可靠。

DEA-Solver官方渠道有多个版本,Learning版面向教学,功能相对基础;Pro版覆盖模型比较全,支持中文界面,商业项目用得比较多。网上能找到免费的试用版和学习版,追求完整模型的话建议支持正版授权。

4.2 交叉验证的流程

  • 第一步:MATLAB程序导出效率值,保存为CSV
  • 第二步:DEA Solver读取同一份X和Y数据
  • 第三步:对比每个DMU的效率和松弛变量

如果出现个别DMU结果不一致,先检查数据读取是否对齐。实际项目中常见的原因是Excel里数据列顺序调换了,或者某个DMU的投入产出数据为0导致求解器路径不同。只要代码逻辑正确,两个工具的解应该高度一致。

4.3 为什么还要信任手写代码

有现成工具为什么还自己写MATLAB?因为DEA Solver这类工具适合单次分析,但项目做到后面通常要反复调参,比如换指标组合、做窗口分析、算Malmquist指数。写成函数后,这些操作就是几行循环的事。而且DEA Solver输出的是静态报告,想画效率分布的直方图、做效率值和环境变量的回归分析,还是得回到MATLAB或Python环境。手写代码的意义不在于“不用工具”,而在于把分析流程工程化。

5. 实际案例:医院运营效率评估

5.1 数据准备与指标选取

拿一个简化的例子来演示。假设要评估10家医院的效率,选了两个投入指标和两个产出指标:

医院床位数医生数门诊量(千人次)出院人数(百人)
A42018085.232.1
B35015278.430.5
C51021092.635.2
D2809555.722.3
E46018888.931.8
F39017082.529.6
G53022595.438.7
H30011062.324.5
I44017579.830.2
J37014374.627.8

指标选取上有一个经验法则:DMU数量要大于投入产出指标数量之和的两倍,最好在3倍以上,否则区分度会很差,大量DMU效率都等于1。我这个例子样本量偏小,仅作演示,正式研究里10个DMU配4个指标是偏紧的,至少要扩展到30个以上。

5.2 运行结果

用上面代码批量跑一遍,数据大致长这样:

X = [420 180; 350 152; 510 210; 280 95; 460 188; 390 170; 530 225; 300 110; 440 175; 370 143]; Y = [85.2 32.1; 78.4 30.5; 92.6 35.2; 55.7 22.3; 88.9 31.8; 82.5 29.6; 95.4 38.7; 62.3 24.5; 79.8 30.2; 74.6 27.8]; theta_all = zeros(size(X, 1), 1); for k = 1:size(X, 1) [theta_all(k), ~, ~, ~] = dea_ccr_io(X, Y, k); end bar(theta_all);

结果中一部分医院(比如G、D这类投入产出结构特殊的)效率值会靠近1,其他医院的效率值在0.85到0.99之间。这里有个分析小技巧:不要只看效率排名,一定要结合λ权重看它的“参照集”。比如医院F的效率是0.91,那就看它λ中权重最大的几个DMU是谁,这些DMU就是它的标杆。管理上可以直接让F院对标G院的资源配置结构。

5.3 松弛变量怎么用

效率值小于1只说明“缩水的空间有多大”,具体哪些指标要改进、改进多少,要看松弛变量。在CCR投入导向下,投入松弛量除以当前投入量,就是该指标的冗余比例。比如某医院医生数的松弛量是15人,那就意味着在不减少产出的前提下,理论上可以精简15名医生。

产出松弛也类似。需要注意一点,效率等于1的DMU也可能有松弛,这种情况被称为“弱有效”。判断是否强有效要同时看θ是否等于1以及所有松弛是否为0。我在程序里特意把松弛变量也返回出来,就是为了避免只看效率值导致误判。

6. 常见问题与排查技巧

6.1 为什么算出来的效率全是1

出现这个情况,九成原因是指标太多、DMU太少,DEA的数据包络太松,每个DMU都能找到自己的一套权重把自己包进去。解决方法:

  • 增加DMU数量
  • 精简投入产出指标,优先选相关性低的指标
  • 用主成分分析先降维

6.2 求解失败或无可行解

最常发生在新手刚接触时,把投入产出方向搞反了。linprog返回exitflag小于等于0时,不要硬读theta。另外注意linprog在旧版MATLAB(2020a之前)默认算法可能对这类带等式约束的小规模问题不友好,建议指定algorithm为'dual-simplex'。我在项目里就遇到过一次同样的代码在两个版本结果不一样的情况,最后统一锁定了算法选项才对齐。

6.3 数据和DEA Solver对不上

很多情况下不是程序问题,而是Excel数据里有隐藏行、空值或者文本型数字。建议在MATLAB里用isnan和isinf先做一次检查,也把数据round到统一精度再导出。DEA对小数位数并不敏感,但完全相同的输入才能保证完全一致的输出。

6.4 零值和负值怎么处理

DEA默认要求投入产出数据非负。遇到某些指标存在0,有两种常见处理:一是换模型,比如SBM(Slacks-Based Measure)允许处理非期望产出和部分零值;二是给零值加一个很小的正数,但这会影响效率结果,不推荐。产出指标里若存在负值,一般要先做数据平移,但平移会改变效率测量结果,最好先想清楚经济含义。

7. 写在最后的个人体会

这套MATLAB DEA程序前前后后改过好几版,从最初只用linprog裸写CCR,到后来支持BCC、加入松弛变量输出、对接DEA Solver验证,每次改动都踩了不少坑。现在回头看,最有价值的不是代码本身,而是“用什么模型、在什么假设下、怎么解读结果”这套分析思路。数据进去、效率出来只是第一步,真正给管理决策提供依据的,是对参照集、松弛变量和规模收益状态的深度解读。如果你也在做类似效率评价,建议先把CCR和BCC的手推线性规划弄明白,再动手写代码,后面换任何模型都不慌。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询