Как сделать выборку из дистрибутива с учетом CDF в Python

Я хотел бы взять образцы из распределения вероятностей с помощью CDF 1 - e^(-x^2).

Есть ли метод в python/scipy/etc. чтобы вы могли выбирать из распределения вероятностей, учитывая только его CDF?


person fomin    schedule 06.03.2020    source источник
comment
Я немного заржавел, но если я правильно помню, CDF был неотъемлемой частью PDF. Так что это похоже на вопрос алгебры. Возможно: wolframalpha.com/input/?i=d%2Fdx+%281+-+e%5E%28-x%5E2%29%29   -  person pinegulf    schedule 06.03.2020
comment
@pinegulf Спасибо, но как тогда делать выборку?   -  person fomin    schedule 06.03.2020
comment
хорошо, поскольку вы знаете CDF со значением X, вы можете сделать цикл, в котором вы делаете какой-то шаг и выясняете, сколько стоит PDF, поскольку вы знаете дельту CDF.   -  person pinegulf    schedule 06.03.2020


Ответы (2)


Чтобы создать собственный класс случайных переменных с учетом CDF, вы можете создать подкласс scipy.rv_continuous и переопределить rv_continuous._cdf. Это автоматически сгенерирует соответствующий PDF-файл и другую статистическую информацию о вашем дистрибутиве, например.

import matplotlib.pyplot as plt
import numpy as np
from scipy import stats

class MyRandomVariableClass(stats.rv_continuous):
    def __init__(self, xtol=1e-14, seed=None):
        super().__init__(a=0, xtol=xtol, seed=seed)

    def _cdf(self, x):
        return 1-np.exp(-x**2)


if __name__ == "__main__":
    my_rv = MyRandomVariableClass()

    # sample distribution
    samples = my_rv.rvs(size = 1000)

    # plot histogram of samples
    fig, ax1 = plt.subplots()
    ax1.hist(list(samples), bins=50)

    # plot PDF and CDF of distribution
    pts = np.linspace(0, 5)
    ax2 = ax1.twinx()
    ax2.set_ylim(0,1.1)
    ax2.plot(pts, my_rv.pdf(pts), color='red')
    ax2.plot(pts, my_rv.cdf(pts), color='orange')

    fig.tight_layout()
    plt.show()

person Heike    schedule 06.03.2020
comment
Вау, это довольно удивительно. Знаете ли вы, как он это делает (то есть создает PDF-файл, а также дает возможность взять из него образец) под капотом? Предполагая, что на самом деле это не делает никакого дифференцирования, это должно быть какое-то числовое приближение. - person fomin; 06.03.2020

Выборка обратного преобразования

Чтобы дополнить решение Heike, вы можете использовать выборку с обратным преобразованием для выборки через CDF. :

import math, random
import matplotlib.pyplot as plt

def inverse_cdf(y):
    # Computed analytically
    return math.sqrt(math.log(-1/(y - 1)))

def sample_distribution():
    uniform_random_sample = random.random()
    return inverse_cdf(uniform_random_sample)

x = [sample_distribution() for i in range(10000)]
plt.hist(x, bins=50)
plt.show()

Как это делает SciPy

Мне было очень любопытно посмотреть, как это работает и в SciPy. На самом деле похоже, что он делает что-то очень похожее на вышеизложенное. На основе документов SciPy:

Метод по умолчанию _rvs основан на обратном cdf, _ppf, применяемом к однородной случайной переменной. Чтобы эффективно генерировать случайные переменные, необходимо либо перезаписать _ppf по умолчанию (например, если обратный cdf может быть выражен в явной форме), либо метод выборки должен быть реализован в пользовательском методе _rvs.

И на основе исходного кода SciPy, _ppf (т. е. обратный CDF) на самом деле выглядит аппроксимированным численно, если не указано явно. Очень круто!

person Jeff N    schedule 14.03.2021