Monte Carlo Python 中多个条目的模拟
Monte Carlo Simulation for Multiple Entries in Python
我一直在努力创建一个 Monte Carlo 模拟,它将 运行 通过我的数据帧的每个 ID 并产生它们各自的均值和标准差。我已经能够编写代码来获取任何一个 ID,但无法遍历数据框中的整个 ID 列表。所以我可以单独编写每一行,但我需要代码遍历任何可变的 ID 列表。
在这里,我尝试创建一个列表列表,其中可以存储每组 Monte Carlo 观察值(并且可以从中获取平均值和标准差)。我不相信这将是最有效的编码方式,但这是我目前所知道的。无论如何 运行 每个 ID 的 Monte Carlo 模拟(没有具体调用每个)?我需要能够从列表中添加和删除各种 ID 和相应的数据。
这是后续:
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'])
IIUC,这就是你想要的:
- 对于每一行(代表一个
ID
),您需要总共 num_samples
Monte Carlo 次模拟该行是否达到其 Revenue
。
- 确定给定模拟实例是否达到其
Revenue
的方法是将 [0,1]
中随机抽取的值与该行的 Odds
进行比较(在标准 Monte Carlo时尚)。
- 您想知道所有样本中每一行的
Revenue
的均值和标准差。
如果是这样,您可以利用二项分布的抽样功能来做到这一点,而不是从均匀分布中抽取,然后根据 Odds
进行过滤。我将 post 在此答案末尾使用此方法的解决方案。
但是由于您已经开始使用统一绘制方法:我建议首先通过 num_samples
(又名 n_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
...
现在找出哪些采样实例 "achieved" 目标 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)})
注意:如果 ID
在 df
中的多行中重复,这一切都会有所不同。在这种情况下,您可以使用 groupby
然后进行汇总统计。但在您的示例中,ID
永远不会重复,所以我暂时保留原样的答案。
我一直在努力创建一个 Monte Carlo 模拟,它将 运行 通过我的数据帧的每个 ID 并产生它们各自的均值和标准差。我已经能够编写代码来获取任何一个 ID,但无法遍历数据框中的整个 ID 列表。所以我可以单独编写每一行,但我需要代码遍历任何可变的 ID 列表。
在这里,我尝试创建一个列表列表,其中可以存储每组 Monte Carlo 观察值(并且可以从中获取平均值和标准差)。我不相信这将是最有效的编码方式,但这是我目前所知道的。无论如何 运行 每个 ID 的 Monte Carlo 模拟(没有具体调用每个)?我需要能够从列表中添加和删除各种 ID 和相应的数据。
这是后续:
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'])
IIUC,这就是你想要的:
- 对于每一行(代表一个
ID
),您需要总共num_samples
Monte Carlo 次模拟该行是否达到其Revenue
。 - 确定给定模拟实例是否达到其
Revenue
的方法是将[0,1]
中随机抽取的值与该行的Odds
进行比较(在标准 Monte Carlo时尚)。 - 您想知道所有样本中每一行的
Revenue
的均值和标准差。
如果是这样,您可以利用二项分布的抽样功能来做到这一点,而不是从均匀分布中抽取,然后根据 Odds
进行过滤。我将 post 在此答案末尾使用此方法的解决方案。
但是由于您已经开始使用统一绘制方法:我建议首先通过 num_samples
(又名 n_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
...
现在找出哪些采样实例 "achieved" 目标 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)})
注意:如果 ID
在 df
中的多行中重复,这一切都会有所不同。在这种情况下,您可以使用 groupby
然后进行汇总统计。但在您的示例中,ID
永远不会重复,所以我暂时保留原样的答案。