Методология
Как вы указали, 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);
Дает:
И 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);
Также даетприемлемый результат:
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
Таким образом, мы могли бы использовать функцию 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.)
.