BigQuery에서 베타 분포를 따르는 난수를 생성하고 실제로 만들어졌는지 확인
16789 단어 BigQuery
재료 기사이므로, 정말로 난수 생성하고 싶은 분은 UDF를 javascript로 쓰는 등을 검토해 주세요.
사소한 관심으로 BigQuery에서 난수를 생성하고 싶습니다.
일이 있습니다.
베타 분포
베타 분포는 이런 형태를 하고 있다고 합니다. (wiki에서)
$\alpha$, $\beta$ 라는 두 개의 매개 변수가 있으며 그 값에 따라 모양이 변경됩니다.
이에 따라 난수를 생성하고 히스토그램을 표시하면 이런 형태가 될 것 같습니다.
난수
BigQuery에서는 0과 1 사이의 균일한 난수만 생성할 수 있습니다.
그러나 대부분의 유명 분포는 균일한 난수에서 이에 따른 분포로 변환하는 기법이 존재하기 때문에 이번에는 BigQuery에서 구현합니다.
정책은 사용자 정의 함수를 사용하여 베타 분포의 $\alpha$,$\beta$와 생성하는 난수의 수를 주면 그 수만큼 난수를 배열로 반환하는 함수를 만들고 싶습니다.
알고리즘은 다음 기사의 기본 알고리즘을 참고로 만들었습니다.
htps : // 코 m / 피요 7 / ms / 36b19c6 68 ba2 69cd
구현한 것이 이쪽
CREATE OR REPLACE FUNCTION math.generate_beta(a FLOAT64, b FLOAT64, n INT64) AS (
# ベータ分布に従う乱数を生成する
# https://qiita.com/piyo7/items/36b19c6ea68baa2a69cd
ARRAY(
WITH assert AS (
SELECT
IF(a > 0 AND b > 0, TRUE, ERROR('パラメータのa,bはいずれも正であることが必要です')) AS c1,
IF(n > 0, TRUE, ERROR('nは1以上です')) AS c2
)
, ab AS (
SELECT
a+b AS alpha,
IF(
math.min([a, b]) <= 1,
math.max([1/a, 1/b]),
SQRT((a+b-2)/(2*a*b-(a+b)))
) AS beta,
FROM assert
)
, abg AS (
SELECT alpha, beta,
a + (1 / beta) AS gamma
FROM ab
)
, u AS (
SELECT RAND() AS U1, RAND() AS U2
# 棄却される可能性があるので多めに作る
FROM UNNEST(GENERATE_ARRAY(0, CEIL(n * 1.5)))
)
, vw AS (
SELECT
U1, U2,
beta * LN(U1/(1-U1)) AS V,
a * EXP(beta * LN(U1/(1-U1))) AS W
FROM u, abg
)
, sampling AS (
SELECT
ROW_NUMBER() OVER () AS rank,
alpha * LN(a / (b+W)) + gamma * V - 1.3862944 < LN(U1*U1*U2) AS accept,
W / (b+W) AS X,
FROM vw, abg
)
SELECT X
FROM sampling
WHERE accept AND rank <= n
));
math.min, math.max는 주어진 배열의 최소값 최대 값을 검색하는 사용자 정의 함수입니다.
적당히 미리 만들어주세요.
이번의 샘플링(난수 생성)은 기각 샘플링이라고 하는 것으로, 생성한 균일한 난수와 동수의 샘플을 얻을 수 없습니다. 특정 조건을 충족시키지 못한 샘플은 기각(버려진)되어 버립니다. 그러므로 주어진 $n$보다 많은 균일 난수를 생성하고 나머지는 버리는 전략을 취했습니다. 1 함수가 재귀적으로 호출할 수 있다면 필요한 수의 샘플이 가능할 때까지 샘플링을 계속할 수 있습니다만, BigQuery는 할 수 없을 것 같아서 포기했습니다.
라고 할까 베타 분포는 기각 샘플링 밖에 없는 것일까?
표시
만든 베타 난수 생성 함수에서 많이 난수를 생성하여 히스토그램을 표시하고 싶습니다.
배열을 히스토그램으로 고치는 함수를 작성했습니다.
CREATE OR REPLACE FUNCTION math.histogram(arr ANY TYPE, bin INT64) AS (
# 配列を値によりビンにわけ個数をカウントする
ARRAY(
WITH min_max AS (
SELECT
math.min(arr) AS x_min,
(math.max(arr) - math.min(arr)) / bin AS d,
)
, bin_range AS (
SELECT
i,
x_min + i * d AS range_left, -- それぞれのビンの範囲の左端
x_min + (i+1) * d AS range_right, -- それぞれのビンの範囲の右端
FROM UNNEST(GENERATE_ARRAY(0, bin-1)) AS i, min_max
)
, hist AS (
SELECT
i,
COUNTIF(i IS NOT NULL) AS n,
FROM UNNEST(arr) AS x, bin_range
WHERE TRUE
AND x >= range_left
AND IF(i=bin-1, x <= range_right, x < range_right)
GROUP BY i
)
SELECT AS STRUCT
range_left AS l,
range_right AS r,
IFNULL(n, 0) AS n,
FROM bin_range LEFT JOIN hist USING (i)
));
이것을 사용하여 스프레드 시트로 봅시다.
SELECT (l + r) / 2 AS x, n
FROM UNNEST(math.histogram(math.generate_beta(2, 5, 10000), 100))
콘솔에서 쿼리를 실행한 후 데이터 탐색 버튼을 눌러 스프레드시트에서 분석할 수 있습니다.
일단 왜곡 된 산처럼 보였습니다.
$\alpha=2,\beta=5$이므로,
이쪽의 오렌지선과 같은 느낌이 될 것입니다만, 그것같은 것 같은 듯한.
알고리즘을 바꾸는 것만으로 여러가지 난수를 생성할 수 있으므로 여러분 해 봅시다!
추가
샘플 수를 늘리면 깨끗해졌습니다.
그러나, 정규화해 비교해 보면, 닮은 것 같고 다르기 때문에(최빈치의 값이 2.5 부근보다 상당히 먼, 0.8 이후의 밑단이 두껍다) 때문에 알고리즘의 실장이 잘못되어 있을지도 모릅니다.
행운이 나쁘면 샘플 수가 부족해집니다. ↩
Reference
이 문제에 관하여(BigQuery에서 베타 분포를 따르는 난수를 생성하고 실제로 만들어졌는지 확인), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/taniyam/items/982d25881e4bbd03f342텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)