蒙特卡洛模拟

目录



##################################1,蒙特卡洛模拟计算Π值
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
n = 100000
r = 1.0
a,b = (0.0,0.0)

xmin,xmax = a-r,a+r
ymin,ymax = b-r,b+r

x = np.random.uniform(xmin,xmax,n)
y = np.random.uniform(xmin,xmax,n)

fig = plt.figure(figsize=(6,6))
axes = fig.add_subplot(1,1,1)
plt.plot(x,y,'ro',markersize=1)
plt.axis('equal')

d = np.sqrt((x-a)**2+(y-b)**2)
res = sum(np.where(d<r,1,0))
#res/n= pai*(r/2)**2 / r**2
pai = 4*res/n

circle = Circle(xy=(a,b),radius = r,alpha=0.5,color='red')
axes.add_patch(circle)
plt.grid(True,linestyle="--",linewidth="0.8")
plt.show()
##################################2,蒙特卡洛模拟计算积分值
n = 100000

xmin,xmax =0.0,1.0
ymin,ymax =0.0,1.0

x = np.random.uniform(xmin,xmax,n)
y = np.random.uniform(xmin,xmax,n)

fig = plt.figure(figsize=(6,6))
axes = fig.add_subplot(1,1,1)
plt.plot(x,y,'ro',markersize=1)
plt.axis('equal')

#计算过程
jifen/1 = res/n
res = sum(np.where(y<x**2,1,0))
jifen = res/n

a = np.linspace(0,1,1000)
b = a**2
plt.plot(a,b,'--k')
plt.fill_between(a,b,0,color='red',alpha=0.5,label='area')
plt.grid()

##################################3,蒙特卡洛模拟,卫生间排队问题
# 厕所排队问题
# 1、两场电影结束时间相隔较长,互不影响;
# 2、每场电影结束之后会有20个人想上厕所;
# 3、这20个人会在0到10分钟之内全部到达厕所);
# 4、每个人上厕所时间在1-3分钟之间
# 首先模拟最简单的情况,也就是厕所只有一个位置,不考虑两人共用的情况则每人必须等上一人出恭完毕方可进行。
# 分析:对于每个人都有如下几个参数:
# 到达时间 / 等待时间 / 开始上厕所时间 / 结束时间

arrivingtime = np.random.uniform(0,10,size=20)
arrivingtime.sort()
workingtime = np.random.uniform(1,3,size=20)

startingtime = [0 for i in range(20)] 
finishtime   = [0 for i in range(20)] 
waitingtime  = [0 for i in range(20)] 
emptytime    = [0 for i in range(20)] 

startingtime[0] = arrivingtime[0]
finishtime[0]   = startingtime[0] + workingtime[0]
waitingtime[0]  = startingtime[0] - arrivingtime[0]

for i in range(1,len(arrivingtime)):
    if finishtime[i-1] >arrivingtime[i]:
        startingtime[i] = finishtime[i-1]
    else:
        startingtime[i] = arrivingtime[i]
        emptytime[i] = arrivingtime[i] - finishtime[i-1]
    finishtime[i] = startingtime[i] + workingtime[i]
    waitingtime[i] =startingtime[i] - arrivingtime[i]
    print('第%d个人:到达时间 开始时间 工作时间 完成时间 等待时间\n' %i,arrivingtime[i],startingtime[i],workingtime[i],finishtime[i],waitingtime[i])

fig = plt.figure(figsize=(6,4))
plt.plot(waitingtime,'-go')
plt.grid(True,linestyle='--',color='gray',linewidth='1')
plt.title('排对上厕所,等待时间')
plt.show()


##################################4蒙特卡洛模拟,人生七年
'''
 社会财富分配问题模拟

一个财富分配游戏:
房间里有100个人,每人都有100元钱,他们在玩一个游戏。每轮游戏中,每个人都要拿出一元钱随机给另一个人,最后这100个人的财富分布是怎样的?

研究问题:
1、财富分配模型
模型假设:
① 每个人初始基金100元
② 从18岁到65岁,每天玩一次,简化运算按照一共玩17000轮
③ 每天拿出一元钱,并且随机分配给另一个人
④ 当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,但仍然有机会得到别人给出的钱
要求:
① 构建模型模拟(这里需要跑17000轮)
② 绘制柱状图,查看该轮财富情况
   ** 横轴标签代表一个玩家的编号,柱子的高低变动反映该玩家财富值的变化
   ** 制图分两个情况绘制:不排序绘制、每轮按照财富值排序绘制
   ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
   ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
   ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
③ 查看最后财富分配数据是什么分布?
④ 最富有的人相比于初始财富,翻了多少倍?
⑤ 10%的人掌握着多少的财富?30%的人掌握着多少的财富?又有百分之多少人财富缩水至100元以下了?
提示:
① 首先,模拟第一轮游戏分配,不考虑某人财富值降到0元的情况
② 其次,仍然模拟第一轮游戏分配,但考虑某人的财富值降到0元时的情况
③ 构建模型
④ 运行模型,先试运行100条/1000条,再运行17000
⑤ 制图

2、在允许借贷情况下,研究以下问题
和初始模型的区别:
允许借贷意味着可以找亲友、银行、投资人借贷 → 资产为负时,仍然参与游戏
要求:
① 构建模型模拟,再次模拟财富分配情况
   ** 最富有的人相比于初始财富,翻了多少倍?
   ** 10%的人掌握着多少的财富?30%的人掌握着多少的财富?又有百分之多少人财富缩水至100元以下了?
② 绘制柱状图,查看该轮财富情况
   ** 横轴标签代表一个玩家的编号,柱子的高低变动反映该玩家财富值的变化
   ** 这里只需要每轮按照财富值排序绘制
   ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
   ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
   ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
③ 游戏次数与财富分布的标准差的情况,绘图来表示
   ** 这里用允许借贷情况下模拟的结果
   ** 横坐标为游戏次数(总共17000次),纵坐标为财富分配标准差
   ** 绘制折线图
④ 玩家从18岁开始,在经过17年后为35岁,这个期间共进行游戏6200次左右,则此刻查看财富情况,将财富值为负的标记成“破产”,通过图表研究
   该类玩家在今后的游戏中能否成功“逆袭”(财富值从负到正为逆袭)、
   ** 这里绘制柱状图 → 6200至17000轮,按照每500轮绘制一次柱状图
提示:
① 该模型只需要将初始模型中,“财富小于零时无需拿钱给别人”的条件删掉即可
② 最后绘制柱状图时,需要将负债的玩家标红,这里可以通过截取dataframe之后,给与color字段来设置颜色

3、努力的人生会更好吗?
模型假设:
① 每个人初始基金仍为100元
② 一共玩17000轮
③ 每天拿出一元钱,并且随机分配给另一个人
④ 有10个人加倍努力,从而获得了1%的竞争优势
⑤ 允许借贷
允许借贷意味着可以找亲友、银行、投资人借贷 → 资产为负时,仍然参与游戏
要求:
① 构建模型模拟,再次模拟财富分配情况
② 努力的人,最后是否富有?
③ 绘制柱状图,查看该轮财富情况
   ** 横轴标签代表一个玩家的编号,柱子的高低变动反映该玩家财富值的变化
   ** 这里只需要每轮按照财富值排序绘制
   ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
   ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
   ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
提示:
① 这里设置的10个人id分别为:[1,11,21,31,41,51,61,71,81,91]
② np.random.choice(person_n, p =[...]) → 这里通过设置p来修改概率:努力的10人概率为(1/100.1*1.01),其他人概率为(1/100.1)


'''

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import time
import warnings
warnings.filterwarnings('ignore') 
# 不发出警告


# 1、财富分配模型测试
# 模型假设:
# ① 每个人初始基金100元
# ② 从18岁到65岁,每天玩一次,简化运算按照一共玩17000天
# ③ 每天拿出一元钱,并且随机分配给另一个人
# ④ 当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,但仍然有机会得到别人给出的钱

# (1) 模拟第一轮游戏分配
# 先不考虑某人财富值降到0元的情况

# 设定初始参数:游戏玩家100人,起始资金100元
person_n = [x for x in range(1,101)]
fortune = pd.DataFrame( [100 for x in range(100)],index = person_n)
fortune.index.name = 'id'
fortune.head(10)

round_r1  = pd.DataFrame({'pre_round':fortune[0],'lost':1})
choice_r1 = pd.Series(np.random.choice(person_n,100))
gain_r1   = pd.DataFrame({'gain':choice_r1.value_counts()})

round_r1  = round_r1.join(gain_r1)
round_r1.fillna(0,inplace=True)
fortune[1] =round_r1['pre_round'] - round_r1['lost'] + round_r1['gain']
fortune.head()

# (2) 模拟第一轮游戏分配
# 考虑情况:当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,但仍然有机会得到别人给出的钱

person_n = [x for x in range(1,101)]
fortune = pd.DataFrame([100 for i in range(100)], index = person_n)
fortune.index.name = 'id'
# 设定初始参数:游戏玩家100人,起始资金100元

round_r1 = pd.DataFrame({'pre_round':fortune[0],'lost':0})
round_r1['lost'][round_r1['pre_round'] > 0] = 1
# 设定第一轮分配财富之前的情况 → 该轮财富值为0的不需要拿钱给别人
round_players = round_r1[round_r1['pre_round'] > 0]
# 筛选出参与游戏的玩家:财富值>0
choice_r1 = pd.Series(np.random.choice(person_n,len(round_players)))      
gain_r1 = pd.DataFrame({'gain':choice_r1.value_counts()})                  
# 这一轮中每个人随机指定给“谁”1元钱,并汇总这一轮每个人的盈利情况
round_r1 = round_r1.join(gain_r1)
round_r1.fillna(0,inplace = True)
fortune[1] = round_r1['pre_round'] - round_r1['lost'] + round_r1['gain']
# 合并数据,得到这一轮财富分配的结果
fortune.head()

# (3)构建函数模型
# 这里注意:当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,但仍然有机会得到别人给出的钱

def game1(data, roundi):
    if len(data[data[roundi - 1] ==0]) > 0:   
    # 当数据包含财富值为0的玩家时
        round_i = pd.DataFrame({'pre_round':data[roundi-1],'lost':0})
        con = round_i['pre_round'] > 0
        round_i['lost'][con] = 1               # 设定每轮分配财富之前的情况 → 该轮财富值为0的不需要拿钱给别人
        round_players_i = round_i[con]         # 筛选出参与游戏的玩家:财富值>0
        choice_i = pd.Series(np.random.choice(person_n,len(round_players_i)))
        gain_i = pd.DataFrame({'gain':choice_i.value_counts()})     # 这一轮中每个人随机指定给“谁”1元钱,并汇总这一轮每个人的盈利情况
        round_i = round_i.join(gain_i)
        round_i.fillna(0,inplace = True)
        return round_i['pre_round'] -  round_i['lost'] + round_i['gain']
        # 合并数据,得到这一轮财富分配的结果
    else:
    # 当数据不包含财富值为0的玩家时
        round_i = pd.DataFrame({'pre_round':data[roundi-1],'lost':1}) # 设定每轮分配财富之前的情况
        choice_i = pd.Series(np.random.choice(person_n,100))
        gain_i = pd.DataFrame({'gain':choice_i.value_counts()})       # 这一轮中每个人随机指定给“谁”1元钱,并汇总这一轮每个人的盈利情况
        round_i = round_i.join(gain_i)
        round_i.fillna(0,inplace = True)
        return round_i['pre_round'] -  round_i['lost'] + round_i['gain']
        # 合并数据,得到这一轮财富分配的结果
print('finished!')

# (4)运行模型,模拟财富分配

person_n = [x for x in range(1,101)]
fortune = pd.DataFrame([100 for i in range(100)], index = person_n)
fortune.index.name = 'id'
# 设定初始参数:游戏玩家100人,起始资金100元

starttime = time.time()                     # 模型开始时间
for round in range(1,17001):
    fortune[round] = game1(fortune,round)   # 进行17000轮随机分配模拟
game1_result = fortune.T                    # 转置后得到结果数据 → 列为每一个人的id,行为每一轮的财富分配结果   
endtime = time.time()                       # 模型结束时间
print('模型总共用时%i秒' % (endtime - starttime))
# 计算时间

game1_result.tail()
# 查看最后5条数据

# (5)绘制柱状图
# ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
# ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
# ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
# ① 不排序绘制

os.chdir('F:\\a_houdashi_work\\bbb_python学习\\我做的项目\\蒙特卡洛模拟之社会财富分配\\财富分配模型_初始模型_不排序绘制\\')

def graph1(data,start,end,length):
    for n in list(range(start,end,length)):
        datai = data.iloc[n]
        plt.figure(figsize = (10,6))
        plt.bar(datai.index,datai.values,color='gray',alpha = 0.8,width = 0.9)
        plt.ylim((0,400))
        plt.xlim((-10,110))
        plt.title('Round %d' % n)
        plt.xlabel('PlayerID')
        plt.ylabel('Fortune')
        plt.grid(color='gray', linestyle='--', linewidth=0.5)
        plt.savefig('graph1_round_%d.png' % n, dpi=200)
# 创建绘图函数1

graph1(game1_result,0,100,10)
graph1(game1_result,100,1000,100)
graph1(game1_result,1000,17400,400)

print('finished!')

game1_result.iloc[10000].index

# 一些结论
round_17000_1 = pd.DataFrame({'money':game1_result.iloc[17000]})
round_17000_1 = pd.DataFrame({'money':game1_result.iloc[17000]}).sort_values(by = 'money',ascending = False).reset_index()
round_17000_1['fortune_pre'] = round_17000_1['money'] / round_17000_1['money'].sum()
round_17000_1['fortune_cumsum'] = round_17000_1['fortune_pre'].cumsum()
round_17000_1.head()

# 最后一轮中,最富有的人财富值为365元,相比于初始财富,翻了3.65倍
# 10%的人掌握着28%的财富,20%的人掌握着51%的财富?
# 60%的人财富缩水至100元以下了?


'''
2、在允许借贷情况下,研究以下问题
和初始模型的区别:
允许借贷意味着可以找亲友、银行、投资人借贷 → 资产为负时,仍然参与游戏
要求:
① 构建模型模拟,再次模拟财富分配情况
   ** 最富有的人相比于初始财富,翻了多少倍?
   ** 10%的人掌握着多少的财富?30%的人掌握着多少的财富?又有百分之多少人财富缩水至100元以下了?
② 绘制柱状图,查看该轮财富情况
   ** 横轴标签代表一个玩家的编号,柱子的高低变动反映该玩家财富值的变化
   ** 这里只需要每轮按照财富值排序绘制
   ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
   ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
   ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
③ 游戏次数与财富分布的标准差的情况,绘图来表示
   ** 这里用允许借贷情况下模拟的结果
   ** 横坐标为游戏次数(总共17000次),纵坐标为财富分配标准差
   ** 绘制折线图
④ 玩家从18岁开始,在经过17年后为35岁,这个期间共进行游戏6200次左右,则此刻查看财富情况,将财富值为负的标记成“破产”,通过图表研究
   该类玩家在今后的游戏中能否成功“逆袭”(财富值从负到正为逆袭)、
   ** 这里绘制柱状图 → 6200至17000轮,按照每500轮绘制一次柱状图
提示:
① 该模型只需要将初始模型中,“财富小于零时无需拿钱给别人”的条件删掉即可
② 最后绘制柱状图时,需要将负债的玩家标红,这里可以通过截取dataframe之后,给与color字段来设置颜色

'''
# (1)构建函数模型
# 这里注意:当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,但仍然有机会得到别人给出的钱

def game2(data, roundi):
    round_i = pd.DataFrame({'pre_round':data[roundi-1],'lost':1}) # 设定每轮分配财富之前的情况
    choice_i = pd.Series(np.random.choice(person_n,100))
    gain_i = pd.DataFrame({'gain':choice_i.value_counts()})       # 这一轮中每个人随机指定给“谁”1元钱,并汇总这一轮每个人的盈利情况
    round_i = round_i.join(gain_i)
    round_i.fillna(0,inplace = True)
    return round_i['pre_round'] -  round_i['lost'] + round_i['gain']
    # 合并数据,得到这一轮财富分配的结果
print('finished!')


# (2)运行模型,模拟财富分配

person_n = [x for x in range(1,101)]
fortune = pd.DataFrame([100 for i in range(100)], index = person_n)
fortune.index.name = 'id'
# 设定初始参数:游戏玩家100人,起始资金100元

starttime = time.time()                     # 模型开始时间
for round in range(1,17001):
    fortune[round] = game2(fortune,round)   # 进行17000轮随机分配模拟
game2_result = fortune.T                    # 转置后得到结果数据 → 列为每一个人的id,行为每一轮的财富分配结果   
endtime = time.time()                       # 模型结束时间
print('模型总共用时%i秒' % (endtime - starttime))
# 计算时间

game2_result.tail()
# 查看最后5条数据

# 一些结论

round_17000_2 = pd.DataFrame({'money':game2_result.iloc[17000]}).sort_values(by = 'money',ascending = False).reset_index()
round_17000_2['fortune_pre'] = round_17000_2['money'] / round_17000_2['money'].sum()
round_17000_2['fortune_cumsum'] = round_17000_2['fortune_pre'].cumsum()
round_17000_2.head()

# 最后一轮中,最富有的人财富值为458元,相比于初始财富,翻了4.58倍
# 10%的人掌握着33%的财富,20%的人掌握着59%的财富?
# 50%的人财富缩水至100元以下了?


# (3)游戏次数与财富分布的标准差的情况,绘图来表示
os.chdir('F:\\a_houdashi_work\\bbb_python学习\\我做的项目\\蒙特卡洛模拟之社会财富分配\\财富分配模型_允许借贷\\')
def graph3(data,start,end,length):
    for n in list(range(start,end,length)):
        datai = data.iloc[n].sort_values().reset_index()[n]
        plt.figure(figsize = (10,6))
        plt.bar(datai.index,datai.values,color='gray',alpha = 0.8,width = 0.9)
        plt.grid(color='gray', linestyle='--', linewidth=0.5)
        plt.ylim((-200,400))
        plt.xlim((-10,110))
        plt.title('Round %d' % n)
        plt.xlabel('PlayerID')
        plt.ylabel('Fortune')
        plt.savefig('graph3_round_%d.png' % n, dpi=200)
# 创建绘图函数2

graph3(game2_result,0,100,10)
graph3(game2_result,100,1000,100)
graph3(game2_result,1000,17400,400)

print('finished!')

datai = game2_result.iloc[17000].sort_values().reset_index()[17000]

datai = game2_result.iloc[17000].sort_values()

datai = game2_result.iloc[17000].sort_values().reset_index()

datai.head(10)

# (4)游戏次数与财富分布的标准差的情况,绘图来表示

game2_st = game2_result.std(axis = 1)
game2_st.plot(figsize = (12,5),color = 'red',alpha = 0.6,grid = True)
plt.show()

# 游戏早期前2000轮的标准差变动最为激烈;
# 而在6000-6500轮游戏后,标准差的变化趋于平缓,但仍在上升;
# 按照我们设定的游戏与人生的对应规则,这时玩家年龄为35岁

# (5)玩家从18岁开始,在经过17年后为35岁,这个期间共进行游戏6200次左右,则此刻查看财富情况,将财富值为负的标记成“破产
# 通过图表研究该类玩家在今后的游戏中能否成功“逆袭”(财富值从负到正为逆袭)、
# 这里绘制折线图

game2_round6200 = pd.DataFrame({'money':game2_result.iloc[6200].sort_values().reset_index()[6200],
                                'id':game2_result.iloc[6200].sort_values().reset_index()['id'],
                                'color':'gray'})
game2_round6200['color'][game2_round6200['money'] < 0] = 'red'
id_pc = game2_round6200['id'][game2_round6200['money'] < 0].tolist()
print('财富值为负的玩家id为:\n',id_pc)
# 筛选数据
# 设置颜色参数

plt.figure(figsize = (10,6))
plt.bar(game2_round6200.index,game2_round6200['money'],color = game2_round6200['color'],alpha = 0.8,width = 0.9)
plt.grid(color='gray', linestyle='--', linewidth=0.5)
plt.ylim((-200,400))
plt.xlim((-10,110))
plt.title('Round 6200')
plt.xlabel('PlayerID')
plt.ylabel('Fortune')
plt.show()
# 绘制柱状图

# 绘图分析

os.chdir('F:\\a_houdashi_work\\bbb_python学习\\我做的项目\\蒙特卡洛模拟之社会财富分配\\财富分配模型_允许借贷_负债玩家逆袭\\')

def graph4(data,start,end,length):
    for n in list(range(start,end,length)):
        datai = pd.DataFrame({'money':data.iloc[n],'color':'gray'})
        datai['color'].loc[id_pc] = 'red'
        datai = datai.sort_values(by = 'money').reset_index()
        plt.figure(figsize = (10,6))
        plt.bar(datai.index,datai['money'],color=datai['color'],alpha = 0.8,width = 0.9)
        plt.grid(color='gray', linestyle='--', linewidth=0.5)
        plt.ylim((-200,400))
        plt.xlim((-10,110))
        plt.title('Round %d' % n)
        plt.xlabel('PlayerID')
        plt.ylabel('Fortune')
        plt.savefig('graph4_round_%d.png' % n, dpi=200)
# 创建绘图函数2

graph4(game2_result,6200,17000,500)

print('finished!')

# 结论
# 以35岁为界,虽然破产以后,不足一半的概率回复到普通人的生活,但想要逆袭暴富,却是相当困难的

'''
3、努力的人生会更好吗?
模型假设:
① 每个人初始基金仍为100元
② 一共玩17000轮
③ 每天拿出一元钱,并且随机分配给另一个人
④ 有10个人加倍努力,从而获得了1%的竞争优势
⑤ 允许借贷
允许借贷意味着可以找亲友、银行、投资人借贷 → 资产为负时,仍然参与游戏
要求:
① 构建模型模拟,再次模拟财富分配情况
② 努力的人,最后是否富有?
③ 绘制柱状图,查看该轮财富情况
   ** 横轴标签代表一个玩家的编号,柱子的高低变动反映该玩家财富值的变化
   ** 这里只需要每轮按照财富值排序绘制
   ** 前100轮,按照每10轮绘制一次柱状图,查看财富变化情况
   ** 100至1000轮,按照每100轮绘制一次柱状图,查看财富变化情况
   ** 1000至17000轮,按照每400轮绘制一次柱状图,查看财富变化情况
提示:
① 这里设置的10个人id分别为:[1,11,21,31,41,51,61,71,81,91]
② np.random.choice(person_n, p =[...]) → 这里通过设置p来修改概率:努力的10人概率为0.0101,其他人概率为(0.899/90)

'''
# (1)构建函数模型
# 这里注意:当某人的财富值降到0元时,他在该轮无需拿出1元钱给别人,
#但仍然有机会得到别人给出的钱

person_p = [0.899/90 for i in range(100)]
for i in [1,11,21,31,41,51,61,71,81,91]:
    person_p[i-1] = 0.0101
# 设置概率

def game3(data, roundi):
    round_i = pd.DataFrame({'pre_round':data[roundi-1],'lost':1}) # 设定每轮分配财富之前的情况
    choice_i = pd.Series(np.random.choice(person_n,100, p = person_p))
    gain_i = pd.DataFrame({'gain':choice_i.value_counts()})       # 这一轮中每个人随机指定给“谁”1元钱,并汇总这一轮每个人的盈利情况
    round_i = round_i.join(gain_i)
    round_i.fillna(0,inplace = True)
    return round_i['pre_round'] -  round_i['lost'] + round_i['gain']
    # 合并数据,得到这一轮财富分配的结果
print('finished!')

# (2)运行模型,模拟财富分配

person_n = [x for x in range(1,101)]
fortune = pd.DataFrame([100 for i in range(100)], index = person_n)
fortune.index.name = 'id'
# 设定初始参数:游戏玩家100人,起始资金100元

starttime = time.time()                     # 模型开始时间
for round in range(1,17001):
    fortune[round] = game3(fortune,round)   # 进行17000轮随机分配模拟
game3_result = fortune.T                    # 转置后得到结果数据 → 列为每一个人的id,行为每一轮的财富分配结果   
endtime = time.time()                       # 模型结束时间
print('模型总共用时%i秒' % (endtime - starttime))
# 计算时间

game3_result.tail()
# 查看最后5条数据

# 绘图分析

os.chdir('F:\\a_houdashi_work\\bbb_python学习\\我做的项目\\蒙特卡洛模拟之社会财富分配\\财富分配模型_努力人生\\')

plt.figure(figsize = (10,6))
data0 = pd.DataFrame({'money':game3_result.iloc[0],'color':'gray'})
data0['color'].loc[[1,11,21,31,41,51,61,71,81,91]] = 'red'
plt.bar(data0.index,data0['money'],color=data0['color'],alpha = 0.8,width = 0.9)
plt.grid(color='gray', linestyle='--', linewidth=0.5)
plt.ylim((-200,400))
plt.xlim((-10,110))
plt.title('Round %d' % 0)
plt.xlabel('PlayerID')
plt.ylabel('Fortune')
plt.savefig('graph5_round_%d.png' % 0, dpi=200)
# 绘制起始图片

def graph5(data,start,end,length):
    for n in list(range(start,end,length)):
        datai = pd.DataFrame({'money':data.iloc[n],'color':'gray'})
        datai['color'].loc[[1,11,21,31,41,51,61,71,81,91]] = 'red'
        datai = datai.sort_values(by = 'money').reset_index()
        plt.figure(figsize = (10,6))
        plt.bar(datai.index,datai['money'],color=datai['color'],alpha = 0.8,width = 0.9)
        plt.grid(color='gray', linestyle='--', linewidth=0.5)
        plt.ylim((-200,400))
        plt.xlim((-10,110))
        plt.title('Round %d' % n)
        plt.xlabel('PlayerID')
        plt.ylabel('Fortune')
        plt.savefig('graph5_round_%d.png' % n, dpi=200)
# 创建绘图函数2

graph5(game3_result,10,100,10)
graph5(game3_result,100,1000,100)
graph5(game3_result,1000,17400,400)

print('finished!')

# 结论
# 社会财富的总体分布形态没有什么变化
# 10位努力玩家中多位都进入了富人top20!

##################################蒙特卡洛模拟,婚姻配对

'''
【项目14】  婚恋配对实验

婚恋配对模拟规则:
① 按照一定规则生成了1万男性+1万女性样本:
   ** 在配对实验中,这2万个样本具有各自不同的个人属性(财富、内涵、外貌),每项属性都有一个得分
   ** 财富值符合指数分布,内涵和颜值符合正态分布
   ** 三项的平均值都为60分,标准差都为15分
② 模拟实验。基于现实世界的提炼及适度简化,我们概括了三个最主流的择偶策略:
   ** 择偶策略1:门当户对,要求双方三项指标加和的总分接近,差值不超过20分;
   ** 择偶策略2:男才女貌,男性要求女性的外貌分比自己高出至少10分,女性要求男性的财富分比自己高出至少10分;
   ** 择偶策略3:志趣相投、适度引领,要求对方的内涵得分在比自己低5分~高10分的区间内,且外貌和财富两项与自己的得分差值都在5分以内
③ 每一轮实验中,我们将三种策略随机平分给所有样本(即采用每种策略的男性有3333个样本)
④ 我们为每位单身男女随机选择一个对象,若双方互相符合要求就算配对成功,配对失败的男女则进入下一轮配对。

1、样本数据处理
   ** 按照一定规则生成了1万男性+1万女性样本:
   ** 在配对实验中,这2万个样本具有各自不同的个人属性(财富、内涵、外貌),每项属性都有一个得分
   ** 财富值符合指数分布,内涵和颜值符合正态分布
   ** 三项的平均值都为60分,标准差都为15分
要求:
① 构建函数实现样本数据生成模型,函数参数之一为“样本数量”,并用该模型生成1万男性+1万女性数据样本
   ** 包括三个指标:财富、内涵、外貌
② 绘制柱状图来查看每个人的属性分值情况
提示:
① 正态分布:np.random.normal(loc=60, scale=15, size=n)
② 指数分布:np.random.exponential(scale=15, size=n) + 45

2、生成99个男性、99个女性样本数据,分别针对三种策略构建算法函数
   ** 择偶策略1:门当户对,要求双方三项指标加和的总分接近,差值不超过20分;
   ** 择偶策略2:男才女貌,男性要求女性的外貌分比自己高出至少10分,女性要求男性的财富分比自己高出至少10分;
   ** 择偶策略3:志趣相投、适度引领,要求对方的内涵得分在比自己低10分~高10分的区间内,且外貌和财富两项与自己的得分差值都在5分以内
   ** 每一轮实验中,我们将三种策略随机平分给所有样本,这里则是三种策略分别33人
   ** 这里不同策略匹配结果可能重合,所以为了简化模型 
   → 先进行策略1模拟,
   → 模拟完成后去掉该轮成功匹配的女性数据,再进行策略2模拟,
   → 模拟完成后去掉该轮成功匹配的女性数据,再进行策略3模拟
① 生成样本数据
② 给男性样本数据,随机分配策略选择 → 这里以男性为出发作为策略选择方
③ 尝试做第一轮匹配,记录成功的匹配对象,并筛选出失败的男女性进入下一轮匹配
④ 构建模型,并模拟1万男性+1万女性的配对实验
⑤ 通过数据分析,回答几个问题:
   ** 百分之多少的样本数据成功匹配到了对象?
   ** 采取不同择偶策略的匹配成功率分别是多少?
   ** 采取不同择偶策略的男性各项平均分是多少?
提示:
① 择偶策略评判标准:
   ** 若匹配成功,则该男性与被匹配女性在这一轮都算成功,并退出游戏
   ** 若匹配失败,则该男性与被匹配女性再则一轮都算失败,并进入下一轮
   ** 若同时多个男性选择了同一个女性,且满足成功配对要求,则综合评分高的男性算为匹配成功
② 构建空的数据集,用于存储匹配成功的数据
③ 每一轮匹配之后,删除成功匹配的数据之后,进入下一轮,这里删除数据用df.drop()
④ 这里建议用while去做迭代 → 当该轮没有任何配对成功,则停止实验

3、以99男+99女的样本数据,绘制匹配折线图
要求:
① 生成样本数据,模拟匹配实验
② 生成绘制数据表格
③ bokhe制图
   ** 这里设置图例,并且可交互(消隐模式)
提示:
① bokeh制图时,y轴为男性,x轴为女性
② 绘制数据表格中,需要把男女性的数字编号提取出来,这样图表横纵轴好识别
③ bokhe绘制折线图示意:p.line([0,女性数字编号,女性数字编号],[男性数字编号,男性数字编号,0])

4、生成“不同类型男女配对成功率”矩阵图
要求:
① 以之前1万男+1万女实验的结果为数据
② 按照财富值、内涵值、外貌值分别给三个区间,以区间来评判“男女类型”
   ** 高分(70-100分),中分(50-70分),低分(0-50分)
   ** 按照此类分布,男性女性都可以分为27中类型:财高品高颜高、财高品中颜高、财高品低颜高、... (财→财富,品→内涵,颜→外貌)
③ bokhe制图
   ** 散点图
   ** 27行*27列,散点的颜色深浅代表匹配成功率
提示:
① 注意绘图的数据结构
② 这里散点图通过xy轴定位数据,然后通过设置颜色的透明度来表示匹配成功率
③ alpha字段为每种类型匹配成功率标准化之后的结果,再乘以一个参数
   → data['alpha'] = (data['chance'] - data['chance'].min())/(data['chance'].max() - data['chance'].min())*8   

'''

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import time
# 导入时间模块

import warnings
warnings.filterwarnings('ignore') 
# 不发出警告

from bokeh.io import output_notebook
output_notebook()
# 导入notebook绘图模块

from bokeh.plotting import figure,show
from bokeh.models import ColumnDataSource,HoverTool
# 导入bokeh绘图模块

'''
1、样本数据处理
   ** 按照一定规则生成了1万男性+1万女性样本:
   ** 在配对实验中,这2万个样本具有各自不同的个人属性(财富、内涵、外貌),每项属性都有一个得分
   ** 财富值符合指数分布,内涵和颜值符合正态分布
   ** 三项的平均值都为60分,标准差都为15分
要求:
① 构建函数实现样本数据生成模型,函数参数之一为“样本数量”,并用该模型生成1万男性+1万女性数据样本
   ** 包括三个指标:财富、内涵、外貌
② 绘制柱状图来查看每个人的属性分值情况
提示:
① 正态分布:np.random.normal(loc=60, scale=15, size=n)
② 指数分布:np.random.exponential(scale=15, size=n) + 45

'''

# 分别生成1万条随机数据,分别为正态分布、指数分布,要求数据均值为60,标准差为15

data_norm = pd.DataFrame({'正态分布':np.random.normal(loc = 60,scale = 15,size = 10000)})
data_exp = pd.DataFrame({'指数分布':np.random.exponential(scale=15, size=10000) + 45})
# 构建样本数据 → 正态分布/指数分布

fig,axes = plt.subplots(1,2,figsize = (12,4))
data_norm.hist(bins=50,ax = axes[0],grid = True,color = 'gray',alpha = 0.6)
data_exp.hist(bins = 50,ax = axes[1],grid = True,color = 'gray',alpha = 0.6)
plt.show()

# 构建函数实现样本数据生成模型,函数参数之一为“样本数量”,并用该模型生成1万男性+1万女性数据样本

def create_sample(n,gender):
    sample_data = pd.DataFrame({'fortune':np.random.exponential(scale=15, size=n) + 45,
                                'apperance':np.random.normal(loc = 60,scale = 15,size = n),
                                'character':np.random.normal(loc = 60,scale = 15,size = n)},
                               index = [gender + str(i) for i in range(1,n+1)])
    sample_data.index.name = 'id'
    sample_data['score'] = sample_data.sum(axis = 1)/3
    # 计算综合评分
    return sample_data
# 创建函数

sample_m = create_sample(10000,'m')
sample_f = create_sample(10000,'f')
# 生成男女样本数据

fig,axes = plt.subplots(2,1,figsize = (12,8))
sample_m[['apperance','character','fortune']].iloc[:50].plot(kind='bar',colormap='Blues_r',grid = True,stacked=True,ax = axes[0],ylim = [0,300]) 
sample_f[['apperance','character','fortune']].iloc[:50].plot(kind='bar',colormap='Reds_r',grid = True,stacked=True,ax = axes[1],ylim = [0,300]) 
plt.show()
# 出图查看数据

sample_m.head()
# 查看男性样本数据分布

'''
2、生成99个男性、99个女性样本数据,分别针对三种策略构建算法函数
   ** 择偶策略1:门当户对,要求双方三项指标加和的总分接近,差值不超过20分;
   ** 择偶策略2:男才女貌,男性要求女性的外貌分比自己高出至少10分,女性要求男性的财富分比自己高出至少10分;
   ** 择偶策略3:志趣相投、适度引领,要求对方的内涵得分在比自己低10分~高10分的区间内,且外貌和财富两项与自己的得分差值都在5分以内
   ** 每一轮实验中,我们将三种策略随机平分给所有样本,这里则是三种策略分别33人
   ** 这里不同策略匹配结果可能重合,所以为了简化模型 
   → 先进行策略1模拟,
   → 模拟完成后去掉该轮成功匹配的女性数据,再进行策略2模拟,
   → 模拟完成后去掉该轮成功匹配的女性数据,再进行策略3模拟
要求:
① 生成样本数据
② 给男性样本数据,随机分配策略选择 → 这里以男性为出发作为策略选择方
③ 尝试做第一轮匹配,记录成功的匹配对象,并筛选出失败的男女性进入下一轮匹配
④ 构建模型,并模拟1万男性+1万女性的配对实验
⑤ 通过数据分析,回答几个问题:
   ** 百分之多少的样本数据成功匹配到了对象?
   ** 采取不同择偶策略的匹配成功率分别是多少?
   ** 采取不同择偶策略的男性各项平均分是多少?
提示:
① 择偶策略评判标准:
   ** 若匹配成功,则该男性与被匹配女性在这一轮都算成功,并退出游戏
   ** 若匹配失败,则该男性与被匹配女性再则一轮都算失败,并进入下一轮
   ** 若同时多个男性选择了同一个女性,且满足成功配对要求,则综合评分高的男性算为匹配成功
② 构建空的数据集,用于存储匹配成功的数据
③ 每一轮匹配之后,删除成功匹配的数据之后,进入下一轮,这里删除数据用df.drop()
④ 这里建议用while去做迭代 → 当该轮没有任何配对成功,则停止实验

'''

# 这里先生成99个男性、99个女性样本数据

sample_m_test = create_sample(99,'m')
sample_f_test = create_sample(99,'f')
# 设置好样本数据

sample_m_test['strategy'] = np.random.choice([1,2,3],99)
# 给男性赋值策略选择
# 1 → 择偶策略1
# 2 → 择偶策略2
# 3 → 择偶策略3

# 择偶策略,测试

match_success = pd.DataFrame(columns = ['m','f','round_n','strategy_type'])
# 构建空的数据集,用于存储匹配成功的数据

round1_f = sample_f_test.copy()  
# 复制女性样本数据
 
round1_m = sample_m_test.copy()
round1_m['choice'] = np.random.choice(round1_f.index,len(round1_m))      
# 复制男性样本数据,并做匹配选择

round1_match = pd.merge(round1_m,round1_f,left_on = 'choice',right_index = True).reset_index()
round1_match['score_dis'] = np.abs(round1_match['score_x'] - round1_match['score_y'])                 # 计算综合评分差值
round1_match['cha_dis'] = np.abs(round1_match['character_x'] - round1_match['character_y'])           # 求出内涵得分差值
round1_match['for_dis'] = np.abs(round1_match['fortune_x'] - round1_match['fortune_y'])               # 求出财富得分差值
round1_match['app_dis'] = np.abs(round1_match['apperance_x'] - round1_match['apperance_y'])           # 求出外貌得分差值
# 合并数据 

# ① 策略1:门当户对,要求双方三项指标加和的总分接近,差值不超过20分;
round1_s1_m = round1_match[round1_match['strategy'] == 1]         
sha =   round1_s1_m[round1_s1_m['score_dis'] <= 20]                              # 筛选策略1的数据 
round1_s1_success = round1_s1_m[round1_s1_m['score_dis'] <= 20].groupby('choice').max()           # 筛选符合要求的数据
round1_s1_success = pd.merge(round1_s1_success,round1_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
round1_s1_success.columns = ['m','f']
round1_s1_success['strategy_type'] = 1
round1_s1_success['round_n'] = 1    # 得到策略1的成功匹配的结果
round1_match.index = round1_match['choice']
round1_match = round1_match.drop(round1_s1_success['f'].tolist())  # 删除策略1成功匹配的女性数据

# ② 策略2:男才女貌,男性要求女性的外貌分比自己高出至少10分,女性要求男性的财富分比自己高出至少10分;
round1_s2_m = round1_match[round1_match['strategy'] == 2]                                          # 筛选策略2的数据 
round1_s2_success = round1_s2_m[(round1_s2_m['fortune_x'] - round1_s2_m['fortune_y'] >= 10) &      # 男性财富比女性高出至少10分
                               (round1_s2_m['apperance_y'] - round1_s2_m['apperance_x'] >= 10)]    # 女性颜值比男性高出至少10分
round1_s2_success = round1_s2_success.groupby('choice').max()                                      # 筛选符合要求的数据
round1_s2_success = pd.merge(round1_s2_success,round1_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
round1_s2_success.columns = ['m','f']
round1_s2_success['strategy_type'] = 2
round1_s2_success['round_n'] = 1    # 得到策略2的成功匹配的结果
round1_match.index = round1_match['choice']
round1_match = round1_match.drop(round1_s2_success['f'].tolist())  # 删除策略2成功匹配的女性数据

# ③ 策略3:志趣相投、适度引领,要求对方的内涵得分在比自己低10分~高10分的区间内,且外貌和财富两项与自己的得分差值都在5分以内
round1_s3_m = round1_match[round1_match['strategy'] == 3]                                          # 筛选策略3的数据 
round1_s3_success = round1_s3_m[(round1_s3_m['cha_dis'] <10) &   # 内涵得分差在10分以内
                               (round1_s3_m['for_dis'] < 5 )&    # 财富得分差在5分以内
                               (round1_s3_m['app_dis'] < 5 )]    # 外貌得分差在5分以内
round1_s3_success = round1_s3_success.groupby('choice').max()                                      # 筛选符合要求的数据
round1_s3_success = pd.merge(round1_s3_success,round1_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
round1_s3_success.columns = ['m','f']
round1_s3_success['strategy_type'] = 3
round1_s3_success['round_n'] = 1    # 得到策略3的成功匹配的结果

# 筛选出成功匹配数据
match_success = pd.concat([match_success,round1_s1_success,round1_s2_success,round1_s3_success])

# 筛选出下一轮实验数据
round2_m = round1_m.drop(match_success['m'].tolist())
round2_f = round1_f.drop(match_success['f'].tolist())

match_success.head()


# 构建模型

def different_strategy(data_m,data_f,roundnum):
    # 创建函数 → 
    # 根据输入的该轮男女数据,得到匹配后数据,并做指标运算
    # 根据匹配好的数据,根据不同策略输出成功匹配的数据
    
    data_m['choice'] = np.random.choice(data_f.index,len(data_m))      
    # 根据男性样本数据做匹配选择
    round_match = pd.merge(data_m,data_f,left_on = 'choice',right_index = True).reset_index()
    round_match['score_dis'] = np.abs(round_match['score_x'] - round_match['score_y'])                 # 计算综合评分差值
    round_match['cha_dis'] = np.abs(round_match['character_x'] - round_match['character_y'])           # 求出内涵得分差值
    round_match['for_dis'] = np.abs(round_match['fortune_x'] - round_match['fortune_y'])               # 求出财富得分差值
    round_match['app_dis'] = np.abs(round_match['apperance_x'] - round_match['apperance_y'])           # 求出外貌得分差值
    # 合并数据 
    
    # 策略1:门当户对,要求双方三项指标加和的总分接近,差值不超过20分;
    s1_m = round_match[round_match['strategy'] == 1]                                      
    s1_success = s1_m[s1_m['score_dis'] <= 20].groupby('choice').max()               
    s1_success = pd.merge(s1_success,data_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
    s1_success.columns = ['m','f']
    s1_success['strategy_type'] = 1
    s1_success['round_n'] = roundnum    # 得到策略1的成功匹配的结果
    round_match.index = round_match['choice']
    round_match = round_match.drop(s1_success['f'].tolist())  # 删除策略1成功匹配的女性数据

    # 策略2:男才女貌,男性要求女性的外貌分比自己高出至少10分,女性要求男性的财富分比自己高出至少10分;
    s2_m = round_match[round_match['strategy'] == 2]                                          
    s2_success = s2_m[(s2_m['fortune_x'] - s2_m['fortune_y'] >= 10) & (s2_m['apperance_y'] - s2_m['apperance_x'] >= 10)] 
    s2_success = s2_success.groupby('choice').max()                               
    s2_success = pd.merge(s2_success,data_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
    s2_success.columns = ['m','f']
    s2_success['strategy_type'] = 2
    s2_success['round_n'] = roundnum    # 得到策略2的成功匹配的结果
    round_match.index = round_match['choice']
    round_match = round_match.drop(s2_success['f'].tolist())  # 删除策略2成功匹配的女性数据

    # 策略3:志趣相投、适度引领,要求对方的内涵得分在比自己低10分~高10分的区间内,且外貌和财富两项与自己的得分差值都在5分以内
    s3_m = round_match[round_match['strategy'] == 3]                                          
    s3_success = s3_m[(s3_m['cha_dis'] <10) & (s3_m['for_dis'] < 5 ) & (s3_m['app_dis'] < 5 )]    
    s3_success = s3_success.groupby('choice').max()                                     
    s3_success = pd.merge(s3_success,data_m.reset_index(),left_on = 'score_x',right_on = 'score')[['id_y','choice']]
    s3_success.columns = ['m','f']
    s3_success['strategy_type'] = 3
    s3_success['round_n'] = roundnum    # 得到策略3的成功匹配的结果
    
    # 该轮成功匹配数据
    data_success = pd.concat([s1_success, s2_success, s3_success])
    
    return data_success

print('finished!')



# 模型运行
# 这里建议用while去做迭代 → 当该轮没有任何配对成功,则停止实验

sample_m1 = create_sample(10000,'m')
sample_f1 = create_sample(10000,'f')
sample_m1['strategy'] = np.random.choice([1,2,3],10000)
# 设置好样本数据

test_m1 = sample_m1.copy()
test_f1 = sample_f1.copy()
# 复制数据

n = 1
# 设定实验次数变量

starttime = time.time()
# 记录起始时间

success_roundn = different_strategy(test_m1, test_f1,n)    
match_success1 = success_roundn                                      
test_m1 = test_m1.drop(success_roundn['m'].tolist())
test_f1 = test_f1.drop(success_roundn['f'].tolist())
print('成功进行第%i轮实验,本轮实验成功匹配%i对,总共成功匹配%i对,还剩下%i位男性和%i位女性' % 
      (n,len(success_roundn),len(match_success1),len(test_m1),len(test_f1)))
# 第一轮实验测试

while len(success_roundn) !=0:
    n += 1
    success_roundn = different_strategy(test_m1,test_f1,n)   
    #得到该轮成功匹配数据
    match_success1 = pd.concat([match_success1,success_roundn])           
    # 将成功匹配数据汇总
    test_m1 = test_m1.drop(success_roundn['m'].tolist())
    test_f1 = test_f1.drop(success_roundn['f'].tolist())
    # 输出下一轮实验数据
    print('成功进行第%i轮实验,本轮实验成功匹配%i对,总共成功匹配%i对,还剩下%i位男性和%i位女性' % 
          (n,len(success_roundn),len(match_success1),len(test_m1),len(test_f1)))
# 运行模型

endtime = time.time()
# 记录结束时间

print('------------')
print('本次实验总共进行了%i轮,配对成功%i对\n------------' % (n,len(match_success1)))
print('实验总共耗时%.2f秒' % (endtime - starttime))


# 通过数据分析,回答几个问题:
#   ** 百分之多少的样本数据成功匹配到了对象?
#   ** 采取不同择偶策略的匹配成功率分别是多少?
#   ** 采取不同择偶策略的男性各项平均分是多少?

# ① 百分之多少的样本数据成功匹配到了对象?
print('%.2f%%的样本数据成功匹配到了对象\n---------' % (len(match_success1)/len(sample_m1)*100))

# ② 采取不同择偶策略的匹配成功率分别是多少?
print('择偶策略1的匹配成功率为%.2f%%' % (len(match_success1[match_success1['strategy_type']==1])/len(sample_m1[sample_m1['strategy'] == 1])*100))
print('择偶策略2的匹配成功率为%.2f%%' % (len(match_success1[match_success1['strategy_type']==2])/len(sample_m1[sample_m1['strategy'] == 2])*100))
print('择偶策略3的匹配成功率为%.2f%%' % (len(match_success1[match_success1['strategy_type']==3])/len(sample_m1[sample_m1['strategy'] == 3])*100))
print('\n---------')

# ③ 采取不同择偶策略的男性各项平均分是多少?
match_m1 = pd.merge(match_success1,sample_m1,left_on = 'm',right_index = True)
result_df = pd.DataFrame([{'财富均值':match_m1[match_m1['strategy_type'] == 1]['fortune'].mean(),
                          '内涵均值':match_m1[match_m1['strategy_type'] == 1]['character'].mean(),
                          '外貌均值':match_m1[match_m1['strategy_type'] == 1]['apperance'].mean()},
                         {'财富均值':match_m1[match_m1['strategy_type'] == 2]['fortune'].mean(),
                          '内涵均值':match_m1[match_m1['strategy_type'] == 2]['character'].mean(),
                          '外貌均值':match_m1[match_m1['strategy_type'] == 2]['apperance'].mean()},
                         {'财富均值':match_m1[match_m1['strategy_type'] == 3]['fortune'].mean(),
                          '内涵均值':match_m1[match_m1['strategy_type'] == 3]['character'].mean(),
                          '外貌均值':match_m1[match_m1['strategy_type'] == 3]['apperance'].mean()}],
                         index = ['择偶策略1','择偶策略2','择偶策略3'])
# 构建数据dataframe

print('择偶策略1的男性 → 财富均值为%.2f,内涵均值为%.2f,外貌均值为%.2f' % 
      (result_df.loc['择偶策略1']['财富均值'],result_df.loc['择偶策略1']['内涵均值'],result_df.loc['择偶策略1']['外貌均值']))
print('择偶策略2的男性 → 财富均值为%.2f,内涵均值为%.2f,外貌均值为%.2f' % 
      (result_df.loc['择偶策略2']['财富均值'],result_df.loc['择偶策略2']['内涵均值'],result_df.loc['择偶策略2']['外貌均值']))
print('择偶策略3的男性 → 财富均值为%.2f,内涵均值为%.2f,外貌均值为%.2f' % 
      (result_df.loc['择偶策略3']['财富均值'],result_df.loc['择偶策略3']['内涵均值'],result_df.loc['择偶策略3']['外貌均值']))

match_m1.boxplot(column = ['fortune','character','apperance'],by='strategy_type',figsize = (10,6),layout = (1,3))
plt.ylim(0,150)
plt.show()
# 绘制箱型图

result_df


'''
3、以99男+99女的样本数据,绘制匹配折线图
要求:
① 生成样本数据,模拟匹配实验
② 生成绘制数据表格
③ bokhe制图
   ** 这里设置图例,并且可交互(消隐模式)
提示:
① bokeh制图时,y轴为男性,x轴为女性
② 绘制数据表格中,需要把男女性的数字编号提取出来,这样图表横纵轴好识别
③ bokhe绘制折线图示意:p.line([0,女性数字编号,女性数字编号],[男性数字编号,男性数字编号,0])

'''

# 生成样本数据,模拟匹配实验

sample_m2 = create_sample(99,'m')
sample_f2 = create_sample(99,'f')
sample_m2['strategy'] = np.random.choice([1,2,3],99)
# 设置好样本数据

test_m2 = sample_m2.copy()
test_f2 = sample_f2.copy()
# 复制数据

n = 1
# 设定实验次数变量

starttime = time.time()
# 记录起始时间

success_roundn = different_strategy(test_m2, test_f2,n)    
match_success2 = success_roundn                                      
test_m2 = test_m2.drop(success_roundn['m'].tolist())
test_f2 = test_f2.drop(success_roundn['f'].tolist())
print('成功进行第%i轮实验,本轮实验成功匹配%i对,总共成功匹配%i对,还剩下%i位男性和%i位女性' % 
      (n,len(success_roundn),len(match_success2),len(test_m2),len(test_f2)))
# 第一轮实验测试

while len(success_roundn) !=0:
    n += 1
    success_roundn = different_strategy(test_m2,test_f2,n)   
    #得到该轮成功匹配数据
    match_success2 = pd.concat([match_success2,success_roundn])           
    # 将成功匹配数据汇总
    test_m2 = test_m2.drop(success_roundn['m'].tolist())
    test_f2 = test_f2.drop(success_roundn['f'].tolist())
    # 输出下一轮实验数据
    print('成功进行第%i轮实验,本轮实验成功匹配%i对,总共成功匹配%i对,还剩下%i位男性和%i位女性' % 
          (n,len(success_roundn),len(match_success2),len(test_m2),len(test_f2)))
# 运行模型

endtime = time.time()
# 记录结束时间

print('------------')
print('本次实验总共进行了%i轮,配对成功%i对\n------------' % (n,len(match_success2)))
print('实验总共耗时%.2f秒' % (endtime - starttime))


# 生成绘制数据表格

from bokeh.palettes import brewer
# 导入调色模块

# 设置调色盘
graphdata1 = match_success2.copy()
graphdata1 = pd.merge(graphdata1,sample_m2,left_on = 'm',right_index = True)
graphdata1 = pd.merge(graphdata1,sample_f2,left_on = 'f',right_index = True)
# 合并数据,得到成功配对的男女各项分值

graphdata1['x'] = '0,' + graphdata1['f'].str[1:] + ',' + graphdata1['f'].str[1:]
graphdata1['x'] = graphdata1['x'].str.split(',')
graphdata1['y'] = graphdata1['m'].str[1:] + ',' + graphdata1['m'].str[1:] + ',0'
graphdata1['y'] = graphdata1['y'].str.split(',')
# 筛选出id的数字编号,制作x,y字段

round_num = graphdata1['round_n'].max()
color = brewer['Blues'][round_num+1]   # 这里+1是为了得到一个色带更宽的调色盘,避免最后一个颜色太浅
graphdata1['color'] = ''
for rn in graphdata1['round_n'].value_counts().index:
    graphdata1['color'][graphdata1['round_n'] == rn] = color[rn-1] 
# 设置颜色

graphdata1 = graphdata1[['m','f','strategy_type','round_n','score_x','score_y','x','y','color']]
# 筛选字段

graphdata1.head()


############ bokeh绘图

p = figure(plot_width=500, plot_height=500,title="配对实验过程模拟示意" ,tools= 'reset,wheel_zoom,pan')   # 构建绘图空间

for datai in graphdata1.values:
    p.line(datai[-3],datai[-2],line_width=1, line_alpha = 0.8, line_color = datai[-1],line_dash = [10,4],legend= 'round %i' % datai[3])  
    # 绘制折线
    p.circle(datai[-3],datai[-2],size = 3,color = datai[-1],legend= 'round %i' % datai[3])
    # 绘制点

p.ygrid.grid_line_dash = [6, 4]
p.xgrid.grid_line_dash = [6, 4]
p.legend.location = "top_right"
p.legend.click_policy="hide"
# 设置其他参数

show(p)


# 数据清洗

graphdata2 = match_success1.copy()
graphdata2 = pd.merge(graphdata2,sample_m1,left_on = 'm',right_index = True)
graphdata2 = pd.merge(graphdata2,sample_f1,left_on = 'f',right_index = True)
# 合并数据,得到成功配对的男女各项分值

graphdata2 = graphdata2[['m','f','apperance_x','character_x','fortune_x','apperance_y','character_y','fortune_y']]
# 筛选字段

graphdata2['for_m'] = pd.cut(graphdata2['fortune_x'],[0,50,70,500],labels = ['财低','财中','财高'])
graphdata2['cha_m'] = pd.cut(graphdata2['character_x'],[0,50,70,500],labels = ['品低','品中','品高'])
graphdata2['app_m'] = pd.cut(graphdata2['apperance_x'],[0,50,70,500],labels = ['颜低','颜中','颜高'])
graphdata2['for_f'] = pd.cut(graphdata2['fortune_y'],[0,50,70,500],labels = ['财低','财中','财高'])
graphdata2['cha_f'] = pd.cut(graphdata2['character_y'],[0,50,70,500],labels = ['品低','品中','品高'])
graphdata2['app_f'] = pd.cut(graphdata2['apperance_y'],[0,50,70,500],labels = ['颜低','颜中','颜高'])
# 指标区间划分

graphdata2['type_m'] = graphdata2['for_m'].astype(np.str) + graphdata2['cha_m'].astype(np.str) + graphdata2['app_m'].astype(np.str)
graphdata2['type_f'] = graphdata2['for_f'].astype(np.str) + graphdata2['cha_f'].astype(np.str) + graphdata2['app_f'].astype(np.str) 

graphdata2 = graphdata2[['m','f','type_m','type_f']]
# 筛选字段

graphdata2.head()

# 匹配成功率计算

success_n = len(graphdata2)
success_chance = graphdata2.groupby(['type_m','type_f']).count().reset_index()
success_chance['chance'] = success_chance['m']/success_n
success_chance['alpha'] = (success_chance['chance'] - success_chance['chance'].min())/(success_chance['chance'].max() - success_chance['chance'].min())*8   # 设置alpha参数
success_chance.head()

# bokeh绘图

mlst = success_chance['type_m'].value_counts().index.tolist()
flst = success_chance['type_f'].value_counts().index.tolist()
source = ColumnDataSource(success_chance)    # 创建数据
hover = HoverTool(tooltips=[("男性类别", "@type_m"),
                           ("女性类别","@type_f"),
                           ("匹配成功率","@chance")]) # 设置标签显示内容

p = figure(plot_width=800, plot_height=800,x_range = mlst, y_range = flst,
           title="不同类型男女配对成功率" ,x_axis_label = '男', y_axis_label = '女',    # X,Y轴label
           tools= [hover,'reset,wheel_zoom,pan,lasso_select'])   # 构建绘图空间

p.square_cross(x = 'type_m', y = 'type_f', source = source,size = 18 ,color = 'red',alpha = 'alpha')
# 绘制点

p.ygrid.grid_line_dash = [6, 4]
p.xgrid.grid_line_dash = [6, 4]
p.xaxis.major_label_orientation = "vertical"
# 设置其他参数

show(p)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值