如何在Python/numpy中创建嵌套加权分布?

如何在Python/numpy中创建嵌套加权分布?,python,numpy,montecarlo,Python,Numpy,Montecarlo,我正在尝试优化(矢量化?)蒙特卡罗模拟的创建,但我还无法找出如何使用numpy或类似库创建嵌套加权随机值。考虑这一情景,由一个面试题> /代码>提问:“三个教室的学生必须为两个班级的校长候选人中的一个投票。教室A有40%的学生,在X X上分为50/50个,Y. B有25%个学生,并且被分成60/40个。C有35%个学生,分为35/65个” 用普通Python创建数据可能看起来像这样 随机导入 n计算=10_000_000 def选择_教室(): 根据百分比返回A、B或C value=random

我正在尝试优化(矢量化?)蒙特卡罗模拟的创建,但我还无法找出如何使用
numpy
或类似库创建嵌套加权随机值。考虑这一情景,由一个<代码>面试题> /代码>提问:“三个教室的学生必须为两个班级的校长候选人中的一个投票。教室A有40%的学生,在X X上分为50/50个,Y. B有25%个学生,并且被分成60/40个。C有35%个学生,分为35/65个”

用普通Python创建数据可能看起来像这样

随机导入
n计算=10_000_000
def选择_教室():
根据百分比返回A、B或C
value=random.random()
如果值小于0.4:
返回“A”
elif值<.65:
返回“B”
其他:
返回“C”
def选择支持(教室):
“根据教室的支持百分比返回X或Y”
value=random.random()
如果教室==“A”:
如果值小于0.5,则返回“X”,否则返回“Y”
elif教室==“B”:
如果值小于0.6,则返回“X”,否则返回“Y”
elif教室==“C”:
如果值小于0.35,则返回“X”,否则返回“Y”
结果=[]
对于范围内的i(n模拟):
教室=选择教室()
支持=选择支持(教室)
results.append({'school':教室,'support':support})
在我的机器上运行10米模拟大约需要10秒。我希望这次能有所改进。我看的第一件事是,
fast\u教室=np.random.choice(['A','B','C',size=n模拟,p=[.4,.25,.35])
。这确实执行得很快,大约350毫秒。但是,我不知道如何从那里生成后续的
X
/
Y
发行版

我尝试过的一件事是Pandas
apply
,它似乎在暗中进行某种优化。下面的Pandas代码段需要约2.5秒才能运行,而列表理解(
[在快速课堂中选择录制支持(录制)]
需要约4秒。不过,感觉这不是“正确”的方式

导入熊猫
将numpy作为np导入
fast_教室=np.random.choice(['A','B','C',size=n模拟,p=[.4,.25,.35])
df=pandas.DataFrame({'classherum':fast_classhers})
df['support']=df.school.apply(选择支持)
我希望找到的是能够生成嵌套加权分布的东西,比如-
np.random.choice(['A',B',C'],['X',Y']],p=[.4,.25,.35],[.5,.5],.6,.4],.35,.65]])


生成这些数据有哪些方法?

您可以显著减少python循环的数量,使代码更加矢量化:

import numpy as np

nsimulations = 12
uniquerooms = ['A','B','C']
supportprobs = [0.5, 0.6, 0.35]
classrooms = np.random.choice(uniquerooms, size=nsimulations, p=[.4, .25, .35])
supports = np.empty_like(classrooms, dtype=classrooms.dtype)
for room, prob in zip(uniquerooms, supportprobs):
    mask = classrooms == room
    supports[mask] = np.random.choice(['X','Y'], size=mask.sum(), p=[prob, 1-prob])

print(classrooms)
# ['C' 'A' 'C' 'A' 'C' 'C' 'A' 'C' 'B' 'C' 'B' 'A']
print(supports)
# ['X' 'Y' 'Y' 'Y' 'Y' 'X' 'Y' 'X' 'X' 'X' 'Y' 'X']

您可以显著减少python循环的数量,使代码更加矢量化:

import numpy as np

nsimulations = 12
uniquerooms = ['A','B','C']
supportprobs = [0.5, 0.6, 0.35]
classrooms = np.random.choice(uniquerooms, size=nsimulations, p=[.4, .25, .35])
supports = np.empty_like(classrooms, dtype=classrooms.dtype)
for room, prob in zip(uniquerooms, supportprobs):
    mask = classrooms == room
    supports[mask] = np.random.choice(['X','Y'], size=mask.sum(), p=[prob, 1-prob])

print(classrooms)
# ['C' 'A' 'C' 'A' 'C' 'C' 'A' 'C' 'B' 'C' 'B' 'A']
print(supports)
# ['X' 'Y' 'Y' 'Y' 'Y' 'X' 'Y' 'X' 'X' 'X' 'Y' 'X']

您可以在对上使用
np.random.choice
,而不是两次运行该函数。这意味着您可以计算拥有对的概率(“教室”、“支持”)。例如,选择教室“a”并获得支持“X”的概率为0.4*0.5=0.2,依此类推

下面的代码对我来说运行得相当快

import numpy as np
nsimulations = 10000000

#Construct the probabilities and pairs 
p = [.4*.5, .4*.5, .25*.6, .25*.4, .35*.35, .35*.65]
pairs = [{'classroom': 'A', 'support': 'X'}, 
         {'classroom': 'A', 'support': 'Y'},
         {'classroom': 'B', 'support': 'X'},
         {'classroom': 'B', 'support': 'Y'},
         {'classroom': 'C', 'support': 'X'},
         {'classroom': 'C', 'support': 'Y'}]

# Sample the pairs based on the probabilities
fast_classrooms = np.random.choice(pairs, size=nsimulations, p=p)
编辑:


与OP发布的方法相比,该代码花费了
0.6193864345550537秒
。OP发布的方法花费了
7.815439224243164秒
。该方法在评论中也得到了@Tom McLean的证实。

您可以对成对使用
np.random.choice
,而不是运行两次函数。这意味着您可以计算拥有成对的概率(“教室”、“支持”)。例如,选择教室“A”并获得支持“X”的概率为0.4*0.5=0.2,依此类推

下面的代码对我来说运行得相当快

import numpy as np
nsimulations = 10000000

#Construct the probabilities and pairs 
p = [.4*.5, .4*.5, .25*.6, .25*.4, .35*.35, .35*.65]
pairs = [{'classroom': 'A', 'support': 'X'}, 
         {'classroom': 'A', 'support': 'Y'},
         {'classroom': 'B', 'support': 'X'},
         {'classroom': 'B', 'support': 'Y'},
         {'classroom': 'C', 'support': 'X'},
         {'classroom': 'C', 'support': 'Y'}]

# Sample the pairs based on the probabilities
fast_classrooms = np.random.choice(pairs, size=nsimulations, p=p)
编辑:


与OP发布的方法相比,这个代码花费了
0.6193864345550537秒
,而OP发布的方法花费了
7.815439224243164秒
。@Tom McLean在评论中也证实了这一点。

我认为当前的最佳答案是最优雅的,但由于条件的可扩展性,我想加入其中:

将numpy导入为np
fast_教室=np.random.choice(['A','B','C',size=n模拟,p=[.4,.25,.35])
np.分段(快速教室,[快速教室='A',快速教室='B',快速教室='C'],
[lambda X:X”如果np.random.random()<.5否则为“Y”,
lambda X:“X”如果np.random.random()<.6否则为“Y”,
lambda X:“X”如果np.random.random()<.35否则为“Y”
])
out:数组(['X','X','Y',…,'Y','X','X']

在我的机器YMMV上进行10英里模拟约660毫秒我认为当前的最佳答案是最优雅的,但由于条件的可扩展性,我想加入其中:

将numpy导入为np
fast_教室=np.random.choice(['A','B','C',size=n模拟,p=[.4,.25,.35])
np.分段(快速教室,[快速教室='A',快速教室='B',快速教室='C'],
[lambda X:X”如果np.random.random()<.5否则为“Y”,
lambda X:“X”如果np.random.random()<.6否则为“Y”,
lambda X:“X”如果np.random.random()<.35否则为“Y”
])
out:数组(['X','X','Y',…,'Y','X','X']

在我的机器上10英里模拟约660毫秒,YMMV

这是我当时的想法,但想不出实现它的方法这是一个如此优雅的答案!我想补充一点,人们可以使用
itertools.product
获得笛卡尔积(X和
Y
的组合以及概率),以防一个人有太多的词对无法手动键入。@Ifanh我真的很喜欢这个答案,它太聪明了!有没有一种快速的方法可以将一系列字典读入pandas中,这样
classification
support
就在它们自己的列中?
pandas。DataFrame(快速教室)
执行得很快