web-dev-qa-db-ja.com

BigQueryによる層別ランダムサンプリング?

BigQueryで層別サンプリングを行うにはどうすればよいですか?

たとえば、category_idを階層として使用して、10%の比例階層化サンプルが必要です。一部のテーブルには最大11000のcategory_idがあります。

10
Felipe Hoffa

#standardSQL、テーブルとその統計を定義しましょう。

WITH table AS (
  SELECT *, subreddit category
  FROM `fh-bigquery.reddit_comments.2018_09` a
), table_stats AS (
  SELECT *, SUM(c) OVER() total 
  FROM (
    SELECT category, COUNT(*) c 
    FROM table
    GROUP BY 1 
    HAVING c>1000000)
)

このセットアップでは:

  • subredditがカテゴリになります
  • コメントが1000000を超えるサブレディットのみが必要です

したがって、サンプルに各カテゴリの1%が必要な場合:

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 1/100 
)
GROUP BY 2

enter image description here

または、約80,000のサンプルが必要だとしましょう。ただし、すべてのカテゴリで比例的に選択されます。

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 80000/total
)
GROUP BY 2

enter image description here

ここで、各グループから同じ数のサンプルを取得したい場合(たとえば、20,000):

SELECT COUNT(*) samples, category, ROUND(100*COUNT(*)/MAX(c),2) percentage
FROM (
  SELECT id, category, c  
  FROM table a
  JOIN table_stats b
  USING(category)
  WHERE Rand()< 20000/c
)
GROUP BY 2

enter image description here

各カテゴリから正確に20,000要素が必要な場合:

SELECT ARRAY_LENGTH(cat_samples) samples, category, ROUND(100*ARRAY_LENGTH(cat_samples)/c,2) percentage
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand() LIMIT 20000) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
)

enter image description here

各グループの正確に2%が必要な場合:

SELECT COUNT(*) samples, sample.category, ROUND(100*COUNT(*)/ANY_VALUE(c),2) percentage
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand()) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
), UNNEST(cat_samples) sample WITH OFFSET off
WHERE off<0.02*c
GROUP BY 2

enter image description here


この最後のアプローチが必要な場合、実際にデータを取り出したいときに失敗することに気付くでしょう。最大のグループサイズに似た初期のLIMITは、必要以上のデータを並べ替えないようにします。

SELECT sample.*
FROM (
  SELECT ARRAY_AGG(a ORDER BY Rand() LIMIT 105000) cat_samples, category, ANY_VALUE(c) c
  FROM table a
  JOIN table_stats b
  USING(category)
  GROUP BY category
), UNNEST(cat_samples) sample WITH OFFSET off
WHERE off<0.02*c
11
Felipe Hoffa

比例した層別サンプルを取得する最も簡単な方法は、データをカテゴリ順に並べ、データの「n番目」のサンプルを実行することです。 10%のサンプルでは、​​10行ごとに必要です。

これは次のようになります。

select t.*
from (select t.*,
             row_number() over (order by category order by Rand()) as seqnum
      from t
     ) t
where seqnum % 10 = 1;

注:これは、すべてのカテゴリが最終的なサンプルになることを保証するものではありません。 10行未満のカテゴリは表示されない場合があります。

同じサイズのサンプルが必要な場合は、within各カテゴリを注文し、固定数を取得します。

select t.*
from (select t.*,
             row_number() over (partition by category order by Rand()) as seqnum
      from t
     ) t
where seqnum <= 100;

注:これは、各カテゴリ内に100行が存在することを保証するものではありません。小さいカテゴリのすべての行と、大きいカテゴリのランダムなサンプルが使用されます。

これらの方法はどちらも非常に便利です。同時に複数のディメンションを操作できます。 1つ目は特に数値のディメンションでも機能する、特に素晴らしい機能を備えています。

4
Gordon Linoff