Бета-версии и дистрибутивы lognorm в PostgreSQL? - PullRequest
0 голосов
/ 09 декабря 2018

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

Мне интересно, есть ли способ запустить его непосредственно в базе данных, где япоказатель производительности будет намного лучше.Я могу генерировать случайные числа, но я не видел статистических функций распределения.

Первый шаг, который мне уже очень помог бы, это:

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

Как мне это сделать?

1 Ответ

0 голосов
/ 12 декабря 2018

Методология

Как вы указали, PostgreSQL может генерировать Унифицированное распределение с помощью функции random().

Общий ответ на этот вопрос:1008 * Выборка обратного преобразования .И ограничения этой методологии:

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

Простой пример: экспоненциальный

Выборка обратного преобразования хорошо работает для Экспоненциальное распределение :

CREATE OR REPLACE FUNCTION expon(N INTEGER, l FLOAT = 1)
RETURNS SETOF FLOAT AS
$BODY$
SELECT
    -(1/l)*ln(1 - random())
FROM
    generate_series(1, N) AS i;
$BODY$
LANGUAGE SQL;

Эта функция генерирует N выборок, взятых из экспоненциального распределения параметра l.

Логнормальное

Для Логнормальное распределение Функция квантиляn опирается на функцию Error , которая не реализована в PostgreSQL.Таким образом, нам нужно реализовать отсутствующую функцию (которая является неотъемлемой, не невозможной, используя WINDOWING функции , но, вероятно, не лучшая идея) или найти другой способ.

К счастьюмы можем сгенерировать нормальное распределение выборок, используя преобразование Бокса-Мюллера :

CREATE OR REPLACE FUNCTION norm(N INTEGER, mu FLOAT = 0, sigma FLOAT = 1)
RETURNS SETOF FLOAT AS
$BODY$
SELECT
    sigma*sqrt(-2.*ln(random()))*cos(2*pi()*random()) + mu
FROM
    generate_series(1, N) AS i;
$BODY$
LANGUAGE SQL;

Следующий вызов:

SELECT norm(10000);

Дает:

enter image description here

И MLE возвращает (mu=0.021131501222537274, sigma=1.0042820700537662) не так уж плохо, мы можем быть на хорошем пути.

Тогда мы можем взять экспоненту этой функции:

CREATE OR REPLACE FUNCTION lognorm(N INTEGER, mu FLOAT = 0, sigma FLOAT = 1)
RETURNS SETOF FLOAT AS
$BODY$
SELECT
    exp(x)
FROM
    norm(N, mu, sigma) AS x;
$BODY$
LANGUAGE SQL;

И у нас есть PRG для логнормального распределения.

Следующий вызов:

SELECT lognorm(10000);

Также даетприемлемый результат:

enter image description here

MLE возвращает (sigma=0.9996878296400589, loc=0.0, exp(mu)=1.0002728392916154).

Числовая интеграция и функция ошибок

Хотя это можетне будьте производительными, довольно легко оценить функцию ошибки с PostgreSQL, используя правило трапеции .Мысль это наивная реализация:

CREATE OR REPLACE FUNCTION erf(x FLOAT, dx NUMERIC = 1e-3)
RETURNS FLOAT AS
$BODY$
WITH
D AS (
SELECT
    y::FLOAT,
    exp(-((y::FLOAT)^2)) AS fx0,
    LEAD(exp(-((y::FLOAT)^2))) OVER(ORDER BY y) AS fx1
FROM
    generate_series(0, x::NUMERIC, dx) AS y
)
SELECT
    COALESCE((2/sqrt(pi()))*SUM((D.fx1 + D.fx0)*dx::FLOAT/2), 0.)
FROM D;
$BODY$
LANGUAGE SQL IMMUTABLE;

И если мы сравним результат с точной формой (Python, scipy ), это выглядит не так уж плохо, мы получаем по крайней мере 6 значимых цифр:

      x      psql     scipy        errabs        errrel
0   0.0  0.000000  0.000000  0.000000e+00           NaN
5   0.5  0.520500  0.520500 -7.323189e-08 -1.406953e-07
10  1.0  0.842701  0.842701 -6.918458e-08 -8.209863e-08
15  1.5  0.966105  0.966105 -2.973257e-08 -3.077571e-08
20  2.0  0.995322  0.995322 -6.888995e-09 -6.921371e-09
25  2.5  0.999593  0.999593 -9.076190e-10 -9.079885e-10
30  3.0  0.999978  0.999978 -6.962642e-11 -6.962795e-11
35  3.5  0.999999  0.999999 -3.149592e-12 -3.149594e-12
40  4.0  1.000000  1.000000 -8.404388e-14 -8.404388e-14
45  4.5  1.000000  1.000000  1.110223e-16  1.110223e-16
50  5.0  1.000000  1.000000  2.442491e-15  2.442491e-15

enter image description here

Таким образом, мы могли бы использовать функцию erf для выполнения выборки обратного преобразования для нормального и логнормального значений, как мы это делали для экспоненциального, но я мог быбыть плохой идеейОн должен работать плохо из-за сложности алгоритма и неточностей интеграции.

Beta

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

В этом случае вам может понадобиться скомпилировать функцию на каком-то языке программирования (например, C / C ++) и связатьэто функция PostgreSQL, как предложил @Nick Barnes в своем комментарии.

Технические соображения

Как @Nick Barnes указал в своих комментариях:

  • Функция с использованием random() не IMMUTABLE (они VOLATILE по умолчанию), поскольку они изменяют начальное значение PRG PostgreSQL;
  • Текущие реализации, представленные здесь, наивны, они не обрабатывают крайние случаи, такие как ln(0.);
  • Функции в LANGUAGE SQL обычно работают хорошо (хотя мы должны учитывать их сложности и их сходимости);
  • Возвращать SETOF FLOAT лучше, чем использовать FLOAT[], и избегать необходимости unnest(), как я делал в предыдущих версиях функций SQL;
  • Ограничение приведения, такое как ::FLOAT, когда это возможно;
  • Существует функция pi(), нет необходимости оценивать ее с помощью 2.*acos(0.).
...