現(xiàn)DEA效率評(píng)價(jià):CCR、BCC與超效率模型實(shí)戰(zhàn))
1. 項(xiàng)目概述用Python Pulp搞定效率評(píng)價(jià)模型如果你正在處理績(jī)效評(píng)估、資源配置或者效率分析這類問題比如評(píng)價(jià)幾家分公司的運(yùn)營效率或者比較不同項(xiàng)目的投入產(chǎn)出比那你大概率聽說過或者正在尋找數(shù)據(jù)包絡(luò)分析DEA的方法。傳統(tǒng)的DEA模型像CCR和BCC是解決這類問題的利器但真到了自己動(dòng)手建模的時(shí)候很多人會(huì)卡在數(shù)學(xué)規(guī)劃求解這一步——要么被復(fù)雜的商業(yè)軟件勸退要么自己寫算法寫到頭禿。其實(shí)用Python的Pulp庫就能優(yōu)雅地解決這個(gè)問題。Pulp是一個(gè)線性規(guī)劃的建模庫它最大的好處是讓你用近乎自然語言的方式描述優(yōu)化問題然后把求解的臟活累活交給背后的求解器。今天要聊的就是如何用Pulp這個(gè)“翻譯官”把CCR、BCC和超效率Super-Efficiency這些DEA模型的數(shù)學(xué)語言翻譯成計(jì)算機(jī)能理解并求解的代碼。這不僅僅是把公式變成代碼更關(guān)鍵的是理解模型背后的假設(shè)、適用場(chǎng)景以及在編程實(shí)現(xiàn)中那些教科書上不會(huì)寫的“坑”。無論你是管理科學(xué)的學(xué)生、從事運(yùn)營分析的數(shù)據(jù)從業(yè)者還是需要做效率評(píng)估的咨詢顧問這套方法都能讓你擺脫對(duì)特定軟件的依賴快速、靈活地構(gòu)建屬于自己的效率分析工具。2. 核心模型原理與Pulp建模思路拆解在動(dòng)手寫代碼之前我們必須先吃透這幾個(gè)模型到底在干什么。DEA的核心思想是構(gòu)建一個(gè)“效率前沿面”把所有被評(píng)價(jià)的單元DMU, Decision Making Unit投射到這個(gè)面上離前沿面越近效率越高。Pulp的作用就是幫我們計(jì)算出每個(gè)DMU到這個(gè)前沿面的“距離”。2.1 CCR模型規(guī)模報(bào)酬不變的基準(zhǔn)CCR模型是DEA的鼻祖它假設(shè)生產(chǎn)過程是規(guī)模報(bào)酬不變的。這意味著如果你把所有的投入都翻倍那么產(chǎn)出也應(yīng)該正好翻倍。這個(gè)假設(shè)在很多宏觀或技術(shù)效率分析中很常用。它的數(shù)學(xué)模型本質(zhì)上是一個(gè)分式規(guī)劃問題但通常會(huì)被轉(zhuǎn)化為等價(jià)的線性規(guī)劃形式來求解。對(duì)于第k個(gè)待評(píng)價(jià)的DMU我們需要求解以下線性規(guī)劃目標(biāo)最大化第k個(gè)DMU的效率值θ或最小化其投入的徑向收縮比例。約束在所有DMU的線性組合下虛擬DMU的產(chǎn)出不能少于第k個(gè)DMU的產(chǎn)出。虛擬DMU的投入不能大于第k個(gè)DMU投入的θ倍。θ無約束通常≥0以及權(quán)重變量非負(fù)。用Pulp建模時(shí)我們的任務(wù)就是定義變量θ和各個(gè)DMU的權(quán)重λ設(shè)定目標(biāo)函數(shù)Maximize θ然后添加上述兩條核心約束。Pulp的語法非常直觀LpVariable定義變量添加約束幾乎就是抄寫數(shù)學(xué)公式。2.2 BCC模型引入規(guī)模報(bào)酬可變BCC模型在CCR的基礎(chǔ)上增加了一個(gè)凸性約束所有權(quán)重λ之和等于1。這個(gè)小小的改動(dòng)意義重大。它放松了規(guī)模報(bào)酬不變的假設(shè)允許規(guī)模報(bào)酬遞增、遞減或不變。因此BCC模型測(cè)算的是“純技術(shù)效率”剝離了規(guī)模因素的影響。在Pulp實(shí)現(xiàn)上這僅僅意味著在CCR模型的約束集合中額外加上一行代碼sum(lambda_vars) 1。這讓我們能區(qū)分一個(gè)DMU效率低下到底是因?yàn)楣芾硭讲恍屑兗夹g(shù)效率低還是因?yàn)橐?guī)模沒處在最優(yōu)狀態(tài)規(guī)模效率低。實(shí)際應(yīng)用中對(duì)于初創(chuàng)公司、教育機(jī)構(gòu)這類規(guī)模差異大且規(guī)模報(bào)酬可能變化的場(chǎng)景BCC模型往往更貼合實(shí)際。2.3 超效率模型突破“滿分”天花板傳統(tǒng)CCR/BCC模型有個(gè)尷尬效率值為1的DMU可能有多個(gè)它們都位于前沿面上無法進(jìn)一步區(qū)分誰更優(yōu)。超效率模型就是為了給這些“優(yōu)等生”排個(gè)名次。它的思路很巧妙在評(píng)價(jià)第k個(gè)DMU時(shí)將其從參考集中剔除。也就是說用除它自己之外的其他所有DMU來構(gòu)建效率前沿。這樣一來即使原本效率為1的DMU其效率值也可能大于1比如1.2表示它即使再等比例增加20%的投入仍然能在前沿面上保持效率。這個(gè)值越大說明它相對(duì)于其他前沿單元的優(yōu)勢(shì)越明顯。在Pulp建模中這是實(shí)現(xiàn)時(shí)最需要小心的地方。核心變化在于構(gòu)造約束時(shí)權(quán)重變量λ對(duì)應(yīng)的列表需要排除當(dāng)前被評(píng)價(jià)的DMU自身。這通常通過在循環(huán)中動(dòng)態(tài)創(chuàng)建變量和約束列表來實(shí)現(xiàn)而不是簡(jiǎn)單地復(fù)制粘貼CCR的代碼。注意超效率模型可能產(chǎn)生無解的情況特別是對(duì)于極端高效的DMU或數(shù)據(jù)存在較強(qiáng)共線性時(shí)。在代碼中必須做好異常處理否則程序會(huì)意外崩潰。3. 基于Pulp的完整代碼實(shí)現(xiàn)與核心環(huán)節(jié)解析理論清晰之后我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我將以一個(gè)包含5個(gè)DMU的簡(jiǎn)單數(shù)據(jù)集為例每個(gè)DMU有2個(gè)投入和2個(gè)產(chǎn)出演示如何構(gòu)建一個(gè)完整、健壯的DEA求解模塊。3.1 環(huán)境準(zhǔn)備與數(shù)據(jù)組織首先確保安裝好pulp庫。通常CBC求解器會(huì)隨pulp一起安裝對(duì)于中小規(guī)模問題足夠使用。pip install pulp數(shù)據(jù)組織是第一步也是容易出錯(cuò)的一步。我習(xí)慣使用Pandas的DataFrame來管理數(shù)據(jù)清晰且便于后續(xù)處理。import pulp import pandas as pd import numpy as np # 示例數(shù)據(jù)5個(gè)DMU2個(gè)投入Input1, Input22個(gè)產(chǎn)出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列表和數(shù)量 dmu_list df.index.tolist() num_dmus len(dmu_list) num_inputs len(inputs) num_outputs len(outputs)將投入和產(chǎn)出數(shù)據(jù)提取為NumPy數(shù)組可以大幅提升后續(xù)循環(huán)中數(shù)據(jù)訪問的速度。input_data df[inputs].values output_data df[outputs].values3.2 CCR模型函數(shù)實(shí)現(xiàn)我們將CCR模型封裝成一個(gè)函數(shù)輸入是某個(gè)DMU的索引輸出是其效率值θ和權(quán)重λ。def solve_ccr(dmu_index): 求解指定DMU的CCR模型效率值。 # 1. 創(chuàng)建問題實(shí)例目標(biāo)是最大化效率theta prob pulp.LpProblem(fCCR_DEA_DMU_{dmu_index}, pulp.LpMaximize) # 2. 創(chuàng)建決策變量 theta pulp.LpVariable(theta, lowBound0, catContinuous) # 效率值 lambdas pulp.LpVariable.dicts(lambda, range(num_dmus), lowBound0) # 權(quán)重變量 # 3. 設(shè)置目標(biāo)函數(shù)最大化theta prob theta # 4. 添加約束 # 投入約束虛擬DMU的投入 當(dāng)前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] # 產(chǎn)出約束虛擬DMU的產(chǎn)出 當(dāng)前DMU產(chǎn)出 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關(guān)閉求解器日志輸出 # 6. 獲取結(jié)果 efficiency pulp.value(theta) lambda_values [pulp.value(lambdas[j]) for j in range(num_dmus)] return efficiency, lambda_values關(guān)鍵點(diǎn)解析lowBound0確保了權(quán)重λ的非負(fù)性這是DEA模型的基本假設(shè)。投入約束使用了意味著虛擬組合的投入不能比當(dāng)前DMU按θ比例收縮后的投入更多。產(chǎn)出約束使用了意味著虛擬組合的產(chǎn)出至少要和當(dāng)前DMU一樣多。pulp.lpSum是Pulp中用于構(gòu)造線性表達(dá)式求和的函數(shù)比用Python自帶的sum更高效。3.3 BCC模型函數(shù)實(shí)現(xiàn)BCC模型在CCR的基礎(chǔ)上增加一個(gè)約束實(shí)現(xiàn)起來只需稍作修改。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] # 產(chǎn)出約束與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假設(shè))** 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模型的靈魂。它保證了參考前沿是由現(xiàn)有DMU的凸組合構(gòu)成的從而允許規(guī)模報(bào)酬可變。3.4 超效率模型函數(shù)實(shí)現(xiàn)超效率模型的實(shí)現(xiàn)需要?jiǎng)討B(tài)排除自身這是代碼中最需要技巧的部分。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) # **關(guān)鍵創(chuàng)建權(quán)重變量時(shí)排除自身索引** lambda_indices [j for j in range(num_dmus) if j ! dmu_index] lambdas pulp.LpVariable.dicts(lambda, lambda_indices, lowBound0) prob theta # 投入約束使用排除自身后的權(quán)重和DMU數(shù)據(jù) 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] # 產(chǎn)出約束使用排除自身后的權(quán)重和DMU數(shù)據(jù) 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)) # 處理可能出現(xiàn)的無解情況 if pulp.LpStatus[prob.status] Optimal: efficiency pulp.value(theta) # 構(gòu)建完整的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: # 若無最優(yōu)解返回None或一個(gè)標(biāo)記值 print(f警告: DMU {dmu_index} 的超效率模型無最優(yōu)解。狀態(tài): {pulp.LpStatus[prob.status]}) return None, [0.0]*num_dmus實(shí)現(xiàn)要點(diǎn)與避坑指南動(dòng)態(tài)索引列表lambda_indices [j for j in range(num_dmus) if j ! dmu_index]這行代碼是核心它確保了參考集中不包含自己。無解處理超效率模型可能無界Unbounded或不可行Infeasible。必須檢查求解狀態(tài)pulp.LpStatus[prob.status]只有狀態(tài)為Optimal時(shí)結(jié)果才有效。對(duì)于無解的情況常見的處理方式是返回None或一個(gè)很大的數(shù)如1e6并在后續(xù)分析中識(shí)別這些特殊DMU。結(jié)果重組由于權(quán)重變量列表不包含自身在返回最終結(jié)果時(shí)需要構(gòu)建一個(gè)完整的權(quán)重列表并在自身對(duì)應(yīng)的索引位置補(bǔ)0這樣結(jié)果格式才能和CCR/BCC統(tǒng)一便于比較。3.5 批量求解與結(jié)果整合最后我們寫一個(gè)主函數(shù)來循環(huán)求解所有DMU并將結(jié)果整理成清晰的表格。def run_dea_analysis(input_df, input_cols, output_cols): 批量運(yùn)行CCR, BCC和超效率模型并返回結(jié)果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) # 計(jì)算規(guī)模效率 (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 # 執(zhí)行分析 final_results run_dea_analysis(df, inputs, outputs) print(final_results[[CCR_Efficiency, BCC_Efficiency, Scale_Efficiency, Super_Efficiency]])這個(gè)run_dea_analysis函數(shù)封裝了完整流程。它計(jì)算了規(guī)模效率CCR效率除以BCC效率這是一個(gè)非常有用的衍生指標(biāo)能直接告訴我們效率損失有多少是源于規(guī)模不當(dāng)。結(jié)果以DataFrame形式呈現(xiàn)一目了然。4. 關(guān)鍵參數(shù)、問題排查與實(shí)戰(zhàn)心得模型跑起來只是第一步如何解讀結(jié)果、處理異常情況才是體現(xiàn)經(jīng)驗(yàn)的地方。4.1 模型結(jié)果解讀與業(yè)務(wù)洞察拿到像下面這樣的結(jié)果表后該怎么看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。這說明它的“純技術(shù)效率”是沒問題的BCC1但綜合技術(shù)效率CCR卻小于1。兩者的比值就是規(guī)模效率0.8571表明其效率損失完全來自于規(guī)模不當(dāng)可能是規(guī)模報(bào)酬遞減。超效率排名DMU A和C的CCR效率都是1但超效率分別為1.25和1.20。這說明它們雖然都在前沿面上但A比C更“穩(wěn)固”即使投入增加25%仍能保持相對(duì)有效而C只能承受20%的增加。這在給多個(gè)“標(biāo)桿”排序時(shí)非常有用。λ權(quán)重分析結(jié)果中的CCR_Lambda等列表指明了當(dāng)前DMU的參考基準(zhǔn)是誰。例如一個(gè)效率低的DMU其λ值可能主要集中在幾個(gè)高效DMU上這為“向誰學(xué)習(xí)”提供了明確方向。4.2 常見問題與解決方案速查表在實(shí)際應(yīng)用中你幾乎一定會(huì)遇到下表中的一個(gè)或幾個(gè)問題。問題現(xiàn)象可能原因排查與解決方案效率值全部為11. DMU數(shù)量過少。2. 投入/產(chǎn)出指標(biāo)選取不當(dāng)存在完全相關(guān)性。3. 數(shù)據(jù)量綱差異巨大。1.經(jīng)驗(yàn)法則DMU數(shù)量應(yīng)至少為投入與產(chǎn)出指標(biāo)數(shù)量之和的2-3倍。2. 檢查指標(biāo)間的皮爾遜相關(guān)系數(shù)移除高度共線性的指標(biāo)。3. 對(duì)數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化處理如歸一化。超效率模型求解失敗返回None或Infeasible1. 被評(píng)價(jià)的DMU是“極端”高效點(diǎn)剔除后無法被其他DMU線性組合表示技術(shù)上的“極點(diǎn)”。2. 數(shù)據(jù)存在嚴(yán)重噪聲或錯(cuò)誤。1. 這是超效率模型的固有特性并非代碼錯(cuò)誤。可記錄這些DMU為“極端有效單元”。2. 檢查數(shù)據(jù)質(zhì)量或嘗試使用SBMSlacks-Based Measure等非徑向模型。求解速度非常慢1. DMU數(shù)量過多如上千個(gè)。2. 使用默認(rèn)的CBC求解器處理大規(guī)模問題效率較低。1. 考慮使用更高效的DEA專用求解包如pyDEA或商業(yè)求解器。2. 為Pulp配置更強(qiáng)大的開源求解器如GLPK或商業(yè)求解器如Gurobi, CPLEX。權(quán)重λ全部為0或集中于某一個(gè)DMU1. 可能存在“松弛”問題即徑向改進(jìn)后仍有改進(jìn)空間。2. 被評(píng)價(jià)DMU與參考集差異過大。1. 考慮使用考慮松弛變量的模型如SBM。2. 檢查該DMU的數(shù)據(jù)是否錄入錯(cuò)誤或是否屬于一個(gè)完全不同的生產(chǎn)類型應(yīng)考慮分組評(píng)價(jià)。規(guī)模效率大于1計(jì)算錯(cuò)誤。理論上規(guī)模效率 CCR效率 / BCC效率且應(yīng)≤1。檢查代碼中效率值的獲取是否正確特別是BCC效率值是否為0導(dǎo)致的除零錯(cuò)誤。在代碼中應(yīng)添加判斷if eff_bcc 1e-10:。4.3 高級(jí)技巧與擴(kuò)展方向當(dāng)基礎(chǔ)模型玩轉(zhuǎn)后可以嘗試以下擴(kuò)展讓你的分析工具更強(qiáng)大方向距離函數(shù)DDFCCR/BCC是徑向模型只考慮等比例改進(jìn)。DDF模型允許在指定方向上如重點(diǎn)削減某類投入測(cè)量效率用Pulp實(shí)現(xiàn)只需修改目標(biāo)函數(shù)和約束的方向。考慮非期望產(chǎn)出的SBM模型很多生產(chǎn)過程會(huì)有污染物等“壞”產(chǎn)出。SBM模型能直接處理這種非期望產(chǎn)出其數(shù)學(xué)形式是分式規(guī)劃可通過Charnes-Cooper變換轉(zhuǎn)化為線性規(guī)劃再用Pulp求解。這會(huì)引入更多變量和約束但建模邏輯一致。窗口DEA與Malmquist指數(shù)分析效率隨時(shí)間的變化趨勢(shì)。這需要將不同時(shí)期的數(shù)據(jù)面板整合為每個(gè)DMU在每個(gè)時(shí)期都構(gòu)建一個(gè)規(guī)劃問題。代碼結(jié)構(gòu)上會(huì)增加一層時(shí)間循環(huán)核心求解函數(shù)不變。與可視化結(jié)合使用matplotlib或plotly繪制效率值分布直方圖、前沿面投影圖對(duì)于單投入單產(chǎn)出可繪制折線圖或者將效率值映射到地理信息系統(tǒng)GIS上讓結(jié)果更加直觀。集成到Web應(yīng)用使用Streamlit或Dash框架將你的DEA求解模塊包裝成一個(gè)交互式Web應(yīng)用。用戶上傳Excel/CSV數(shù)據(jù)選擇模型和指標(biāo)點(diǎn)擊按鈕即可生成分析報(bào)告和圖表實(shí)用性極大提升。在整個(gè)實(shí)現(xiàn)過程中最深的體會(huì)是Pulp將建模與求解分離的理念極大地簡(jiǎn)化了DEA編程。你不需要懂單純形法或內(nèi)點(diǎn)法的具體實(shí)現(xiàn)只需要關(guān)心如何正確地“描述”問題。這讓我們能把精力集中在業(yè)務(wù)邏輯模型選擇、指標(biāo)構(gòu)建、結(jié)果解讀而非算法細(xì)節(jié)上。另一個(gè)心得是數(shù)據(jù)的質(zhì)量和平滑性比模型本身更重要。投入產(chǎn)出指標(biāo)的選取是否科學(xué)、數(shù)據(jù)是否存在極端值或量綱差異這些因素對(duì)結(jié)果的影響往往超過選擇CCR還是BCC模型。在運(yùn)行任何DEA模型前花時(shí)間做好描述性統(tǒng)計(jì)和相關(guān)性分析是磨刀不誤砍柴工。