带bigquery的sql分层随机抽样?

sycxhyv7  于 2021-08-09  发布在  Java
关注(0)|答案(2)|浏览(412)

如何对bigquery进行分层抽样?
例如,我们需要一个10%比例的分层样本,使用类别id作为地层。我们的一些表中有多达11000个类别ID。

q0qdq0h2

q0qdq0h21#

#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条评论的subreddits
所以,如果我们想要样本中每个类别的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


或者说,我们需要大约80000个样本,但在所有类别中按比例选择:

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


现在,如果你想从每组获得相同数量的样本(比如说,20000个):

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


如果您希望每个类别中正好有20000个元素:

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
nhjlsmyf

nhjlsmyf2#

我认为获得成比例分层样本的最简单方法是按类别对数据进行排序,然后对数据进行第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行。它将所有的行用于较小的类别,并随机抽取较大的类别。
这两种方法都很方便。它们可以同时处理多个维度。第一个有一个特别好的特性,它也可以处理数字尺寸。

相关问题