尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python Pulp实现DEA效率评价:CCR、BCC与超效率模型实战

Python Pulp实现DEA效率评价:CCR、BCC与超效率模型实战 1. 项目概述用Python Pulp搞定效率评价模型如果你正在处理绩效评估、资源配置或者效率分析这类问题比如评价几家分公司的运营效率或者比较不同项目的投入产出比那你大概率听说过或者正在寻找数据包络分析DEA的方法。传统的DEA模型像CCR和BCC是解决这类问题的利器但真到了自己动手建模的时候很多人会卡在数学规划求解这一步——要么被复杂的商业软件劝退要么自己写算法写到头秃。其实用Python的Pulp库就能优雅地解决这个问题。Pulp是一个线性规划的建模库它最大的好处是让你用近乎自然语言的方式描述优化问题然后把求解的脏活累活交给背后的求解器。今天要聊的就是如何用Pulp这个“翻译官”把CCR、BCC和超效率Super-Efficiency这些DEA模型的数学语言翻译成计算机能理解并求解的代码。这不仅仅是把公式变成代码更关键的是理解模型背后的假设、适用场景以及在编程实现中那些教科书上不会写的“坑”。无论你是管理科学的学生、从事运营分析的数据从业者还是需要做效率评估的咨询顾问这套方法都能让你摆脱对特定软件的依赖快速、灵活地构建属于自己的效率分析工具。2. 核心模型原理与Pulp建模思路拆解在动手写代码之前我们必须先吃透这几个模型到底在干什么。DEA的核心思想是构建一个“效率前沿面”把所有被评价的单元DMU, Decision Making Unit投射到这个面上离前沿面越近效率越高。Pulp的作用就是帮我们计算出每个DMU到这个前沿面的“距离”。2.1 CCR模型规模报酬不变的基准CCR模型是DEA的鼻祖它假设生产过程是规模报酬不变的。这意味着如果你把所有的投入都翻倍那么产出也应该正好翻倍。这个假设在很多宏观或技术效率分析中很常用。它的数学模型本质上是一个分式规划问题但通常会被转化为等价的线性规划形式来求解。对于第k个待评价的DMU我们需要求解以下线性规划目标最大化第k个DMU的效率值θ或最小化其投入的径向收缩比例。约束在所有DMU的线性组合下虚拟DMU的产出不能少于第k个DMU的产出。虚拟DMU的投入不能大于第k个DMU投入的θ倍。θ无约束通常≥0以及权重变量非负。用Pulp建模时我们的任务就是定义变量θ和各个DMU的权重λ设定目标函数Maximize θ然后添加上述两条核心约束。Pulp的语法非常直观LpVariable定义变量添加约束几乎就是抄写数学公式。2.2 BCC模型引入规模报酬可变BCC模型在CCR的基础上增加了一个凸性约束所有权重λ之和等于1。这个小小的改动意义重大。它放松了规模报酬不变的假设允许规模报酬递增、递减或不变。因此BCC模型测算的是“纯技术效率”剥离了规模因素的影响。在Pulp实现上这仅仅意味着在CCR模型的约束集合中额外加上一行代码sum(lambda_vars) 1。这让我们能区分一个DMU效率低下到底是因为管理水平不行纯技术效率低还是因为规模没处在最优状态规模效率低。实际应用中对于初创公司、教育机构这类规模差异大且规模报酬可能变化的场景BCC模型往往更贴合实际。2.3 超效率模型突破“满分”天花板传统CCR/BCC模型有个尴尬效率值为1的DMU可能有多个它们都位于前沿面上无法进一步区分谁更优。超效率模型就是为了给这些“优等生”排个名次。它的思路很巧妙在评价第k个DMU时将其从参考集中剔除。也就是说用除它自己之外的其他所有DMU来构建效率前沿。这样一来即使原本效率为1的DMU其效率值也可能大于1比如1.2表示它即使再等比例增加20%的投入仍然能在前沿面上保持效率。这个值越大说明它相对于其他前沿单元的优势越明显。在Pulp建模中这是实现时最需要小心的地方。核心变化在于构造约束时权重变量λ对应的列表需要排除当前被评价的DMU自身。这通常通过在循环中动态创建变量和约束列表来实现而不是简单地复制粘贴CCR的代码。注意超效率模型可能产生无解的情况特别是对于极端高效的DMU或数据存在较强共线性时。在代码中必须做好异常处理否则程序会意外崩溃。3. 基于Pulp的完整代码实现与核心环节解析理论清晰之后我们进入实战环节。我将以一个包含5个DMU的简单数据集为例每个DMU有2个投入和2个产出演示如何构建一个完整、健壮的DEA求解模块。3.1 环境准备与数据组织首先确保安装好pulp库。通常CBC求解器会随pulp一起安装对于中小规模问题足够使用。pip install pulp数据组织是第一步也是容易出错的一步。我习惯使用Pandas的DataFrame来管理数据清晰且便于后续处理。import pulp import pandas as pd import numpy as np # 示例数据5个DMU2个投入Input1, Input22个产出Output1, Output2 data { DMU: [A, B, C, D, E], Input1: [4, 7, 8, 4, 2], Input2: [3, 3, 1, 2, 4], Output1: [5, 7, 6, 8, 3], Output2: [2, 5, 4, 3, 2] } df pd.DataFrame(data).set_index(DMU) inputs [Input1, Input2] outputs [Output1, Output2] # 获取DMU列表和数量 dmu_list df.index.tolist() num_dmus len(dmu_list) num_inputs len(inputs) num_outputs len(outputs)将投入和产出数据提取为NumPy数组可以大幅提升后续循环中数据访问的速度。input_data df[inputs].values output_data df[outputs].values3.2 CCR模型函数实现我们将CCR模型封装成一个函数输入是某个DMU的索引输出是其效率值θ和权重λ。def solve_ccr(dmu_index): 求解指定DMU的CCR模型效率值。 # 1. 创建问题实例目标是最大化效率theta prob pulp.LpProblem(fCCR_DEA_DMU_{dmu_index}, pulp.LpMaximize) # 2. 创建决策变量 theta pulp.LpVariable(theta, lowBound0, catContinuous) # 效率值 lambdas pulp.LpVariable.dicts(lambda, range(num_dmus), lowBound0) # 权重变量 # 3. 设置目标函数最大化theta prob theta # 4. 添加约束 # 投入约束虚拟DMU的投入 当前DMU投入 * theta for i in range(num_inputs): prob pulp.lpSum([lambdas[j] * input_data[j, i] for j in range(num_dmus)]) theta * input_data[dmu_index, i] # 产出约束虚拟DMU的产出 当前DMU产出 for r in range(num_outputs): prob pulp.lpSum([lambdas[j] * output_data[j, r] for j in range(num_dmus)]) output_data[dmu_index, r] # 5. 求解问题 prob.solve(pulp.PULP_CBC_CMD(msgFalse)) # msgFalse关闭求解器日志输出 # 6. 获取结果 efficiency pulp.value(theta) lambda_values [pulp.value(lambdas[j]) for j in range(num_dmus)] return efficiency, lambda_values关键点解析lowBound0确保了权重λ的非负性这是DEA模型的基本假设。投入约束使用了意味着虚拟组合的投入不能比当前DMU按θ比例收缩后的投入更多。产出约束使用了意味着虚拟组合的产出至少要和当前DMU一样多。pulp.lpSum是Pulp中用于构造线性表达式求和的函数比用Python自带的sum更高效。3.3 BCC模型函数实现BCC模型在CCR的基础上增加一个约束实现起来只需稍作修改。def solve_bcc(dmu_index): 求解指定DMU的BCC模型效率值。 prob pulp.LpProblem(fBCC_DEA_DMU_{dmu_index}, pulp.LpMaximize) theta pulp.LpVariable(theta, lowBound0, catContinuous) lambdas pulp.LpVariable.dicts(lambda, range(num_dmus), lowBound0) prob theta # 投入约束与CCR相同 for i in range(num_inputs): prob pulp.lpSum([lambdas[j] * input_data[j, i] for j in range(num_dmus)]) theta * input_data[dmu_index, i] # 产出约束与CCR相同 for r in range(num_outputs): prob pulp.lpSum([lambdas[j] * output_data[j, r] for j in range(num_dmus)]) output_data[dmu_index, r] # **BCC核心增加凸性约束 (VRS假设)** prob pulp.lpSum([lambdas[j] for j in range(num_dmus)]) 1 prob.solve(pulp.PULP_CBC_CMD(msgFalse)) efficiency pulp.value(theta) lambda_values [pulp.value(lambdas[j]) for j in range(num_dmus)] return efficiency, lambda_values这一行prob pulp.lpSum([lambdas[j] for j in range(num_dmus)]) 1就是BCC模型的灵魂。它保证了参考前沿是由现有DMU的凸组合构成的从而允许规模报酬可变。3.4 超效率模型函数实现超效率模型的实现需要动态排除自身这是代码中最需要技巧的部分。def solve_super_ccr(dmu_index): 求解指定DMU的超效率CCR模型效率值。 prob pulp.LpProblem(fSuper_CCR_DEA_DMU_{dmu_index}, pulp.LpMaximize) theta pulp.LpVariable(theta, lowBound0, catContinuous) # **关键创建权重变量时排除自身索引** lambda_indices [j for j in range(num_dmus) if j ! dmu_index] lambdas pulp.LpVariable.dicts(lambda, lambda_indices, lowBound0) prob theta # 投入约束使用排除自身后的权重和DMU数据 for i in range(num_inputs): prob pulp.lpSum([lambdas[j] * input_data[j, i] for j in lambda_indices]) theta * input_data[dmu_index, i] # 产出约束使用排除自身后的权重和DMU数据 for r in range(num_outputs): prob pulp.lpSum([lambdas[j] * output_data[j, r] for j in lambda_indices]) output_data[dmu_index, r] prob.solve(pulp.PULP_CBC_CMD(msgFalse)) # 处理可能出现的无解情况 if pulp.LpStatus[prob.status] Optimal: efficiency pulp.value(theta) # 构建完整的lambda列表自身位置填充0 full_lambda [0.0] * num_dmus for j in lambda_indices: full_lambda[j] pulp.value(lambdas[j]) return efficiency, full_lambda else: # 若无最优解返回None或一个标记值 print(f警告: DMU {dmu_index} 的超效率模型无最优解。状态: {pulp.LpStatus[prob.status]}) return None, [0.0]*num_dmus实现要点与避坑指南动态索引列表lambda_indices [j for j in range(num_dmus) if j ! dmu_index]这行代码是核心它确保了参考集中不包含自己。无解处理超效率模型可能无界Unbounded或不可行Infeasible。必须检查求解状态pulp.LpStatus[prob.status]只有状态为Optimal时结果才有效。对于无解的情况常见的处理方式是返回None或一个很大的数如1e6并在后续分析中识别这些特殊DMU。结果重组由于权重变量列表不包含自身在返回最终结果时需要构建一个完整的权重列表并在自身对应的索引位置补0这样结果格式才能和CCR/BCC统一便于比较。3.5 批量求解与结果整合最后我们写一个主函数来循环求解所有DMU并将结果整理成清晰的表格。def run_dea_analysis(input_df, input_cols, output_cols): 批量运行CCR, BCC和超效率模型并返回结果DataFrame。 dmu_names input_df.index.tolist() input_vals input_df[input_cols].values output_vals input_df[output_cols].values results [] for idx, name in enumerate(dmu_names): # 求解各模型 eff_ccr, lamb_ccr solve_ccr(idx) eff_bcc, lamb_bcc solve_bcc(idx) eff_super, lamb_super solve_super_ccr(idx) # 计算规模效率 (CCR效率 / BCC效率) scale_eff eff_ccr / eff_bcc if eff_bcc 0 else None result_row { DMU: name, CCR_Efficiency: round(eff_ccr, 4), BCC_Efficiency: round(eff_bcc, 4), Scale_Efficiency: round(scale_eff, 4) if scale_eff else None, Super_Efficiency: round(eff_super, 4) if eff_super else None, CCR_Lambda: lamb_ccr, BCC_Lambda: lamb_bcc, Super_Lambda: lamb_super if eff_super else None } results.append(result_row) result_df pd.DataFrame(results) result_df.set_index(DMU, inplaceTrue) return result_df # 执行分析 final_results run_dea_analysis(df, inputs, outputs) print(final_results[[CCR_Efficiency, BCC_Efficiency, Scale_Efficiency, Super_Efficiency]])这个run_dea_analysis函数封装了完整流程。它计算了规模效率CCR效率除以BCC效率这是一个非常有用的衍生指标能直接告诉我们效率损失有多少是源于规模不当。结果以DataFrame形式呈现一目了然。4. 关键参数、问题排查与实战心得模型跑起来只是第一步如何解读结果、处理异常情况才是体现经验的地方。4.1 模型结果解读与业务洞察拿到像下面这样的结果表后该怎么看DMUCCR_EfficiencyBCC_EfficiencyScale_EfficiencySuper_EfficiencyA1.00001.00001.00001.2500B0.85711.00000.85710.8571C1.00001.00001.00001.2000...............CCR效率 vs BCC效率以DMU B为例其CCR效率为0.8571BCC效率为1.0000。这说明它的“纯技术效率”是没问题的BCC1但综合技术效率CCR却小于1。两者的比值就是规模效率0.8571表明其效率损失完全来自于规模不当可能是规模报酬递减。超效率排名DMU A和C的CCR效率都是1但超效率分别为1.25和1.20。这说明它们虽然都在前沿面上但A比C更“稳固”即使投入增加25%仍能保持相对有效而C只能承受20%的增加。这在给多个“标杆”排序时非常有用。λ权重分析结果中的CCR_Lambda等列表指明了当前DMU的参考基准是谁。例如一个效率低的DMU其λ值可能主要集中在几个高效DMU上这为“向谁学习”提供了明确方向。4.2 常见问题与解决方案速查表在实际应用中你几乎一定会遇到下表中的一个或几个问题。问题现象可能原因排查与解决方案效率值全部为11. DMU数量过少。2. 投入/产出指标选取不当存在完全相关性。3. 数据量纲差异巨大。1.经验法则DMU数量应至少为投入与产出指标数量之和的2-3倍。2. 检查指标间的皮尔逊相关系数移除高度共线性的指标。3. 对数据进行标准化处理如归一化。超效率模型求解失败返回None或Infeasible1. 被评价的DMU是“极端”高效点剔除后无法被其他DMU线性组合表示技术上的“极点”。2. 数据存在严重噪声或错误。1. 这是超效率模型的固有特性并非代码错误。可记录这些DMU为“极端有效单元”。2. 检查数据质量或尝试使用SBMSlacks-Based Measure等非径向模型。求解速度非常慢1. DMU数量过多如上千个。2. 使用默认的CBC求解器处理大规模问题效率较低。1. 考虑使用更高效的DEA专用求解包如pyDEA或商业求解器。2. 为Pulp配置更强大的开源求解器如GLPK或商业求解器如Gurobi, CPLEX。权重λ全部为0或集中于某一个DMU1. 可能存在“松弛”问题即径向改进后仍有改进空间。2. 被评价DMU与参考集差异过大。1. 考虑使用考虑松弛变量的模型如SBM。2. 检查该DMU的数据是否录入错误或是否属于一个完全不同的生产类型应考虑分组评价。规模效率大于1计算错误。理论上规模效率 CCR效率 / BCC效率且应≤1。检查代码中效率值的获取是否正确特别是BCC效率值是否为0导致的除零错误。在代码中应添加判断if eff_bcc 1e-10:。4.3 高级技巧与扩展方向当基础模型玩转后可以尝试以下扩展让你的分析工具更强大方向距离函数DDFCCR/BCC是径向模型只考虑等比例改进。DDF模型允许在指定方向上如重点削减某类投入测量效率用Pulp实现只需修改目标函数和约束的方向。考虑非期望产出的SBM模型很多生产过程会有污染物等“坏”产出。SBM模型能直接处理这种非期望产出其数学形式是分式规划可通过Charnes-Cooper变换转化为线性规划再用Pulp求解。这会引入更多变量和约束但建模逻辑一致。窗口DEA与Malmquist指数分析效率随时间的变化趋势。这需要将不同时期的数据面板整合为每个DMU在每个时期都构建一个规划问题。代码结构上会增加一层时间循环核心求解函数不变。与可视化结合使用matplotlib或plotly绘制效率值分布直方图、前沿面投影图对于单投入单产出可绘制折线图或者将效率值映射到地理信息系统GIS上让结果更加直观。集成到Web应用使用Streamlit或Dash框架将你的DEA求解模块包装成一个交互式Web应用。用户上传Excel/CSV数据选择模型和指标点击按钮即可生成分析报告和图表实用性极大提升。在整个实现过程中最深的体会是Pulp将建模与求解分离的理念极大地简化了DEA编程。你不需要懂单纯形法或内点法的具体实现只需要关心如何正确地“描述”问题。这让我们能把精力集中在业务逻辑模型选择、指标构建、结果解读而非算法细节上。另一个心得是数据的质量和平滑性比模型本身更重要。投入产出指标的选取是否科学、数据是否存在极端值或量纲差异这些因素对结果的影响往往超过选择CCR还是BCC模型。在运行任何DEA模型前花时间做好描述性统计和相关性分析是磨刀不误砍柴工。
返回列表