Chapter7 - 文件导出与复杂曲线绘制
本章内容如下。学习导出数据文件,配合其他软件进行数据分析。NetLogo擅长仿真和模拟,并不擅长对数据做分析和统计,因此我们需要使用其他工具辅助分析。掌握洛伦兹曲线的概念。洛伦兹曲线能够非常方便地反映经济体系的财富分布不均衡现象。通过这样一条曲线,我们将很容易验证二八定律。绘制复杂曲线:洛伦兹曲线的若干编程技术。
人工经济模型回顾及遗留问题
首先回顾第6章引入的模型。为了解释人类社会财富分布的不均衡现象,物理学家Victor M. Yakovenko提出了一个简单的人工经济模型,该模型符合如下基本规则:
- 经济系统中人和财富的总量保持不变
- 开始的时候,每个人都有等量的货币
- 每当两个主体相遇,他们就随机分配财富
第6章也遗留了一些问题。首先,模拟得到的财富分布曲线是否满足帕累托分布?即是否遵循幂律分布?其次,这样一条财富分布曲线是否服从二八定律?本章将会回答这两个问题。
NetLogo导出文件
首先打开NetLogo,把财富分布数据导出到磁盘上的一个文件中。因为NetLogo分析数据的手段非常有限,所以我们需要借助其他工具来进行相应的统计分析。为了实现这个功能,需要创建一个新的按钮——save-flie
,创建好后,添加如下代码:
1 | to save-file |
file-open
是文件打开命令file-print
是向文件中打印内容file-close
是文件关闭命令word
是将两个甚至多个字符串拼接在一起。word value1 value2 value3 ...
- 相关其他命令需要查询和文件、字符串操作有关的命令
- 接下来就可以用MATLAB或其他你熟悉的工具,根据这些数据绘制简单的直方图了。
洛伦兹曲线
洛伦兹曲线(Lorenz curve)是经济学中一种反映财富分布不均衡现象的曲线,由美国经济学家M. O. 洛伦兹(Max Otto Lorenz)于1905年提出。
相较于前面讲到的概率分布函数曲线来说,洛伦兹曲线具有如下优点:
- 绘制它并不需要我们对数据进行财富区间分组处理
- 它所反映的财富分布情况比概率分布曲线更客观、更准确
- 基于它,我们更容易计算基尼系数,这是一种可以客观反映财富分布不均衡性的常用指标
接下来详细讲解如何绘制洛伦兹曲线。
首先用图形化的方式模拟一个国家的财富分布。假设这个国家一共有6个人,这些人的财富多少用身体大小来进行形象化的表示,如图所示。
在现在这种状态下,他们的财富分布很不均衡。
接下来通过3个步骤得到洛伦兹曲线。
第1步,把这6个人按照财富值从小到大排序,财富最少的人排在最左边,财富最多的人排在最右边,如图7-8所示。这样可以得到一条单调上升的曲线,但它并不是洛伦兹曲线。
第2步,对财富值进行累加。比如把前面3个人的财富之和跟第4个人的财富加在一起,形成一个高度,这个高度就是第4个人对应的洛伦兹曲线纵坐标的值。这样的话每个人在洛伦兹曲线上对应的纵坐标如图7-9所示,最后一个人即最富有的人,他所对应的纵坐标就是整个社会的财富总和。
第3步,将这条曲线的横坐标和纵坐标归一化。在横坐标上除以最大值,由于这个国家一共有6个人,因此每个点的横坐标都除以6,所以第5个人的横坐标就是5/6。同理,纵坐标也要除以社会的总财富。因此对于洛伦兹曲线来说,比如第i个点,它的横坐标就是从穷到富的前i/N的人数,其中N为社会总人数,纵坐标对应的是这前i/N的人所占有的财富与社会总财富的比例。图7-10所示的是最终的洛伦兹曲线。
图7-10的横坐标表示人口比例,纵坐标表示这些人所占财富的比例。洛伦兹曲线一定是这样一条弯曲的曲线,而且它一定会介于斜线A和竖线B与横坐标构成的三角形区域内部。
实际上,这两条特殊的线也有其经济含义,斜线A代表社会财富分布绝对均衡情况下的洛伦兹曲线。为什么可以这么说呢?我们可以想象,如果社会的财富分布极其均衡,即所有人的财富水平相等,那么当生成洛伦兹曲线的时候,一定是一条直线。
竖线B与横坐标构成的直角形折线,对应的就是社会财富分布极端不均衡的情况下的洛伦兹曲线。在这种情况下,一个人拥有全部社会总财富,而其他人的财富是0,所以这时只有最后一个人的纵坐标是1,而其他人都是0,因此刚好是这条折线。
由此可见,对于一般的随机的财富分布来说,洛伦兹曲线一定会介于这二者之间,而且越靠近折线,所反映的财富分布就越不均衡,越靠近斜线A财富分布就越均衡。
在这样一条洛伦兹曲线上很容易验证二八定律。那么如何验证呢?
我们可以作两条辅助线,首先找到横坐标为0.8的位置,这表示它对应的是社会按照从穷到富排序前80%的人口,找到它与洛伦兹曲线的交点,查看交点的纵坐标,这个纵坐标表示这80%的穷人所占据的财富比例。
用NetLogo实现洛伦兹曲线
首先把名称设置成Lorenz curve,然后设定它的横纵坐标分别为population %和wealth %,接下来设定3个不同颜色的画笔,它们的名称分别设定为lorenz、equal和dominant,3个画笔分别对应洛伦兹曲线、对角线和表示财富分布极端不均衡的折线。然后把绘图更新命令改成自定义函数update-lorenz-plot,并把绘图笔更新命令处默认的命令删除。
1 | to update-lorenz-plot |
绘图语句
NetLogo的图形化元素具有一定的层次结构,我们通常接触的有两个层次。
plot
代表绘图set-current-plot "plot-name"
表示把后面的绘图框激活,其中的名称跟设置的标题是一致的pen
代表画笔set-current-pen "pen-name"
每个图可能有多条曲线,比如目前要画的图有3条曲线,对应3个画笔。要完成某一条曲线的绘制就要激活相应的画笔。plot-pen-down
代表开始绘图plot-pen-up
代表停止绘图
绘图命令:
plot
这是一个使用频率比较高的指令,它的作用是等水平间隔地绘制点。
1 | clear-plot |
这里使用了plot语句来画图,第一步先清空当前绘图Lorenz curve。在每一个绘图周期都要重新清空,然后用set-current-plot-pen "equal"
语句激活“equal”画笔。我们在图形界面设置的equal画笔对应的是红色,因此这时绘制的线就是红色的。接下来的plot语句从坐标(0, 0)到(1, 1)绘制了一条红色对角线
plotxy
绘制洛伦兹曲线
1 | set-current-plot-pen "lorenz" |
- 确定间隔
要绘制这条曲线,我们要先设定横坐标平移的间隔量。由于这条洛伦兹曲线针对货币转移模型里的所有主体,人数在模型中是变量num_agents
,因此把洛沦兹曲线的横坐标划分成num_agents
份,那么每一份的宽度就是1 / num_agents
,于是用set-plot-pen-interval 1 / num_agents
来设置间隔量。
- 排序
sort命令,该命令的作用是对一个列表中的元素按从小到大进行排序。那么let sorted-wealths sort [money] of turtles
语句的列表就是[money] of turtles
,其作用是把所有主体的财富值构成一个大的数值列表。但是数值排序是乱的,而用sort命令就可以对数值列表按从小到大进行排序。因此sorted-wealths
这个变量就是排序好的财富值列表。
- 循环与求和
repeat num_agents[ ]
是一个循环过程,其中repeat是一个多次的循环,相当于Python语言中的for循环,紧随其后的是循环次数num_agents
。- 在每一次循环中,首先
set wealth-sum-so-far (wealth-sum-so-far + item index sorted-wealths)
语句给wealth-sum-so-far
赋值,wealth-sum-so-far
的含义相当于循环到第i个主体时积累的社会财富总量。这个量实际上是把前一次积累的财富总量加上当前主体所对应的财富量。 item idx lst
表示从list的列表中取出下标编号为idx的元素,比如当前列表是[0123],那么item 1 lst得到的数就应该是1。此处注意,在NetLogo语法里,它的列表下标始于0。item index sorted-wealths
,就是从排序好的财富列表里取出第index这个下标的元素。然后把取出的财富值加到wealth-sum-so-far
这个变量里,就得到了前i个主体的财富总量。
- 归一化
- 纵坐标是归一化的财富总量。有了当前第i个主体的归一化的财富总量,直接用
wealth-sum-so-far /total-wealth
就可以得到相应的纵坐标。
由图可知:这样的简单人工经济模型不满足二八定律
小结
- 学会对文件和字符串进行操作的若干命令
- 学会
for
循环和对应下标值 - 了解洛伦兹曲线
- 了解
plot
绘图的相关命令1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78turtles-own [money]
to setup
clear-all
reset-ticks
create-turtles num_agents[
set money (total_money / num_agents) ;; 初始情况下每个turtle都拥有相同的货币量
setxy random-xcor random-ycor ;; 设置为随机取值,实现随机分布
]
end
to go
ask turtles [
let agsets other turtles-here ;; 其他所有turtles-here
let agentNum count agsets
if agentNum >= 1 [
transaction (one-of agsets)
]
forward 1
]
tick
end
to transaction [trader]
let deltam 0
let money1 ([money] of trader)
let epsilon (random-float 1) ;; 定义epsilon为一个0~1的随机数。前面用过random-float,它会产生一个0~1的随机小数
let all money + money1
set money epsilon * all
ask trader [
set money (1 - epsilon) * all
]
end
to to-update-plot
let lst [money] of turtles
set-histogram-num-bars 100
if not empty? lst [
set-plot-x-range 0 (max lst)
histogram lst
]
end
to save-file
file-open "agent.txt"
let wealths ""
ask turtles[
set wealths (word wealths money "\r\n")
]
file-print wealths
file-close
end
to update-lorenz-plot
clear-plot
;; 绘制表示财富分布绝对均衡的斜线
set-current-plot-pen "equal"
plot 0
plot 1
;; 绘制表示财富分布极端不均衡的折线
set-current-plot-pen "dominant"
plot-pen-down ;; plot-pen-down:开始绘图。
plotxy 0 0
plotxy 1 0
plotxy 1 1
plot-pen-up ;; plot-pen-up:停止绘图。
set-current-plot-pen "lorenz"
set-plot-pen-interval 1 / num_agents ;; 按照x的一定间隔进行划分
plot 0
let sorted-wealths sort [money] of turtles ;; 对数组进行排序
let total-wealth sum sorted-wealths ;; 对数组进行求和
let wealth-sum-so-far 0
let index 0
repeat num_agents[ ;; 在for循环中循环num_agents次
set wealth-sum-so-far (wealth-sum-so-far + item index sorted-wealths)
plot (wealth-sum-so-far / total-wealth)
set index (index + 1)
]
end