【发布时间】:2019-03-24 21:10:08
【问题描述】:
如何在 BigQuery 上进行分层抽样?
例如,我们想要使用 category_id 作为分层的 10% 比例分层样本。在我们的一些表中,我们有多达 11000 个 category_id。
【问题讨论】:
标签: sql google-bigquery
如何在 BigQuery 上进行分层抽样?
例如,我们想要使用 category_id 作为分层的 10% 比例分层样本。在我们的一些表中,我们有多达 11000 个 category_id。
【问题讨论】:
标签: sql google-bigquery
使用#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 行的类别可能不会出现。
如果您想要相同大小的样本,则在每个类别中排序 并取一个固定数量:
select t.*
from (select t.*,
row_number() over (partition by category order by rand()) as seqnum
from t
) t
where seqnum <= 100;
注意:这并不能保证每个类别中存在 100 行。它获取较小类别的所有行和较大类别的随机样本。
这两种方法都很方便。它们可以同时处理多个维度。第一个有一个特别好的功能,它也可以处理数字维度。
【讨论】:
seqnum 在这两种情况下都是一个数字。唯一的区别是,在一种情况下,您(试图)对每个类别采取 固定百分比 的样本,而在第二种情况下,您(最多)采取 固定(并且相等) 每个类别的样本数,对吗?
row_number() over (order by income) 也适用于模数方法。