Python для вероятностного программирования

Как я рассказывал ранее   для моделирования реальных процессов исключительно полезным  инструментом являются распределения  случайных величин. С их помощью  определяется пространство событий и задаются вероятности,  соответствующие каждому из них (например, множество голосов за/против в  ходе опроса или серия A/B тестирований с результатами наличия/отсутствия  конверсий).

В предыдущей статье  мы разобрались с тем, как создавать распределения вероятностей, а  теперь рассмотрим функции для работы с ними. Остановимся на возможностях  библиотеки SciPy.

В частности, в ней реализован метод rvs, с помощью которого осуществляется случайная выборка из распределения. При этом, задав параметр size,   можно запрашивать сразу массивы таких выборок. Например, получим 1000  значений из нормального распределения со средним 0 и стандартным  отклонением 1:

import scipy.stats as stats
import seaborn as sns
sns.set()

loc, scale  = 0,1    
distr = stats.norm(loc=loc, scale = scale)    
points_num=10000
distr.rvs(size=points_num)

На основе полученных 10000 значений можно построить график распределения, применяя функцию kdeplot библиотеки Seaborn (подробнее о ней я рассказывал здесь):

sns.kdeplot(distr.rvs(size=points_num), label = f'Нормальное распределение со средним {loc}\nи стандартным отклонением {scale},\nполученное путем случайной выборки {points_num} точек')

Другой  важной задачей является получение значения кумулятивной функции  распределения в заданной точке (вероятность того, что  случайная  величина не превышает этого числа), для чего используется   метод cdf. Например, с помощью рассмотренного метода rvs  случайно извлечем для равномерного распределения на отрезке [0,1] 1000  точек и для каждой из них посчитаем кумулятивную функцию распределения:

beg = 0
width = 1
distr = stats.uniform(loc = beg, scale = width)
points_num=10000
x = distr.rvs(size=points_num)  
distr.cdf(x)

Визуализируем полученные результаты с помощью функции sns.lineplot (подробнее рассказывал здесь):

sns.lineplot(x,distr.cdf(x), label = f'Кумулятивная функция распределения\nдля равномерного распределения на отрезке [{beg},{beg+width}]')

Теперь  остановимся на методах, извлекающих значение вероятности (для  дискретных случайных величин) или функции плотности вероятности (для  абсолютно непрерывных случайных величин) в заданных точках.

Для дискретных случайных величин мы можем извлечь вероятность точек путем вызова метода pmf.  Например, зададим Пуассоновское распределение вероятности с параметром  mu=3  и для 10 точек (0:9) посчитаем их вероятность с выводом значений и  визуализацией на графике.

mu=3
distr = stats.poisson(mu=mu)    
points_num=10
x = np.arange(10)
plt.vlines(x,0,distr.pmf(x),label = f'Пуассоновское распределение вероятности {points_num} неотрицательных\nцелых значений с параметром mu = {mu}')
plt.legend()

Для абсолютно непрерывных случайных величин (множество точек не  счетно и вероятность каждой равна нулю) для визуализации распределения  вероятностей применяют функцию плотности вероятности - pdf.  Например, зададим ее для 10000 точек на отрезке [0,3] экспоненциального  распределения с параметром lambda_=4 и отобразим результаты:

x = np.linspace(0,3,10000)    
lambda_ = 4
distr = stats.expon(scale=1/lambda_)
distr.pdf(x)    
plt.plot(x,distr.pdf(x), label= f'Распределение экспоненциальной случайной\nвеличины с параметром lambda_ равным {lambda_}')
plt.legend()

Полезным методом является  ppf,  применяемый для извлечения значения случайной величины,  которое с  заданной вероятностью не меньше всех других значений (квантиль,  процентиль). Данное значение часто используется при различных проверках,  например, истинности нулевой гипотезы  (подробнее читай здесь). Давайте приведем пример для нормальной случайной величины с математическим ожиданием 0 и стандартным отклонением 1:

loc, scale  = 0,1    
distr = stats.norm(loc=loc, scale = scale)
distr.ppf(0.975)    
distr.ppf(0.95)

Также для распределений вычисляются такие описательные статистики, как математическое ожидание, дисперсия ... (подробнее здесь). Для их  получения можно воспользоваться методом stats  c параметром moments. Так, для получения математического ожидания и дисперсии предыдущей случайной величины можно поступить так:

distr.stats(moments='mv')