
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模型前花時間做好描述性統計和相關性分析是磨刀不誤砍柴工。