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 이후의 밑단이 두껍다) 때문에 알고리즘의 실장이 잘못되어 있을지도 모릅니다.





행운이 나쁘면 샘플 수가 부족해집니다.

좋은 웹페이지 즐겨찾기