1 绪论

1.1 原文内容简述

  • 在 “Talent vs Luck: the role of randomness in success and failure” 这篇文章中,作者假设社会中人的才能服从正态分布,而人的资产服从长尾分布。作者使用数学模型模拟了运气因素在社会中的作用机制,建立人-事件-资产的交互模型量化了运气因素的作用效果。实验内容如下:
  • 将1000个代表人类个体的点随机分布在一个二维空间中,实验模拟环境示意图如下。人类个体不会移动、重生或死亡。在模拟程序初始化时,为每个人类个体分配相同的资产值 $C = 10$ 。每个人类个体分配一个才能参数 $T$ , $T$ 服从参数 $\mu = 0.6$ 、 $\sigma = 0.1$ 的正态分布。才能值一旦确定终生不可变化,由于正态分布中会出现小于0和大于1的值,在此将所有小于0的值一律改为0,将所有大于1的值一律改为1,确保 $T$ 值在 $[0,1]$ 范围内。

实验模拟环境示意图

  • 一次模拟相当于40年的社会演化,代表人类个体由20岁到60岁的历程。将一次模拟分成80次迭代过程,一次迭代表示6个月的演化。每一次迭代开始前,初始化500个幸运事件和500个不幸事件随机分布在地图中,如上图,其中绿色的点代表幸运事件,红色的点代表不幸事件。
  • 当个体遇到幸运事件,即绿色的点出现在个体点的附近时,通过随机数与才能值的比较决定资产是否翻倍,公式如下:
  • 当个体遇到不幸事件,即红色的点出现在个体点的附近时,资产值立即减半,公式如下:
  • 实验结果如下,尽管人类个体的才能服从正态分布,但经过40年的模拟演变后,人类个体的资产值呈现出高度的不对称性,且符合帕累托法则。才能最高的个体最终资产值仅处于中间位置,而资产值最高的个体的才能值也仅处于中等水平。这一结果证明了当前社会是非精英化的,运气因素对于个体的成功起到重要作用。

资产-个体分布图
资产-才能分布图

1.2 实验缺陷

1.2.1 假设简单

  • 整个模拟实验建立在诸多假设条件下。有些假设是基于统计数据的,如个体才能服从正态分布以及个体资产服从长尾分布。但仍有部分假设缺少理论或事实支持,同时作者并未解释做出这些假设的原因,仅仅是作为默认条件避而不谈,部分列举如下:
    • 为什么选择正方形的二维空间作为模拟环境?
    • 为什么不考虑初始资产值的分布,一律初始为10?
    • 为什么资产的变化仅与幸运事件和不幸事件有关?
    • 为什么资产的变化方式只能是翻倍和减半?
    • 为什么作者相信个体在幸运事件中增加资产要靠才能,却不相信个体在不幸事件中能够依靠才能避免资产减少?

1.2.2 结论草率

  • 文章中仅展示了一组参数条件下模型的结果,以此断定模型准确模拟了真实世界的情况,这样的结论是不具有说服力的。更为合理的做法是改变模型参数,如才能值正态分布的参数、幸运事件与不幸事件的比例、初始资产的值等等,进行多次试验,综合不同参数条件下的实验结果评价模型的模拟能力。

2 实验设计

2.1 模型界面

  • 本文使用 python 语言对原文模型进行复现,模型界面如下。包含参数设置区、图表选择区、时序滑块和图表展示区四部分。 GUI 部分由 PyQt 库实现,图表部分由 pyecharts 库实现,代码见文末。

模型界面

  • 参数设置区用于设置模型参数,包括地图规模、人数、幸运事件数量、不幸事件数量、迭代次数、才能值分布的均值和方差、图表粒度。其中图表粒度表示柱状图中划分的区间个数。
  • 图表选择区用于选择展示的图表,包括才能-个体柱状图 (T-N) 、幸运事件-个体柱状图 (L-N) 、资产-个体柱状图 (C-N) 、资产-才能散点图 (C-T) 、才能-资产散点图 (T-C) 、幸运事件-资产散点图 (L-C) 、资产-幸运事件散点图 (C-L) 、不幸事件-资产散点图 (U-C) 、资产-不幸事件散点图 (C-U) 、资产最高者-时间折线图 (Cmax-Time) 、资产最高者-事件折线图 (Cmax-Event) 。
  • 时序滑块用于选择某一次迭代后的图表。为了观察图表中两个变量的演变规律,程序在每一次迭代后绘制一份统计图,通过时序滑块进行选择。
  • 图表展示区用于显示指定的某一类型、某一次迭代对应的统计图。

2.2 实验内容

  • 考虑到对真实世界的准确模拟十分困难,原文的模型设计过于简陋,而本人又没有学习过社会学方面的知识,因此不对原文模型进行大幅度的改进。此外,模型涉及的参数较多,想要准确把握每个参数对模型能力的影响需要进行大量的对比实验,难以操作。因此实验内容定为继承原文的模型设计,分别增加额外的假设条件,观察模型的模拟结果。

2.2.1 无额外假设

  • 本次实验不对原文实验条件做改变。模拟参数及结果如下。

才能-个体柱状图
资产-个体柱状图
资产-才能散点图
资产-幸运事件散点图
资产-不幸事件散点图
资产最高者-事件折线图

  • 根据才能-个体柱状图,个体才能值符合均值为0.6、标准差为0.1的正态分布。
  • 根据资产-个体柱状图,资产值在122.88左右的有980人,资产值在384左右的有8人,资产值在640左右的有3人,资产值在1152左右的有3人,资产值在2432左右的有1人,资产值在4992左右的有3人,资产值在10112左右的有2人。计算表明,80%的个体占有者59.04%的资产,虽然贫富差距明显,但没有达到帕累托法则的8:2的比例。
  • 根据资产-才能散点图,在资产值较低的群体中,才能值分布均匀。而对于资产值较高的少数个体,虽然其才能值不是最高的,但均是处于中上游的。
  • 根据资产-幸运事件散点图、资产-不幸事件散点图和资产最高者-事件折线图,可以总结资产最高者的特征,即自身才能值较高,相对遇到更多的幸运事件和更少的不幸事件。在这之中才能和运气都起到了重要作用。
  • 值得注意的是,由于贫富差距明显,资产值方差较大,使得柱状图由于等距划分区间而呈现出畸形状态,不易理解。因此对资产值进行以2为底的对数变换,重新绘制统计图,结果如下。

对数资产-个体柱状图
对数资产-才能散点图
对数资产-幸运事件散点图
对数资产-不幸事件散点图

  • 根据对数资产-个体柱状图,对数资产值的分布表现出正态分布的特征。这仅是对统计图直接观察的结果,以我所掌握的数学技巧不足以计算或证明对数资产值的真实分布。
  • 根据对数资产-才能散点图、对数资产-幸运事件散点图,对数资产值与才能值和幸运事件数的关系并不明显。而根据对数资产-不幸事件散点图,对数资产值与不幸事件数存在较明显的线性关系,对比幸运事件和不幸事件中的资产变化公式,可知是由于不幸事件中资产值的无条件减半使得不幸事件对资产值的影响力高于幸运事件。

2.2.2 抵抗力模式

  • 考虑一定程度上减小上述实验中幸运事件与不幸事件对资产值影响力的差距,本次实验中,根据个体自身才能值使个体具有一定能力避免在不幸事件中损失资产,类比在幸运事件中的资产增加公式,在不幸事件中的资产减少公式定义为:模拟参数及结果如下:

资产-个体柱状图
资产-才能散点图

  • 根据资产-个体柱状图,资产值在245.76左右的有989人,资产值在768左右的有2人,资产值在1280左右的有2人,资产值在2304左右的有3人,资产值在4864左右的有2人,资产值在9984左右的有1人,资产值在20224左右的有1人。计算表明,80%的个体占有者66.87%的资产。
  • 根据资产-才能散点图,分布规律没有明显改变,资产值较高的少数个体才能值仍处于中上游的。
  • 将资产值做对数变换后,模拟结果如下。对比2.2.1节的模拟结果,显然不幸事件与对数资产值的线性关系有所减弱,表明个体对不幸事件具有抵抗力后,不幸事件对资产值的影响力得以减小。但此时对比下面两图,发现不幸事件对资产值的影响力仍高于幸运事件对资产值的影响力,这一现象在本次实验中无法解释,需要进一步研究。

对数资产-幸运事件散点图
对数资产-不幸事件散点图

  • 在抵抗力模式中,由于个体具有了避免资产损失的可能性,因此总体的资产值水平有所上升。但贫富差距并没有明显降低,因为资产值较高的少数个体才能值均为中上游,在不幸事件中的抵抗力也是相对较强的。同时,模拟结果对实验方向产生了新的启发,即不仅研究运气因素对资产值的影响,还需要进一步研究幸运与不幸两种运气成分影响力的区别。

2.2.3 能力决定模式

  • 本次实验中,不以才能值作为概率阈值,资产值的变化方式不再是简单的翻倍和减半,而是直接与才能值相关。在幸运事件中资产值的变化公式定义为:在不幸事件中资产值的变化公式定义为:模拟参数及结果如下:

资产-个体柱状图

  • 根据资产-个体柱状图,资产值在5.213左右的有987人,资产值在16.2905左右的有2人,资产值在27.151左右的有4人,资产值在38.0115左右的有2人,资产值在92.314左右的有2人,资产值在103.1745左右的有1人,资产值在222.64左右的有1人,资产值在428.989左右的有1人。计算表明,80%的个体占有者84.41%的资产。
  • 将资产值做对数变换后,模拟结果如下。对比2.2.1节的模拟结果,发现才能值与对数资产值之间产生了比较明显的正相关关系,表明在才能值直接参与资产变化公式时,才能值对资产值的影响力得到增强。

对数资产-才能散点图

  • 在能力决定模式中,贫富差距有了明显的缩小。从才能值的平均水平0.6来理解,才能值直接影响资产变化,减小了资产值的增长幅度,同时也减小了资产值的损失比例,有效地抑制了贫富差距向两端扩张。但从个体来看,对于资产值最高的是的少数个体,由于才能值处于中上游,资产值可以保证稳定增长,而对于才能值低于平均水平的个体,资产的减少幅度要大于资产的增长幅度,从而呈现出资产不断减少的趋势。因此能力决定模式的缺陷是对弱势群体没有足够的保障。

2.2.4 工资模式

  • 在真实世界中,幸运事件与不幸事件只是影响资产变化的诸多因素之一。在日常生活中,人们的资产变化通常是由于工作带来的工资收入。因此在本次实验中为每个个体增加一个额外参数 (\omega) ,表示个体的工作强度,工作强度服从均值为5、标准差为1的正态分布,工作强度与才能值的乘积作为该个体的工资收入,在每次迭代中都加入资产值,公式如下:模拟参数及结果如下:

资产-个体柱状图

  • 根据资产-个体柱状图,资产值在1581.4865左右的有995人,资产值在4941.4615左右的有2人,资产值在11529.6485左右的有1人,资产值在14823.472左右的有1人,资产值在130117.0135左右的有1人。计算表明,80%的个体占有者72.71%的资产。
  • 将资产值做对数变换后,模拟结果如下。对比2.2.1节的模拟结果,发现不幸事件数与对数资产值之间的线性关系得到削弱,表明一定的工资收入也能够减小不幸事件对资产值的影响力,与2.2.2节的抵抗力模式有异曲同工之处,且工资模式的作用效果明显强于抵抗力模式。

对数资产-不幸事件散点图

  • 直观上理解,加入了工作强度这一因素,使得个体同样具有了应对不幸事件中资产损失的抵抗了,与抵抗力模式不同的是,工资模式相对弱化了才能值的决定作用,即使才能值较低的个体也可通过具备较高的工作强度来提高工资水平,相当于具备在低谷时期反弹逆袭的能力。

2.2.5 钱运合一模式

  • 原文中默认所有个体遭遇幸运事件和不幸事件的概率相同,而本次实验中将考虑另一种情况,即资产值较高者遇到幸运事件的概率相对较高,资产值较低者遇到不幸事件的概率相对较高。在进行程序设计时将遇到事件的概率量化为个体的事件感知范围,对于资产值高于平均水平的个体,幸运事件的感知范围扩大到以自身为中心的2x2正方形区域,对于资产值低于平均水平的个体,不幸事件的感知范围扩大到以自身为中心的2x2正方形区域。模拟参数及结果如下。

资产-个体柱状图
资产-幸运事件散点图
资产-不幸事件散点图
资产最高者-事件折线图

  • 根据资产-个体柱状图,资产值在15.36左右的有998人,资产值在624左右的有1人,资产值在1264左右的有1人。计算表明,80%的个体占有者71.37%的资产。
  • 根据资产-幸运事件散点图和资产-不幸事件散点图,资产值较高的少数个体更依赖于运气因素。他们遇到幸运事件的次数不再只是在中上游位置,而是遇到幸运事件次数最多的几个人。同样,他们遇到不幸事件的次数也不再只是在中下游位置,而是遇到不幸事件次数最少的几个人。
  • 根据资产最高者-事件散点图,也可以看出资产最高的人更依赖运气因素。相比于前几次实验的资产最高者,该个体遇到了更多的幸运事件和更少的不幸事件。
  • 将资产值做对数变换后,模拟结果如下。根据对数资产-幸运事件散点图,幸运事件数与对数资产值的整体关系仍不明显,但对于资产值较高的少数个体,其幸运事件数与对数资产值之间存在明显的正相关关系。根据对数资产-不幸事件散点图,不幸事件数与对数资产值之间仍存在明显的正相关关系,且对比2.2.1节的模拟结果,钱运合一模式下的正相关关系表现更强。

对数资产-幸运事件散点图
对数资产-不幸事件散点图

  • 在钱运合一模式中,将运气与资产值绑定,强化了运气因素在资产值变化中的决定性作用。

2.2.6 税收模式

  • 考虑强制收税可作为减小贫富差距的有力手段,本次实验中将根据个体的资产值情况,在个体遇到幸运事件资产增长时收取不同的税款。税收规则定为:资产值前10%的个体收取50%的税款,资产值前50%的个体收取30%的税款,其余个体收取10%的税款,税收公式如下:

模拟参数及结果如下:

资产-个体柱状图
资产-才能散点图
资产-幸运事件散点图
资产最高者-事件折线图
对数资产-幸运事件散点图
对数资产-不幸事件散点图

  • 根据资产-个体柱状图,资产值在0.0259左右的有996人,资产值在0.0273左右的有1人,资产值在0.0298左右的有1人,资产值在0.674左右的有1人,资产值在2.135左右的有1人。计算表明,80%的个体占有者72.29%的资产。
  • 根据资产-才能事件散点图和对数资产-才能事件散点图,可以看到在税收模式下,资产最高者的才能不再高于平均水平,而是处于中下游位置。
  • 根据资产-幸运事件散点图和对数资产-幸运事件散点图,可以看到在税收模式下,资产最高者遇到幸运事件的次数也不再高于平均水平,甚至接近于次数最低。
  • 根据资产最高者-时间折线图,资产最高者的资产变化不再呈上升趋势,而是呈现出高峰后不断被削减的趋势。
  • 经过多次模拟结果大同小异,显然,加入税收规则对资产的变化规律产生了巨大的影响。首先,税收对资产值有很强的抑制作用,整个群体的资产值都远远小于初始资产值。其次,税收减小了才能和运气对资产的影响力,资产值较高的少数个体都不是在才能和运气上占优势的人,甚至处于偏弱势的地位。尽管如此,根据对数资产-不幸事件散点图,不幸事件的影响力并没有得到十分明显的削弱。

3 总结

  • 经过上述实验,可以看出原文中三位作者得出的结论其实并不严谨。
  • 首先,贫富差距确实存在,每一次的资产-个体柱状图都是如此畸形,但资产值奇高的毕竟只是少数几个个体,因为我们无法避免在此种随机条件下一个群体中总有几个运气极好的人存在。我不认为这是严格的贫富差距,贫富差距应该是一个大的社会群体中几个小的社会群体之间资产值的明显差距。但本次实验得到的结论仅仅是群体中存在几个资产值奇高的个体,仅仅是因为他们的才能值略高于平均水平,他们有幸遇到了更多的幸运事件和更少的不幸事件。
  • 其次,帕累托法则不是铁律。经过多次实验,虽然存在贫富差距,但远没有帕累托法则的8:2一般悬殊的差距。二八定律仅仅是一个模糊的统计结果,在分析具体问题时没有太大的参考价值,它仅仅是提供了一个社会学启示,告诉人们要分清主要矛盾和次要矛盾。因此模拟结果符合二八定律不足以证明模型的准确度,我甚至怀疑原文作者是经过千挑万选找到了某一次符合二八定律的结果并附在文章中。
  • 最后,该实验也无法证明运气抑或才能的决定性作用。通过上述实验可以看出,仅仅额外添加一个貌似合理的假设,就能或多或少地改变运气和才能的影响力。这归因于模型设计的简陋,它包含了太少的假设,仅有的这些假设也不具有很强的说服力,而除此之外仍有太多太多需要考虑的因素,如真实世界中的资产继承、社会保障、债务等等。正是由于无法考虑并量化这些因素,才使得当前的模型如此脆弱。在这个简单的模型中,显然是放大了运气因素的作用力,因此原文作者否定精英社会的理由也不成立。在这个模型中无法形成精英社会的原因是精英个体数太少,当运气的作用力被放大时,最幸运的个体往往出现在个体偏集中的中游位置,最终导致资产最高者往往是才能值略高于平均水平的个体。
  • 原文提出了一个很好的想法,即研究运气因素在资产变化过程中的影响力。但其实验思路、实验模型、实验结果缺少社会学和经济学的理论支持,以至于连我这个毫无社会学和经济学基础的人也无法相信。期待作者后续的工作。

4 源码

  • 编程环境:python3.6.4 + pyqt5.6.0 + pyecharts0.5.11
# -*- coding: utf-8 -*-  
import sys,random,os
from copy import deepcopy
import numpy as np
import pandas as pd
from pyecharts import Bar,Scatter,Line
from PyQt5 import QtCore, QtGui
try:
from PyQt5.QtWebKitWidgets import QWebView as QView
'''
some pyqt5 version only support QtWebKitWidgets, some only support QtWebEngineWidgets.
i dont know why. so just change the import when occur error.
'''
except:
from PyQt5.QtWebEngineWidgets import QWebEngineView as QView
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton, QGridLayout, QLabel, QComboBox, QFrame, QVBoxLayout, QSlider, QGraphicsView

class TVL(QWidget):

def __init__(self,parent=None):
QWidget.__init__(self)
self.capitalLog = []
self.luckyLog = []
self.unluckyLog = []
self.lucky = []
self.unlucky = []
self.capital = []
self.sitex = []
self.sitey = []
self.talent = []
self.displayTarget = ""
self.epochs = 0
self.displayGrand = 0
self.outdir = "file:///"+os.getcwd().replace('\\','/')+"/data/"
self.setWindowTitle('Talent vs Luck')
self.setWindowIcon(QtGui.QIcon('title.ico'))
self.resize(851, 535)
self.setMinimumSize(851, 535)
self.setMaximumSize(851, 535)
self.gridLayoutWidget = QWidget(self)
self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 191, 511))
self.gridLayout_2 = QGridLayout(self.gridLayoutWidget)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.M_lab = QLabel("地图规模",self.gridLayoutWidget)
self.M_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.M_lab, 0, 0, 1, 1)
self.T_avg = QComboBox(self.gridLayoutWidget)
for i in range(1,10):
self.T_avg.addItem(str(i/10))
self.gridLayout_2.addWidget(self.T_avg, 5, 1, 1, 1)
self.avg_lab = QLabel("talent均值",self.gridLayoutWidget)
self.avg_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.avg_lab, 5, 0, 1, 1)
self.T_std = QComboBox(self.gridLayoutWidget)
for i in range(1,10):
self.T_std.addItem(str(i/10))
self.gridLayout_2.addWidget(self.T_std, 6, 1, 1, 1)
self.std_lab = QLabel("talent标准差",self.gridLayoutWidget)
self.std_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.std_lab, 6, 0, 1, 1)
self.grand_lab = QLabel("图表粒度",self.gridLayoutWidget)
self.grand_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.grand_lab, 7, 0, 1, 1)
self.grand = QComboBox(self.gridLayoutWidget)
for i in range(1,5):
self.grand.addItem(str(i*10))
self.gridLayout_2.addWidget(self.grand, 7, 1, 1, 1)
self.luck = QComboBox(self.gridLayoutWidget)
for i in range(1,10):
self.luck.addItem(str(i*100))
self.gridLayout_2.addWidget(self.luck, 2, 1, 1, 1)
self.step = QComboBox(self.gridLayoutWidget)
for i in range(1,7):
self.step.addItem(str(i*50))
self.gridLayout_2.addWidget(self.step, 4, 1, 1, 1)
self.map = QComboBox(self.gridLayoutWidget)
for i in range(1,7):
self.map.addItem(str(i*100)+"x"+str(i*100))
self.gridLayout_2.addWidget(self.map, 0, 1, 1, 1)
self.unluck = QComboBox(self.gridLayoutWidget)
for i in range(1,10):
self.unluck.addItem(str(i*100))
self.gridLayout_2.addWidget(self.unluck, 3, 1, 1, 1)
self.N_lab = QLabel("人数",self.gridLayoutWidget)
self.N_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.N_lab, 1, 0, 1, 1)
self.unluck_lab = QLabel("不幸事件",self.gridLayoutWidget)
self.unluck_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.unluck_lab, 3, 0, 1, 1)
self.luck_lab = QLabel("幸运事件",self.gridLayoutWidget)
self.luck_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.luck_lab, 2, 0, 1, 1)
self.s_lab = QLabel("迭代次数",self.gridLayoutWidget)
self.s_lab.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout_2.addWidget(self.s_lab, 4, 0, 1, 1)
self.N = QComboBox(self.gridLayoutWidget)
for i in range(1,7):
self.N.addItem(str(i*500))
self.gridLayout_2.addWidget(self.N, 1, 1, 1, 1)
self.line = QFrame(self)
self.line.setGeometry(QtCore.QRect(210, 10, 20, 511))
self.line.setFrameShape(QFrame.VLine)
self.line.setFrameShadow(QFrame.Sunken)
self.line_2 = QFrame(self)
self.line_2.setGeometry(QtCore.QRect(340, 10, 20, 511))
self.line_2.setFrameShape(QFrame.VLine)
self.line_2.setFrameShadow(QFrame.Sunken)
self.verticalLayoutWidget = QWidget(self)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(230, 10, 101, 511))
self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.start = QPushButton("Start",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.start)
self.TN = QPushButton("T-N",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.TN)
self.LN = QPushButton("L-N",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.LN)
self.CN = QPushButton("C-N",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CN)
self.CT = QPushButton("C-T",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CT)
self.TC = QPushButton("T-C",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.TC)
self.LC = QPushButton("L-C",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.LC)
self.CL = QPushButton("C-L",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CL)
self.UC = QPushButton("U-C",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.UC)
self.CU = QPushButton("C-U",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CU)
self.CmaxTime = QPushButton("Cmax-Time",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CmaxTime)
self.CmaxEvent = QPushButton("Cmax-Event",self.verticalLayoutWidget)
self.verticalLayout_2.addWidget(self.CmaxEvent)
self.verticalLayoutWidget_2 = QWidget(self)
self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(360, 10, 471, 511))
self.verticalLayout_3 = QVBoxLayout(self.verticalLayoutWidget_2)
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
self.slider = QSlider(self.verticalLayoutWidget_2)
self.slider.setMinimum(0)
self.slider.setOrientation(QtCore.Qt.Horizontal)
self.verticalLayout_3.addWidget(self.slider)
self.screen = QView(self.verticalLayoutWidget_2)
self.verticalLayout_3.addWidget(self.screen)
self.start.clicked.connect(self.startAction)
self.TN.clicked.connect(self.TNAction)
self.LN.clicked.connect(self.LNAction)
self.CN.clicked.connect(self.CNAction)
self.CT.clicked.connect(self.CTAction)
self.TC.clicked.connect(self.TCAction)
self.LC.clicked.connect(self.LCAction)
self.CL.clicked.connect(self.CLAction)
self.UC.clicked.connect(self.UCAction)
self.CU.clicked.connect(self.CUAction)
self.CmaxTime.clicked.connect(self.CmaxTimeAction)
self.CmaxEvent.clicked.connect(self.CmaxEventAction)
self.slider.valueChanged[int].connect(self.sliderAction)
self.TN.setEnabled(False)
self.LN.setEnabled(False)
self.CN.setEnabled(False)
self.CT.setEnabled(False)
self.TC.setEnabled(False)
self.LC.setEnabled(False)
self.CL.setEnabled(False)
self.UC.setEnabled(False)
self.CU.setEnabled(False)
self.CmaxTime.setEnabled(False)
self.CmaxEvent.setEnabled(False)
self.slider.setEnabled(False)
self.screen.setHtml("please set configuration")

def sliderAction(self,value):
self.slider.setValue(value)
if self.displayTarget=="TN":
self.screen.load(QtCore.QUrl(self.outdir+"TN"+str((value+1)*10)+".html"))
elif self.displayTarget=="LN":
self.screen.load(QtCore.QUrl(self.outdir+"LN"+str(value)+".html"))
elif self.displayTarget=="CN":
self.screen.load(QtCore.QUrl(self.outdir+"CN"+str(value)+".html"))
elif self.displayTarget=="CT":
self.screen.load(QtCore.QUrl(self.outdir+"CT"+str(value)+".html"))
elif self.displayTarget=="TC":
self.screen.load(QtCore.QUrl(self.outdir+"TC"+str(value)+".html"))
elif self.displayTarget=="LC":
self.screen.load(QtCore.QUrl(self.outdir+"LC"+str(value)+".html"))
elif self.displayTarget=="CL":
self.screen.load(QtCore.QUrl(self.outdir+"CL"+str(value)+".html"))
elif self.displayTarget=="UC":
self.screen.load(QtCore.QUrl(self.outdir+"UC"+str(value)+".html"))
elif self.displayTarget=="CU":
self.screen.load(QtCore.QUrl(self.outdir+"CU"+str(value)+".html"))

def renderBar(self,source,xname,yname,times,path):
for i in range(times):
index = pd.cut(source[i],bins=self.displayGrand).categories.values
index = [round(i.mid,4) for i in index]
bar = Bar(width=450,height=450,is_animation=False)
bar.add(xname,index,pd.value_counts(source[i],bins=self.displayGrand,sort=False),xaxis_name=xname,yaxis_name=yname,is_legend_show=False,is_toolbox_show=False,is_label_show=True)
bar.render("data/"+path+str(i)+".html")

def renderScatter(self,xname,yname,ax,ay,times,path,op):
for i in range(times):
sc = Scatter(width=450,height=450,is_animation=False)
if op==0:
sc.add(xname,ax[i],ay[i],xaxis_name=xname,yaxis_name=yname,is_legend_show=False,is_toolbox_show=False)
elif op==-1:
sc.add(xname,ax[i],ay,xaxis_name=xname,yaxis_name=yname,is_legend_show=False,is_toolbox_show=False)
else:
sc.add(xname,ax,ay[i],xaxis_name=xname,yaxis_name=yname,is_legend_show=False,is_toolbox_show=False)
sc.render("data/"+path+str(i)+".html")

def startAction(self):

#init
mode = 0 #0~5
self.capitalLog = []
self.luckyLog = []
self.unluckyLog = []
mapsize = int(self.map.currentText()[:3])
num = int(self.N.currentText())
lucknum = int(self.luck.currentText())
unlucknum = int(self.unluck.currentText())
self.epochs = int(self.step.currentText())
self.displayGrand = int(self.grand.currentText())
std = float(self.T_std.currentText())
avg = float(self.T_avg.currentText())
self.talent = np.random.normal(avg, std, num)
if mode==3:
pay = np.random.normal(5, 1, num)
self.capital = [10]*num
self.sitex = np.random.uniform(0,mapsize,num)
self.sitey = np.random.uniform(0,mapsize,num)
self.lucky = [0]*num
self.unlucky = [0]*num
self.capitalLog.append(deepcopy(self.capital))
self.luckyLog.append(deepcopy(self.lucky))
self.unluckyLog.append(deepcopy(self.unlucky))
#simulate
for i in range(self.epochs):
goodx = np.random.uniform(0,mapsize,lucknum)
goody = np.random.uniform(0,mapsize,lucknum)
badx = np.random.uniform(0,mapsize,unlucknum)
bady = np.random.uniform(0,mapsize,unlucknum)
if mode==4:
avgcap = sum(self.capital)/num
if mode==5:
rankedcap = sorted(self.capital)
level1 = rankedcap[int(num*0.5)]
level2 = rankedcap[int(num*0.9)]
for j in range(num):
if mode==3:
self.capital[j] = self.capital[j]+pay[j]*self.talent[j]
for k in range(lucknum):
disx = abs(goodx[k]-self.sitex[j])
disy = abs(goody[k]-self.sitey[j])
if (disx<2 and disy<2) or (mode==4 and self.capital[j]>avgcap and disx<3 and disy<3):
self.lucky[j]+=1
r = random.random()
if mode==2:
self.capital[j]=(1+self.talent[j])*self.capital[j]
else:
if r<self.talent[j]:
if mode==5:
if self.capital[j]<=level1:
self.capital[j]*=1.9
elif self.capital[j]<=level2:
self.capital[j]*=1.7
else:
self.capital[j]*=1.5
else:
self.capital[j]*=2
for k in range(unlucknum):
disx = abs(badx[k]-self.sitex[j])
disy = abs(bady[k]-self.sitey[j])
if (disx<2 and disy<2) or (mode==4 and self.capital[j]<avgcap and disx<3 and disy<3):
self.unlucky[j]+=1
self.capital[j]/=2
r = random.random()
if mode==1:
if r>self.talent[j]:
self.capital[j]/=2
elif mode==2:
self.capital[j]=self.talent[j]*self.capital[j]
else:
self.capital[j]/=2
self.capitalLog.append(deepcopy(self.capital))
self.luckyLog.append(deepcopy(self.lucky))
self.unluckyLog.append(deepcopy(self.unlucky))
if os.path.exists("./data")==False:
os.mkdir("data")
#draw TN and Cmax
for i in [10,20,30,40,50]:
index = pd.cut(self.talent,bins=i).categories.values
index = [round(i.mid,4) for i in index]
bar = Bar(width=450,height=450,is_animation=False)
bar.add("talent",index,pd.value_counts(self.talent,bins=i,sort=False),xaxis_name="talent",yaxis_name="individuals",is_legend_show=False,is_toolbox_show=False)
bar.render("data/TN"+str(i)+".html")
cmax = np.argmax(self.capital)
ctime = [k[cmax] for k in self.capitalLog]
cstep = list(range(self.epochs+1))
line = Line(width=450,height=450,is_animation=False)
line.add("captial",cstep,ctime,xaxis_name="time",yaxis_name="captial",is_legend_show=False,is_toolbox_show=False)
line.render("data/CmaxTime.html")
cevent = [0]
for i in range(1,self.epochs+1):
cevent.append((self.luckyLog[i][cmax]-self.luckyLog[i-1][cmax])-(self.unluckyLog[i][cmax]-self.unluckyLog[i-1][cmax]))
line = Line(width=450,height=450,is_animation=False)
line.add("event",cstep,cevent,xaxis_name="time",yaxis_name="event",is_legend_show=False,is_toolbox_show=False)
line.render("data/CmaxEvent.html")
#draw others
self.renderBar(self.luckyLog,"lucky","individuals",self.epochs,"LN")
self.renderBar(self.capitalLog,"captial","individuals",self.epochs,"CN")
self.renderScatter("captial","talent",self.capitalLog,self.talent,self.epochs,"CT",-1)
self.renderScatter("talent","captial",self.talent,self.capitalLog,self.epochs,"TC",1)
self.renderScatter("lucky","captial",self.luckyLog,self.capitalLog,self.epochs,"LC",0)
self.renderScatter("captial","lucky",self.capitalLog,self.luckyLog,self.epochs,"CL",0)
self.renderScatter("unlucky","captial",self.unluckyLog,self.capitalLog,self.epochs,"UC",0)
self.renderScatter("captial","unlucky",self.capitalLog,self.unluckyLog,self.epochs,"CU",0)
self.TN.setEnabled(True)
self.LN.setEnabled(True)
self.CN.setEnabled(True)
self.CT.setEnabled(True)
self.TC.setEnabled(True)
self.LC.setEnabled(True)
self.CL.setEnabled(True)
self.UC.setEnabled(True)
self.CU.setEnabled(True)
self.CmaxTime.setEnabled(True)
self.CmaxEvent.setEnabled(True)
self.screen.setHtml("finished")

def displayInit(self,t,m,p):
self.displayTarget = t
self.slider.setEnabled(True)
self.slider.setValue(0)
self.slider.setMaximum(m)
self.screen.load(QtCore.QUrl(self.outdir+p))

def TNAction(self):
self.displayInit("TN",4,"TN10.html")

def LNAction(self):
self.displayInit("LN",self.epochs,"LN0.html")

def CNAction(self):
self.displayInit("CN",self.epochs,"CN0.html")

def CTAction(self):
self.displayInit("CT",self.epochs,"CT0.html")

def TCAction(self):
self.displayInit("TC",self.epochs,"TC0.html")

def LCAction(self):
self.displayInit("LC",self.epochs,"LC0.html")

def CLAction(self):
self.displayInit("CL",self.epochs,"CL0.html")

def UCAction(self):
self.displayInit("UC",self.epochs,"UC0.html")

def CUAction(self):
self.displayInit("CU",self.epochs,"CU0.html")

def CmaxTimeAction(self):
self.slider.setEnabled(False)
self.slider.setValue(0)
self.screen.load(QtCore.QUrl(self.outdir+"CmaxTime.html"))

def CmaxEventAction(self):
self.slider.setEnabled(False)
self.slider.setValue(0)
self.screen.load(QtCore.QUrl(self.outdir+"CmaxEvent.html"))


app = QApplication(sys.argv)
qb = TVL()
qb.show()
sys.exit(app.exec_())