2026/9/19 15:48:22

从泰尔指数到SDG框架:量化越南增长中的贫困隔离

从泰尔指数到SDG框架:量化越南增长中的贫困隔离 简介世界银行2025年2月发布政策研究工作论文聚焦越南经济快速增长背后贫困隔离加剧的现象探讨能否实现公平发展的可持续发展目标。报告面向关注越南经济、减贫与不平等议题的研究者、政策分析人员及国际发展领域从业者基于2002—2020年越南家庭生活标准调查等省级面板数据展开实证分析。压缩包内含1个PDF文件容量1.75MB为英文原版完整报告便于直接阅读与引用。目前已有92人浏览学习适合作为行业调查报告和2025年度研报的参考文献。报告核心发现包括省内不平等程度明显高于省际不平等且差距随时间扩大经济增长对减贫有积极影响但效果受不平等水平制约不平等加剧对经济增长产生负面效应且对不同贫困指标影响存在差异农业向非农转移具有减贫益处。据此提出政策制定者应着力缩小空间差距和收入不平等为越南实现可持续和公平发展提供实证依据与建议。1. 世界银行2025研报的越南难题增长与贫困隔离如何同时出现“越南经济保持快速增长但贫困呈隔离加剧”——世界银行2025年研报的这个判断表面上是两个统计口径打架GDP增速亮眼贫困发生率却顽固地集中在特定地区和群体。集中贫困说明增长收益有明确的空间边界大城市与加工制造走廊吃到红利偏远省份与弱势人口被甩在后面。对做数据的人来说这首先是一个可以复现的度量问题隔离怎么定义、用什么指标量化、怎么从公共数据与家庭调查里把证据链拉出来。下面用泰尔指数分解、WDI API、SDG目标追踪框架和Bootstrap检验把这条链完整走一遍代码可直接改参数用在其他发展中国家。适合做全球发展数据、区域不平等课题以及把增长与分配问题量化的数据工程师和政策分析人员。2. 度量贫困隔离的指标选择为什么基尼系数不够泰尔指数可以分解2.1 基尼系数对“贫困扎堆”不敏感基尼系数衡量的是个人之间收入分配的整体离散度。它把所有个体按收入排成队列只看曲线下的面积不看每个人在哪个省份、哪个地区。两个基尼系数完全相同的国家可能一个省际差异悬殊、城镇与乡村收入泾渭分明另一个地区之间相对均衡、贫困人口均匀分布。研报里说的“贫困隔离”是后者这种空间结构问题不是整体离散度问题。越南的实际情况很容易理解胡志明市与河内两大增长极吸纳了主要的外商投资与工业产值中部高原、西北山区与湄公河三角洲部分省份的人均收入长期低于全国平均水平。当GDP增长发生在增长极内部或与之配套的供应链上贫困人口没有跟着迁移、也没有获得足够转移支付时就出现“增长的同时贫困在空间上越来越集中”的现象。泰尔指数Theil Index在隔离分析里比基尼更常用因为它满足群体可分解性总体不平等可以拆成组间不平等between-group与组内不平等within-group两个部分。当我们按省分组时组间项回答的问题很直接如果组内人人拿省平均收入还剩多少不平等这个份额越大说明增长收益越集中在少数地区隔离越严重。2.2 用 Python 实现泰尔指数与地区分解泰尔指数的定义是 T (1/n) Σ (x_i / x̄) ln(x_i / x̄)其中 x_i 是第 i 个样本的家庭人均收入或消费x̄ 是全样本均值。下面的实现接受一个 pandas DataFrame按省级分组做分解。import numpy as np import pandas as pd def theil_index(values): 计算非负序列的泰尔-L指数。 x np.asarray(values, dtypefloat) x x[x 0] # 泰尔指数定义要求 x_i 0 n len(x) if n 0: return np.nan x_mean x.mean() ratios x / x_mean return float(np.sum(ratios * np.log(ratios)) / n) def theil_decomposition(df, value_col, group_col, checkTrue): 按 group_col 对 value_col 做泰尔指数分解。 返回: total : 总体泰尔指数 between : 组间项把每个组的样本替换成组均值后再算泰尔 within : 组内项各组泰尔指数按人数加权平均 total theil_index(df[value_col]) group_means df.groupby(group_col)[value_col].transform(mean) between theil_index(group_means) within 0.0 n_total 0 for _, g in df.groupby(group_col): n_g len(g) n_total n_g within n_g * theil_index(g[value_col]) within / n_total if check: assert abs(total - (between within)) 1e-10, 分解不守恒 return {total: total, between: between, within: within}transform(mean)保留原始行数把每个样本替换成所属省份的均值组内项则是按人口加权的各省泰尔指数之和权重是省份样本量占全样本的比例。泰尔指数的可分解性是精确恒等式total between within代码里的断言就是验证这一点。两个参数在真实分析里要仔细定。value_col建议用家庭人均消费支出而不是收入消费在低收入国家更稳定、更能反映真实福利国际比较时数据可取自世界银行 WDI 的消费份额指标而越南国内分析常用 GSO 的 VHLSS越南生活水平调查微观数据。group_col用省级行政区划编码如果想看城乡维度也可以换成 urban/rural 二分变量。2.3 模拟数据看“隔离加剧”的信号长什么样拿不到微观数据时先用带随机种子的模拟数据验证方法。下面模拟 2010 与 2025 两年、6 个省份各 200 户的收入核心区别是 2025 年各省均值拉得更开——这正是“增长集中在少数省份”的简化设定。rng np.random.default_rng(42) frames [] for year, spread in [(2010, 1.0), (2025, 2.0)]: for prov in range(6): prov_mean 50.0 * (1 spread * prov / 4.5) incomes rng.lognormal( meannp.log(prov_mean / 2), sigma0.4, size200 ) frames.append(pd.DataFrame({ year: year, province: fP{prov 1}, income: incomes, })) sim_df pd.concat(frames, ignore_indexTrue) for year in [2010, 2025]: res theil_decomposition(sim_df[sim_df[year] year], income, province) share res[between] / res[total] * 100 print(f{year}: total{res[total]:.4f}, fbetween{res[between]:.4f}, fwithin{res[within]:.4f}, between_share{share:.1f}%)运行后输出大致如下2010 年组间占比约 36%2025 年上升到约 53%。这组数字只用于展示方法不是越南官方调查结果。注意全国均值在增长基尼系数差距也不大真正变化的是组间占比——这就是研报里“贫困隔离加剧”在统计量上的呈现。读结果时主要看三个数。第一是between_share的时间趋势占比持续上升说明省份均值差异在扩大。第二是within是否同步变化如果组内项也上升说明省内部同样在分化问题就不仅限于地区间。第三是total本身它提醒你整体不平等可能被隔离问题掩盖——如果只看基尼系数2025 年的数值未必比 2010 年刺眼。3. 数据准备用世界银行 WDI API 拉取越南经济与贫困指标3.1 wbgapi 的最小闭环从安装到取出 VNM 数据世界银行开放数据WDI是这类研报最常见的公共数据源。Python 里比较顺手的客户端是wbgapi它封装了 REST 接口不需要自己拼 URL 和处理翻页。安装与取数的最小闭环如下pip install wbgapiimport wbgapi as wb # 越南的 ISO3 代码是 VNM df wb.data.DataFrame( [NY.GDP.MKTP.KD.ZG, SI.POV.GINI, SI.POV.NAHC], economyVNM, timerange(2000, 2026), ) print(df.head())timerange(2000, 2026)生成从 2000 到 2025 的年份列economyVNM把查询限定到越南避免返回东南亚全量数据。这一步拿回来的是以年份为列、指标为行的宽表缺失年份的单元格是 NaN需要后续处理。如果只看单个年份time2018即可要批量对比东南亚economyVNM:IDN:THA这类冒号分隔写法也支持。代码里的三个指标代码分别对应实际 GDP 年增长率、基尼系数、国家贫困线贫困率这是第 4 章记分卡的基础。3.2 把宽表整理成可分析的面板数据WDI 返回的宽表结构在时间维度上不适合直接合并。常见做法是先转置成长表再按年份 merge 成面板数据。下面封装一个按指标拉取并转置的小函数import pandas as pd def wdi_long(code, economyVNM, start2000, end2026): 拉单个指标并转成长表。 raw wb.data.DataFrame(code, economyeconomy, timerange(start, end)) long raw.T.reset_index() long.columns [year, code] long[year] long[year].astype(int) return long codes [NY.GDP.MKTP.KD.ZG, SI.POV.GINI, SI.POV.NAHC] panel None for code in codes: cur wdi_long(code) panel cur if panel is None else panel.merge(cur, onyear, howouter) panel panel.sort_values(year).reset_index(dropTrue) print(panel.tail(8))wdi_long的参数分别是指标代码、经济体、起始年份和结束年份缺省值把越南和常规时间跨度写死调用时只需传指标代码。raw.T把年份转置成行第 2 章算出来的省级微观数据就能直接按year和这套宏观指标做 merge。howouter是保险写法贫困指标可能中间缺年份外连接保证不丢 GDP 和基尼序列的任何一年。如果要同时跑多个经济体把economy参数改成列表提前用pd.concat合并再按(economy, year)建索引。日常分析里建议把这套逻辑封装成独立模块避免每个新指标都走一遍清洗流程。3.3 缺失值与口径变化什么能插值什么不能世界银行这套 API 里最常被坑的是两个问题。第一是GDP和贫困率的发布口径会随购买力平价基准年调整2017 年 PPP 基准更新之后国际贫困线从 1.90 美元调整到 2.15 美元历史数据在同一指标名下也可能被重写所以做跨年对比前要确认数据版本。第二是越南自身的国家贫困线SI.POV.NAHC不是每年公布基尼系数也跳着发布直接画趋势线之前要先处理空洞。能做的处理很有限。相邻年份的线性插值只适合 GDP 这类高频率序列贫困率和基尼不建议插值宁可保留 NaN 并在图上标注。常见替代方案是参考世界银行贫困与不平等平台的年度估算它的数据比 WDI 原始序列平滑一些。如果只是需要连续序列跑模型可以用ffill向下填充但务必在代码和图表里标注口径是“沿用上一期值”。panel[gini_ffill] panel[SI.POV.GINI].ffill() print(panel[[year, SI.POV.GINI, gini_ffill]].dropna(subset[gini_ffill]).head(12))ffill只向下填充缺失值不影响已存在的观测。如果 2018 和 2020 有值、2019 缺失那 2019 会沿用 2018如果 2021 之后都是空沿用会一路带下去。因此用之前先tail看一眼末尾。指标代码名称时间频率在本次分析中的作用NY.GDP.MKTP.KD.ZG实际GDP年增长率年度最及时SDG 8.1 增长目标SI.POV.GINI基尼系数不固定年份不平等总览泰尔分解的对照组SI.POV.NAHC国家贫困线贫困率不固定年份研报中“贫困”的主口径SI.DST.FRST.20最底层20%收入份额不固定年份SDG 10 公平分配的代理指标提示PPP 基准年更新后WDI 里同一代码的历史值可能被重写。做跨年对比前先记录下载日期与数据版本避免图表里出现断崖式的口径跳变。4. 把研报问题重写成 SDG 目标追踪框架4.1 锁定与“公平发展”强相关的三类目标世界银行的研报把问题挂到了 SDG 上转成技术任务时不需要把 17 个目标全铺开只留和“增长 隔离 公平”直接相关的三类SDG 1无贫穷看国家贫困线贫困率的下降速度对应指标 1.1.1生活在国际贫困线以下的人口比例。SDG 8体面工作与经济增长看实际人均 GDP 增速是否持续为正且跑赢人口增长对应指标 8.1.1。SDG 10减少不平等看底层 40% 人口的收入增速是否高于整体平均增速10.1.1以及最底层 20% 的收入份额是否在下降。这三个目标组合起来正好覆盖“增长快但隔离加剧”的两个侧面增长由 SDG 8 撑住分配由 SDG 1 和 SDG 10 兜底。研报里如果出现“公平发展受阻”的表述在数据上的对应就是 8.1.1 为正、1.1.1 下降但 10.1.1 为负或第 2 章算的区域组间泰尔上行。4.2 构建指标记分卡在第 3 章面板数据基础上把三类指标整理成一张记分卡。WDI 里部分 SDG 指标没有直接换算用代理指标并标注口径是研报分析里的常规做法。sdg_map { SDG 1.1.1: (SI.POV.NAHC, decrease), SDG 8.1.1: (NY.GDP.MKTP.KD.ZG, increase), SDG 10.1.1: (SI.DST.FRST.20, increase), } def check_sdg(series, direction): s series.dropna() if len(s) 2: return 数据不足 delta s.iloc[-1] - s.iloc[-2] if direction increase: return 达标 if delta 0 else 偏离 return 达标 if delta 0 else 偏离 scoreboard [] for name, (code, direction) in sdg_map.items(): if code not in panel.columns: scoreboard.append({目标: name, 指标: code, 最新可获年份: None, 最近变化: None, 判定: 数据缺失}) continue series panel[code] latest_year panel.loc[series.last_valid_index(), year] scoreboard.append({ 目标: name, 指标: code, 最新可获年份: latest_year, 最近变化: round(series.dropna().iloc[-1] - series.dropna().iloc[-2], 3), 判定: check_sdg(series, direction), }) scorecard pd.DataFrame(scoreboard) print(scorecard)check_sdg不看最新值绝对值而是看最近两个可观测年份的变化方向贫困率下降、增长率上升、底层收入份额上升都判为合格。这样规避了绝对阈值设置的问题也强制使用者去注意“最近可获年份”是哪一年。latest_year是从最后一个有效值反推出来的写报告时务必标注因为不同指标的更新滞后可能差出两三年。实际跑出来SDG 8.1.1 的越南数据通常更新到最近年度而 SI.POV.NAHC 和 SI.DST.FRST.20 大概落后两三年。记分卡上的“数据缺失”本身就是一个发现你能看到增长但分配维度的数据滞后这恰好限制了“公平发展”的实时评估。4.3 用记分卡对照研报叙事证据链怎么闭合把三类指标按下面的表结构归档方便和研报段落做映射SDG代理指标理想方向读法1 无贫穷SI.POV.NAHC↓贫困率下降若停滞则增长未充分下沉8 经济增长NY.GDP.MKTP.KD.ZG↑高增长是研报的前提条件10 减少不平等SI.DST.FRST.20↑份额下降是“隔离加速”的信号10 补充维度省份泰尔组间占比↓组间占比上升即收入的空间隔离恶化最后一行“省份泰尔组间占比”来自第 2 章的微观计算WDI 里没有现成指标需要自行从家庭调查数据生成。把这一行放进记分卡才能把微观结论和 SDG 框架拉通。如果记分卡结果是 SDG 8 达标、SDG 1 改善、SDG 10 与空间泰尔同时恶化研报的结论就站得住经济增长确实在发生但分配维度走反了方向增长收益没有进入最贫困的那一端。5. 进阶验证用 Bootstrap 重抽样判断隔离加剧是否显著5.1 为什么直接看两个年份的差异不够第 2 章模拟里 2010 和 2025 年组间占比从 36% 涨到 53%要说“加剧”还得过显著性检验。家庭调查本身是抽样数据每年抽到的样本会有波动组间占比差 3 个百分点可能是样本噪声差 17 个百分点未必是。直接给两个点然后下结论审稿人通常会要求补置信区间。5.2 用分层重抽样构造置信区间常见做法是对家庭样本做非参数 Bootstrap有放回地重抽同样数量的行重复计算泰尔分解把组间占比的 2.5% 和 97.5% 分位数当作 95% 置信区间。为了保留省份结构按省份分层重抽样而不是全样本乱抽。def bootstrap_between_share(df, value_col, group_col, B2000, seed42): rng np.random.default_rng(seed) shares [] for _ in range(B): sampled [] for prov, g in df.groupby(group_col): g_boot g.sample(nlen(g), replaceTrue, random_staterng) sampled.append(g_boot) boot_df pd.concat(sampled, ignore_indexTrue) res theil_decomposition(boot_df, value_col, group_col, checkFalse) shares.append(res[between] / res[total]) return np.percentile(shares, [2.5, 97.5]) n_lo, n_hi bootstrap_between_share( sim_df[sim_df[year] 2025], income, province ) print(f2025 组间占比 95% CI: {n_lo:.1%} ~ {n_hi:.1%})B2000对应分位数估计的稳定性计算耗时的话可以降到 500但区间会明显抖动。replaceTrue是有放回的关键抽样行数等于原省份样本量保持省份规模不变。checkFalse是因为 Bootstrap 样本里可能出现极端抽样让断言在小数位上产生浮点误差正式判断不需要每次重跑 assert。5.3 结果怎么读模拟数据下2025 年区间大约落在 50% 到 60% 之间2010 年区间约在 33% 到 39%。如果两个区间没有重叠就可以写“组间占比上升在统计上显著”。注意这个显著只针对抽样波动不代表因果解释——越南的增长极效应、转移支付制度、教育分布都可能是背后驱动Bootstrap 不会替你回答这些问题。如果区间有重叠处理方式不是强行下结论而是回到数据侧检查两件事一是样本量是否足够支撑省级维度二是value_col是否选用了同一口径的消费或收入变量。真实场景下用 VHLSS 2010 和 2020 两波调查做同样操作时最影响区间宽度的往往不是 Bootstrap 次数而是两年调查问卷里支出模块的口径差异。本文还有配套的精品资源点击获取