使用BigQuery进行分层随机抽样?

时间:2018-10-20 00:41:28

标签: sql google-bigquery

如何在BigQuery上进行分层抽样?

例如,我们希望使用category_id作为层次的比例为10%的分层样本。我们的某些表中最多有11000个category_id。

2 个答案:

答案 0 :(得分:6)

使用#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条评论的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

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

答案 1 :(得分:3)

我认为获取成比例分层样本的最简单方法是按类别对数据进行排序,然后对数据进行“第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行。对于较小的类别,它需要所有行,而对于较大的类别,它需要随机抽样。

这两种方法都很方便。它们可以同时处理多个维度。第一个具有特别好的功能,它也可以使用数字尺寸。