用Python的Pulp库搞定NDDF模型:一个环境经济学研究生的效率测算实战笔记
用Python的Pulp库实现NDDF模型:环境经济学研究生的效率测算指南
当我在撰写关于绿色全要素生产率的硕士论文时,NDDF(非径向方向距离函数)模型的理论推导让我头疼不已。直到发现Python的Pulp库,这个开源的线性规划工具竟然能完美解决我的计算需求。本文将分享如何从零开始用Pulp实现NDDF模型,避开我踩过的那些坑。
1. 为什么选择Pulp而非商业工具
在环境经济学领域,Gurobi和CPLEX常被推荐用于效率测算。但作为学生研究者,Pulp提供了三个不可替代的优势:
- 零成本门槛:完全开源,无需担心许可证费用
- 轻量级集成:与Python科学计算栈(Pandas/NumPy)无缝衔接
- 灵活建模:支持多种求解器后端(包括CBC、GLPK等)
特别在处理NDDF模型时,Pulp的语法几乎可以直接映射数学公式。比如弱可处置性约束的代码表达:
if self.disp == "weak disposability": for s in self._s: prob += pulp.lpSum([self.weights[j] * self.bad_outs.values[j][s] for j in self._j]) == self.bad_outs.values[j0][s] - self.betab[s] * self.gb.values[j0][s]2. 数学公式到代码的转换技巧
NDDF的核心是方向向量的处理,这里最容易出错。根据Zhou(2012)的规范,正确的约束条件设置应遵循:
变量定义规则:
- λ(前沿面权重):
pulp.LpVariable.dicts("Weight", self._j) - β(缩放系数):分投入/产出定义上下界
- λ(前沿面权重):
方向向量陷阱:
- 文献中g=(-E,Y,-D)的负号已在公式中体现
- 代码中方向向量应统一用非负值:
directional_factor = [0, 0, 1, 1, 1] # 对应(K,L,E,Y,D) self.gx = directional_factor[:self.I] * self.inputs- 弱可处置性实现: 通过
disp参数切换强弱处置模式,关键区别在于非期望产出的约束符号(==或≥)
3. 数据准备与预处理实战
我的Excel数据常出现以下问题,导致模型报错:
- 数据类型不一致:用
df = pd.read_excel().astype(float)统一类型 - 负值处理:非期望产出存在负值时需标准化:
bad_outs = (bad_outs - bad_outs.min()) / (bad_outs.max() - bad_outs.min())- 权重向量配置示例:
- 3项投入+1项好产出+3项坏产出:
weight = [1/9,1/9,1/9, 1/3, 1/9,1/9,1/9]
完整的数据加载模板:
inputs = pd.read_excel('data.xlsx', usecols='A:C') outputs = pd.read_excel('data.xlsx', usecols='D') bad_outs = pd.read_excel('data.xlsx', usecols='E:G')4. 结果解读与可视化分析
模型输出包含四个关键部分:
- 效率值(β):值越大表示无效率程度越高
- 前沿面权重(λ):反映参考DMU的构成
- 状态码:检查是否所有DMU都成功求解
建议用以下方法分析结果:
results = pd.concat([ pd.DataFrame.from_dict(solve_results[1], orient='index', columns=['inefficiency']), pd.DataFrame.from_dict(solve_results[2], orient='index') ], axis=1) results.to_excel('results.xlsx')典型问题排查表:
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无解状态 | 方向向量符号错误 | 检查g向量与公式一致性 |
| β值全为0 | 权重配置不当 | 调整weight_vector比例 |
| 异常大值 | 数据量纲不统一 | 对数据做标准化处理 |
5. 进阶应用:Luenberger指数计算
获得各期NDDF结果后,可按以下公式计算生产率变化:
GML = 0.5 * [(β_t - β_t+1) + (β'_t - β'_t+1)]实现代码片段:
def luenberger_index(beta_t, beta_t1, beta_prime_t, beta_prime_t1): return 0.5 * ((beta_t - beta_t1) + (beta_prime_t - beta_prime_t1))记得在结果中添加时间标识列,方便面板数据分析。这是我用Pulp完成NDDF测算后最深刻的体会:好的代码组织能节省后续90%的分析时间。
