基尼系数(Gini Index)是经济学中衡量一个国家或地区居民贫富差距程度的常用指标。当以居民收入数据为依据计算时,所得结果为收入基尼系数,而以居民财产数据为基础计算时,所得为财富基尼系数。

  本例我们将假设一个只有10位居民的“小国”,以该国10位居民的财产数据为基础,计算该“国”的财富基尼系数。

版权声明

本文节选自《Python编程基础及应用》第2版,高等教育出版社,作者:陈波,刘慧君

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。本文不可以以纸质出版为目的进行改编、摘抄。

image-20240413120345004

图1 基尼系数的计算

  我们借助图1来说明基尼系数的计算过程。 图中的横轴为人群百分比,纵轴为财富百分比。虚线则为洛伦兹曲线,它表示社会中最贫穷的百分比人群所拥有的财富占社会总财富的百分比。以图中洛伦兹曲线上的黑点为例,该黑点对应横坐标40%,纵坐标11%,表示社会中最穷的40%人口只拥有社会总财富的11%,显然,该社会的财富分配是不均衡的。

  如果一个国家的财富是完全平均分配的,即所有居民都拥有相同金额的财富,则图1中的洛伦兹曲线将与45度倾斜的完全平等曲线重合。因为社会最穷的20%居民拥有社会总财富的20%,最穷的70%居民拥有社会总财富的70%…… 完全平等曲线上任意一点的横纵坐标值均相等,正好与该情形对应。

  社会财富分配越平均,洛伦兹曲线越接近完全平等曲线,图中A区域的面积越小,B区域的面积越大;社会财富分配越不均衡,贫富差距越大,洛伦兹曲线越远离完全平等曲线,图中A区域的面积越大,B区域的面积越小。根据上述特性,基尼系数被定义为:基尼系数 = A/(A+B)。

  其中的A、B分别代表图中A、B区域的面积。当社会财富完全平均分配时,洛伦兹曲线与完全平等曲线重合,A=0,基尼系数等于0。当社会财富集中在少数富人手中时,洛伦兹曲线无限远离完全平等曲线,B变得很小,基尼系数趋近于1。基尼系数越大,贫富分化越严重。

  如图1所示,A和B两个区域合起来正好是一个底为1(100%)、高为1的三角形,故A+B=0.5。实践中,我们只需对洛伦兹曲线进行积分,计算B区域面积,再用0.5减去B,即得A。

  按前述方法,我们编程计算了一个10人小国的基尼系数。程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#gini.py
w = [5,25,100,15,10,90,70,50,60,80]
sampleCount = len(w) #样本总数,即人数
w.sort() #从小(穷)到大(富)排序
print("财富列表:",w) #有序的财富列表
cw = []
for i in range(sampleCount): #计算累积财富值
cw.append(sum(w[:i+1])) #cw[i]表示社会最穷的i+1个人的总财富
print("财富列表排序累积结果:",cw)

totalWealth = cw[-1] #社会总财富
for i in range(len(cw)): #计算累积财富占比
cw[i] = cw[i] / totalWealth #cw[i]表示社会最穷的i+1个人的财富占比
cw = [0] + cw #加0: 0%的人拥有0%的财富
cp = [0] + [i/sampleCount for i in range(1,sampleCount+1)]
print("人群占比:", " ".join([f"{x:.2f}" for x in cp]))
print("财富占比:", " ".join([f"{x:.2f}" for x in cw]))

B = 0 #梯形法累加计算B区域面积
for i in range(1,sampleCount+1):
B = B + (cw[i] + cw[i-1]) * (cp[i] - cp[i-1]) / 2
A = 0.5 - B #A+B = 0.5
print(f"财富基尼系数: {A/(A+B):.2f}")

上述程序的执行结果为:

1
2
3
4
5
财富列表: [5, 25, 100, 15, 10, 90, 70, 50, 60, 80]
财富列表排序累积结果: [5, 15, 30, 55, 105, 165, 235, 315, 405, 505]
人群占比: 0.00 0.10 0.20 0.30 0.40 0.50 0.60 0.70 0.80 0.90 1.00
财富占比: 0.00 0.01 0.03 0.06 0.11 0.21 0.33 0.47 0.62 0.80 1.00
财富基尼系数: 0.37

🚩第2行:w为财富列表,其内存储了该国10位居民各自拥有的财富值。

🚩第3行:sampleCount意为样本数,即为该国的居民总数。本例中,其值为10。

🚩第4行:对财富列表进行排序,默认为递增序,即较穷的个体排在较富的个体前面。

🚩第5行:打印排序后的财富列表,见执行结果第1行。

🚩第6 ~ 9行:对w列表中的财富值进行累积计算,得累积财富列表cw。其中,sum(w[:i+1])使用切片语法计算财富列表前i项的和。计算完成后,cw[i]表示社会中最穷的i+1个人所拥有的财富总额。如执行结果的第2行所示,cw[0]=5,表示社会中最穷的1个人拥有的财富为5;cw[2]=30,表示社会中最穷的3个人拥有的财富总额为30(5+10+15);而cw[9]=505,表示社会中最穷的10个人,即全体居民的财富总额为505。

  读者可能对代码和上述解释中的i+1部分感到疑惑,请注意列表的下标从0开始计数且切片w[:i+1]的结果序列不包含位于下标i+1的元素。

🚩第11行:cw[-1]为cw列表中的倒数第1个元素,即cw[9],正好等于全体居民的财富总额,即所谓社会总财富。

🚩第12 ~ 13行:依次将cw[i]除以社会总财富,得累积财富占比。计算完成后,cw[i]表示社会最穷的i+1个人所拥有的财富占社会总财富的比例。

🚩第14行:在cw排头处加入一个0,表示社会最穷的0个人拥有社会总财富的0%。

🚩第15行:计算与累积财富占比列表cw对应的人群占比列表cp。同样地,cp的排头处也加了一个0,意为0%的人群,与cw[0]相对应。cp[0]=0、cw[0]=0合起来表示社会最穷人群的0%占有社会总财富的0%。

🚩第16 ~ 17行:打印人群占比列表cp和财富占比列表cw,请见执行结果的第3 ~ 4行。从中可见,社会中最穷的40%(0.4)人口拥有社会总财富的11%(0.11)。

  如果把cp视为横坐标,cw视为纵坐标,即可得到平面上的10个点,将这10个点串接绘成折线图,即为图1中的洛伦兹曲线。

🚩第19 ~ 21行:计算B区域的面积。

  如图1所示,洛伦兹曲线下方的区域B可以分割成多个规则的梯形,按照梯形面积公式“上底加下底乘以高除以2”依次计算各梯形的面积并累加即得B区域面积。读者可能注意到,左下方最靠近坐标系原点的梯形事实上是一个三角形,这并不影响我们的计算设计,因为三角形可以视为上底等于0的特殊梯形。

  在循环过程中,i从1开始计数,cw[i]对应梯形下底,cw[i-1]为梯形上底,cp[i]-cp[i-1]为梯形的高。

🚩第22行:根据公式A+B=0.5,计算A区域的面积。

🚩第23行:按公式计算并打印财富基尼系数,请见执行结果第5行。

  以前述计算为基础,程序gini.py的后续部分绘制了财富基尼系数计算示意图1。关于matplotlib绘图,我们将在第10章中详细讨论,此处仅概要描述绘图过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#gini.py的后续部分,从第24行开始
import matplotlib.pyplot as plt, matplotlib.ticker as ticker
plt.rcParams["font.family"] = "SimHei"
plt.rcParams["font.size"] = 11
fig,ax = plt.subplots(figsize=(6,6))
ax.plot(cp,cw,color='black',linestyle="--",label="洛伦兹曲线")
ax.plot(cp[4],cw[4],"o",color="black")
ax.text(cp[4]-0.07,cw[4]+0.05,f"{cp[4]*100:.0f}%,{cw[4]*100:.0f}%")
ax.plot(cp,cp,color='black',label="完全平等曲线")
for x,y in zip(cp,cw):
ax.plot([x,x],[0,y],linestyle="--",color='black')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
ax.set_xlabel("人群占比")
ax.set_ylabel("财富占比")
ax.set_title("基尼系数的计算")
ax.fill_between(cp,y1=cp,y2=cw,facecolor='red',alpha=0.2) #填空A区
ax.fill_between(cp,y1=cw,y2=0,facecolor='green',alpha=0.2) #填空B区
ax.text(0.5,0.4,"A",fontsize=20)
ax.text(0.75,0.1,"B",fontsize=20)
ax.xaxis.set_major_formatter(ticker.PercentFormatter(xmax=1))
ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1))
ax.legend()
plt.show()

🚩第26 ~ 27行:设置中文字体及字号,以便正确显示中文。

🚩第28行:创建一个宽6英寸、高6英寸的图fig及子图ax。

🚩第29行:以cp列表为横坐标,cw列表为纵坐标绘制折线图,线型(linestyle)为虚线,标签(label)为“洛伦兹曲线”。

🚩第30行:在cp[4]、cw[4]的位置画一个圆点。

🚩第31行:为前述圆点添加坐标值,即图中的40%,11%。该值说明最穷的40%人口只拥有社会总财富的11%。

🚩第32行:以cp列表为横纵坐标绘制完全平等曲线,由于横纵坐标相等,该曲线事实上为45度倾斜的直线。

🚩第33 ~ 34行:以虚线绘制图中B区域各梯形的边线。

🚩第35 ~ 36行:设置子图ax的横纵坐标范围为0 ~ 1。

🚩第37 ~ 39行:设置子图ax的横纵坐标标签,以及子图标题。

🚩第40 ~ 41行:分别以浅红、浅绿填充图中A、B区域。

🚩第42 ~ 43行:在图中添加A、B标识文字。

🚩第44 ~ 45行:设置子图的横纵坐标以百分比形式显示。

🚩第46行:显示图示,见图1左上角。

🚩第47行:显示后台绘制好的图表。

练习

  (基尼系数) 在随书源代码的CH5子目录下,w10676.py存储了某地10676位居民的财产数据。请以该数据为基础,计算该地的财富基尼系数。

  注:上述10676位居民的财产数据事实上源自“真实”的社会金融调查数据,通过该数据计算而得的财产基尼系数真实反应了该国的贫富分化情况。由于数据过于惊人,不便展示,请读者自行计算。

代码及数据下载

http://codelearn.club/download/ginidata.zip