【问题标题】:Stratified random sampling with BigQuery?使用 BigQuery 进行分层随机抽样?
【发布时间】:2019-03-24 21:10:08
【问题描述】:

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

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

【问题讨论】:

    标签: sql google-bigquery


    【解决方案1】:

    使用#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 cmets 的子版块

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • QQ - 为什么说“第一个有一个特别好的功能,它也可以处理数字维度。”seqnum 在这两种情况下都是一个数字。唯一的区别是,在一种情况下,您(试图)对每个类别采取 固定百分比 的样本,而在第二种情况下,您(最多)采取 固定(并且相等) 每个类别的样本数,对吗?
      • @Josh 。 . .我的意思是,如果您想按数字列进行分层,第 n 个样本将起作用,例如 row_number() over (order by income) 也适用于模数方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-16
      • 2016-07-16
      • 2014-06-22
      • 2018-10-16
      • 1970-01-01
      • 2013-01-28
      • 2019-09-20
      相关资源
      最近更新 更多