【问题标题】:Monte Carlo Simulation for Multiple Entries in PythonPython中多个条目的蒙特卡罗模拟
【发布时间】:2019-03-01 17:08:03
【问题描述】:

我一直在努力创建一个蒙特卡罗模拟,该模拟将遍历我的数据框的每个 ID,并产生它们各自的均值和标准偏差。我已经能够编写代码来获取任何一个 ID,但不能遍历我的数据框中的整个 ID 列表。所以我可以单独编写每一行,但我需要代码来遍历任何可变的 ID 列表。

在这里,我尝试创建一个列表列表,其中可以存储每组 Monte Carlo 观测值(并且可以从中获取平均值和标准差)。我不相信这将是最有效的编码方式,但这是我目前所知道的。无论如何要在每个 ID 上运行 Monte Carlo 模拟(没有专门调用每个 ID)?我需要能够从列表中添加和删除各种 ID 和相应的数据。

这是对以下内容的跟进:Utilizing Monte Carlo to Predict Revenue in Python

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np


ID = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Revenue = [1000, 1200, 1300, 100 ,500, 0, 800, 950, 4321, 800, 1000, 1200, 1300, 100 ,500, 0, 800, 950, 4321, 800]
odds = [0.5, 0.6, 0.33, 0.1, 0.9, 0.87, 0.37, 0.55, 0.97, 0.09, 0.5, 0.6, 0.33, 0.1, 0.9, 0.87, 0.37, 0.55, 0.97, 0.09]

d = {'ID': ID, 'Revenue': Revenue, 'Odds': odds}
df = pd.DataFrame(d)
df['Expected Value'] = df['Revenue']*df['Odds']

print(df)

num_samples = 100
df['Random Number'] = np.random.rand(len(df))

def monte_carlo_array(df):
    for _ in range(len(df)):
        yield []

mc_arrays = list(monte_carlo_array(df))

# Fill each list with 100 observations (no filtering necessary)
id_1 = []
filter_1 = (df['ID'] == 5)

for _ in range(num_samples):
    sample = df['Revenue'] * np.where(np.random.rand(len(df)) < \
                          df['Odds'], 1, 0)
    for l in monte_carlo_array(df):
        for i in l:
        mc_arrays[i].append(sample.sum())
    id_1.append(sample.loc[filter_1].sum())


# Plot simulation results.
n_bins = 10
plt.hist([id_1], bins=n_bins, label=["ID: 1"])
plt.legend()
plt.title("{} simulations of revenue".format(num_samples))

print(mc_arrays)

df['Monte Carlo Mean'] = np.mean(mc_arrays[0])
print(df['Monte Carlo Mean'])

【问题讨论】:

    标签: python python-3.x pandas montecarlo


    【解决方案1】:

    IIUC,这就是你想要的:

    • 对于每一行(代表ID),您需要总共num_samples Monte Carlo 模拟该行是否达到其Revenue
    • 确定给定模拟实例是否达到其Revenue 的方法是将[0,1] 中的随机绘制值与该行的Odds 进行比较(以标准蒙特卡罗方式)。
    • 您想知道所有样本中每一行的Revenue 的平均值和标准差。

    如果是这样,您可以通过利用二项分布的采样功能来做到这一点,而不是从 Uniform 中提取然后基于 Odds 进行过滤。我将在这个答案的最后发布一个使用这种方法的解决方案。

    但是由于您已经开始使用 Uniform-draw 方法:我建议您首先通过num_samples(在下面的代码中也称为n_draws)制作n_rows = len(df) 的采样矩阵s_draws。然后对s_draws 的每一行中的所有样本应用Odds 检查。然后乘以Revenue,并取每一行的平均值和标准差。像这样:

    首先,绘制采样矩阵:

    np.random.seed(42)
    
    n_rows = len(df)
    n_draws = 5
    s_draws = pd.DataFrame(np.random.rand(n_rows, n_draws))
    
    # the matrix of random values between [0,1]
    # note: only showing the first 3 rows for brevity
    s_draws
               0         1         2         3         4
    0   0.374540  0.950714  0.731994  0.598658  0.156019
    1   0.155995  0.058084  0.866176  0.601115  0.708073
    2   0.020584  0.969910  0.832443  0.212339  0.181825
    ...
    

    现在找出哪些采样实例“达到”了目标Revenue

    s_rev = s_draws.apply(lambda col: col.lt(df.Odds) * df.Revenue)
    
    # the matrix of sampled revenue
    s_rev
           0     1     2     3     4
    0   1000     0     0     0  1000
    1   1200  1200     0     0     0
    2   1300     0     0  1300  1300
    ...
    

    最后,计算每一行的汇总统计/ID

    s_result = pd.DataFrame({"avg": s_rev.mean(axis=1), "sd": s_rev.std(axis=1)})
    
    # the summary statistics of each row of samples
    s_result
           avg          sd
    0    400.0  547.722558
    1    480.0  657.267069
    2    780.0  712.039325
    ...
    

    这是使用二项式采样的版本:

    draws = pd.DataFrame(
        np.random.binomial(n=1, p=df.Odds, size=(n_draws, n_rows)).T
    ).multiply(df.Revenue, axis=0)
    
    pd.DataFrame({"avg": draws.mean(axis=1), "sd": draws.std(axis=1)})
    

    注意:如果IDdf 的多行中重复,这一切都会有所不同。在这种情况下,您可以使用groupby,然后获取汇总统计信息。但在您的示例中,ID 永远不会重复,所以我现在将保持原样。

    【讨论】:

    • 您使用的 Uniform-draw 方法对我来说非常有意义,并且在我将其应用于我的代码时效果很好。 s_result 正在产生我正在寻找的东西。不过,我仍然对二项式采样组件有些困惑。该行:pd.DataFrame({"avg": draws.mean(axis=0), "sd": draws.std(axis=0)}) 正在返回一个 n_draws 长的数据帧。如果这个版本的代码效率更高,我想找到一种使用它的方法(或者至少为将来学习它)。
    • 哎呀,对不起,meanstd 中的参数应该是 axis=1。接得好!二项式方法只是捕捉了将随机抽取与Odds 进行比较背后的直觉——这基本上就是二项式随机变量的含义。任何一种方法都应该产生相同的结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 2012-04-26
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    相关资源
    最近更新 更多