BigQueryで層別サンプリングを行うにはどうすればよいですか?
たとえば、category_idを階層として使用して、10%の比例階層化サンプルが必要です。一部のテーブルには最大11000のcategory_idがあります。
#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
がカテゴリになりますしたがって、サンプルに各カテゴリの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
または、約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
ここで、各グループから同じ数のサンプルを取得したい場合(たとえば、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
各カテゴリから正確に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
)
各グループの正確に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
この最後のアプローチが必要な場合、実際にデータを取り出したいときに失敗することに気付くでしょう。最大のグループサイズに似た初期の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
比例した層別サンプルを取得する最も簡単な方法は、データをカテゴリ順に並べ、データの「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つ目は特に数値のディメンションでも機能する、特に素晴らしい機能を備えています。