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

Fel*_*ffa 9 sql google-bigquery

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

例如,我们想要使用category_id作为分层的10%比例分层样本.在我们的一些表中,我们有多达11000个category_ids.

Fel*_*ffa 10

使用#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)
)
Run Code Online (Sandbox Code Playgroud)

在此设置中:

  • 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
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

或假设我们要约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
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

现在,如果您想从每个组中获取相同数量的样本(比如说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
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

如果您想从每个类别中准确获取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
)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

如果您要每个组的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
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


如果最后一种方法是您想要的,那么您可能会在实际想要获取数据时注意到它失败了。尽早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
Run Code Online (Sandbox Code Playgroud)


Gor*_*off 7

我认为获得按比例分层样本的最简单方法是按类别对数据进行排序并对数据进行“第 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;
Run Code Online (Sandbox Code Playgroud)

注意:这并不能保证所有类别都会在最终样本中。少于 10 行的类别可能不会出现。

如果您想要相同大小的样本,则每个类别中排序并取一个固定数量:

select t.*
from (select t.*,
             row_number() over (partition by category order by rand()) as seqnum
      from t
     ) t
where seqnum <= 100;
Run Code Online (Sandbox Code Playgroud)

注意:这并不能保证每个类别中存在 100 行。它需要较小类别的所有行和较大类别的随机样本。

这两种方法都非常方便。它们可以同时处理多个维度。第一个有一个特别好的特性,它也可以处理数字维度。