Моделирование Монте-Карло для нескольких записей в Python

1

Я работал над созданием симулятора Монте-Карло, который будет проходить через каждый идентификатор моего фрейма данных и давать соответствующие средства и стандартные отклонения. Я смог написать код, чтобы получить его для любого ID, но не перебирать весь список идентификаторов в моем фреймворке данных. Поэтому я мог писать каждую строчку индивидуально, но мне нужен код для итерации через любой измененный список идентификаторов.

Здесь я попытался создать список списков, в которых каждый набор наблюдений Монте-Карло может быть сохранен (и можно взять среднее и std). Я не верю, что это будет самый эффективный способ кодирования этого, но это то, что я знаю на данный момент. Есть ли в любом случае, чтобы запустить симуляцию Монте-Карло по каждому из идентификаторов (без специального вызова каждого)? Мне нужно иметь возможность добавлять и удалять различные идентификаторы и соответствующие данные из списка.

Это продолжение: использование Монте-Карло для прогнозирования доходов на 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'])
Теги:
pandas
python-3.x
montecarlo

1 ответ

2
Лучший ответ

IIUC, это то, что вы собираетесь:

  • Для каждой строки (которая представляет собой ID) вы хотите, чтобы общее число num_samples Monte Carlo num_samples, достигает ли эта строка ее Revenue.
  • То, как вы определяете, достигает ли данный имитируемый экземпляр своего Revenue, сравнивает случайное значение в [0,1] против Odds для этой строки (в стандартном методе Монте-Карло).
  • Вы хотите знать среднее и стандартное отклонение Revenue для каждой строки по всем образцам.

Если это так, вы можете сделать это, используя функцию выборки распределения Binomial вместо того, чтобы рисовать из униформы, а затем фильтровать на основе Odds. Я отправлю решение, используя этот подход в конце этого ответа.

Но так как вы начали с подхода Uniform-draw: я бы рекомендовал сначала создать матрицу выборки s_draws из n_rows = len(df) помощью num_samples (aka n_draws в моем коде ниже). Затем примените проверку Odds ко всем образцам в каждой строке s_draws. Затем умножьте на Revenue и возьмите среднее значение и sd для каждой строки. Как это:

Сначала нарисуем матрицу выборки:

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
...

И здесь версия с использованием Binomial выборки:

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 никогда не повторяется, поэтому я оставлю ответ как есть на данный момент.

  • 0
    Подход Uniform-Draw, который вы использовали, имеет для меня прекрасный смысл и хорошо работает, когда я применил его к своему коду. s_result дает именно то, что я ищу. Я все еще немного запутался с компонентом биномиальной выборки. Строка: pd.DataFrame({"avg": draws.mean(axis=0), "sd": draws.std(axis=0)}) возвращает фрейм данных длиной n_draws. Если эта версия кода более эффективна, я хотел бы найти способ ее использовать (или, по крайней мере, изучить ее на будущее).
  • 0
    Упс, извините, аргументы в mean и std должны быть axis=1 . Хороший улов! Биномиальный подход просто отражает интуицию, стоящую за сравнением случайных ничьих и Odds - это, в основном, и есть биномиальная случайная переменная. Любой подход должен дать одинаковые результаты.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню