BigQuery 计算多列值之间的重叠百分比

Posted

技术标签:

【中文标题】BigQuery 计算多列值之间的重叠百分比【英文标题】:BigQuery Calculating Percent Overlap Between Values Multiple Columns 【发布时间】:2020-05-15 00:09:10 【问题描述】:

我对 SQL 和 bigquery 还很陌生,并且正在使用大约 140 万行的数据集。

我目前感兴趣的值是 category_name(字符串)、item_id(字符串)。我感兴趣的是计算 category_name 中每个值的不同 item_id(此列共有 269 个不同值)。基本上在我的基础数据集中,每一行数据都包含一个 item_id 的实例,它显示在一个 category_name 中,其中 item_id 可以根据当天出现的 category_names 的数量每天有多行。

我已经能够运行一个成功的查询,为 category_name 的每个值添加不同 item_id 的新列,现在最终输出应该是我无法弄清楚如何计算不同 item_id 的百分比对于 1 个类别名称,它也出现在每个其他类别名称中。所以基本上我正在寻找一个新列(如数据透视表),它将计算 2 个 category_names 的匹配 item_ids,然后将该计数除以 1 个 category_name 中的不同 item_id 的总数。因此,基本上每个 category_name 将有 269 个新列,并且每一行将表示基本 category_name 与每个其他 category_name 的重叠百分比。

这是我当前感兴趣的表格中的数据

category_name  |   item_id
---------------|------------
category1      |  item1
category2      |  item1
category3      |  item1
category1      |  item2
category4      |  item2
category1      |  item3
category5      |  item3
category5      |  item2
category6      |  item4
category3      |  item5
category3      |  item6
category1      |  item6
category2      |  item5
category1      |  item4

这是我当前的查询结果的样子

category_name  |  distinct_items
---------------|-----------------
category1      |  5
category2      |  2
category3      |  3
category4      |  1
category5      |  2
category6      |  1

这是我希望最终输出的样子:

category_name  | category1   |  category2  |   category3   |   category4   |  category5  |  category6
--------------------------------------------------------------------------------------------------------
category1      |   100%      |     20%     |      40%      |      20%      |     40%     |     20%
category2      |    50%      |     100%    |      100%     |       0%      |      0%     |     0%
category3      |    66.67%   |     66.67%  |      100%     |       0%      |      0%     |     0%
category4      |   100%      |      0%     |       0%      |      100%     |     100%    |     0%
category5      |   100%      |      0%     |       0%      |       50%     |     100%    |     0%
category6      |   100%      |      0%     |       0%      |       0%      |      0%     |    100%

本质上,category_name 的行值将是 category_name 是目标并将它们的 distinct_items 总数与其他 category_names 进行比较,并根据 item_ids 查找匹配百分比/distinct_items 总数。如果有另一种方法可以在没有数据透视表的情况下获得此输出,那也将不胜感激。上下文有 269 个 category_names 和 6525 个不同的 item_ids。

如果有一个更简单的公式,我可以在谷歌数据工作室中使用这个聚合,因为数据工作室的最终输出应该是散点图,x 和 y 轴类别名称和气泡是重叠百分比所以本质上只是用散点图可视化数据透视表结果。如果我的描述和问题中的任何内容没有意义或需要更清晰,请随时标记我并让我知道什么是令人困惑的。任何帮助是极大的赞赏!谢谢

【问题讨论】:

【参考方案1】:

以下是 BigQuery 标准 SQL

第 1 步 - 动态生成查询文本,因此您无需手动输入所有 269 个类别...

#standardSQL
SELECT '''SELECT category_name, ''' || 
  STRING_AGG(DISTINCT
    ' MAX(IF(category_name2 = "' || category_name || '", percent, NULL)) AS ' || category_name
  ) || '''
FROM (
  SELECT t1.category_name, t2.category_name category_name2,
    ROUND(100 * COUNTIF(t1.item_id = t2.item_id) / COUNT(DISTINCT t1.item_id), 2) percent
  FROM `project.dataset.table` t1 
  CROSS JOIN `project.dataset.table` t2
  GROUP BY t1.category_name, t2.category_name
)
GROUP BY category_name
'''
FROM `project.dataset.table`

如果您针对问题中的示例数据运行以上 - 您将获得以下查询的扁平版本

SELECT category_name, 
  MAX(IF(category_name2 = "category1", percent, NULL)) AS category1, 
  MAX(IF(category_name2 = "category2", percent, NULL)) AS category2, 
  MAX(IF(category_name2 = "category3", percent, NULL)) AS category3, 
  MAX(IF(category_name2 = "category4", percent, NULL)) AS category4, 
  MAX(IF(category_name2 = "category5", percent, NULL)) AS category5, 
  MAX(IF(category_name2 = "category6", percent, NULL)) AS category6 
FROM ( 
  SELECT t1.category_name, t2.category_name category_name2, 
  ROUND(100 * COUNTIF(t1.item_id = t2.item_id) / COUNT(DISTINCT t1.item_id), 2) percent 
  FROM `project.dataset.table` t1 
  CROSS JOIN `project.dataset.table` t2 
  GROUP BY t1.category_name, t2.category_name 
) 
GROUP BY category_name   

第 2 步 - 复制第 1 步中的查询结果并将其作为查询运行 - 就是这样!

如果您将其应用于问题中的样本数据 - 输出将是

Row category_name   category1   category2   category3   category4   category5   category6    
1   category1       100.0       20.0        40.0        20.0        40.0        20.0     
2   category2       50.0        100.0       100.0       0.0         0.0         0.0  
3   category3       66.67       66.67       100.0       0.0         0.0         0.0  
4   category4       100.0       0.0         0.0         100.0       100.0       0.0  
5   category5       100.0       0.0         0.0         50.0        100.0       0.0  
6   category6       100.0       0.0         0.0         0.0         0.0         100.0         

注意 1:您可以使用您选择的任何客户端自动执行上述整个过程 注2:我在您的简化示例中主要使用数据。在您的实际情况下,您可能需要一些小的调整 - 如果您对此有任何问题 - 请发布新问题

【讨论】:

您好,谢谢您的回答!我尝试了您的方法,得到的结果是单行和单列,其中包含有关行中每一列的动态文本的大量信息。我尝试将此结果保存到表中并从该新表中运行 select * 并且没有任何改变。如果我在如何将第 1 步到第 2 步的结果保存时犯了一些错误,请告诉我。我还想知道您在回答中在 t1 和 t2 中指的是哪些表。请告诉我,非常感谢! 1) t1 和 t2 是别名。 2)你应该用你的桌子替换project.dataset.table。 3)再读一遍答案。您应该按照描述的两个步骤 - 运行第 1 步,它将返回一行和一列。只需复制该列中的文本并将其粘贴到查询编辑器中,然后运行它! - 如果有更多问题,请告诉我 再一次 - 您在第 1 步中唯一需要做的就是将 project.dataset.table 替换为您自己的表参考。您需要在那里替换三个实例。此外,显然这个表应该有category_nameitem_id 列,因为它是有问题的 - 否则它们也需要用正确的列名替换。祝你好运 是 - ! 不能用于列名。如果类别值在 ir 中具有 ! 类别 - 您应该调整查询以确保将此类不允许的字符替换为例如下划线。或者只是删除它们!见答案中的注2 您可以轻松做到这一点,只需将第三行中第二次出现的category_name 替换为REPLACE(category_name, '!', '_') 之类的东西【参考方案2】:

您可以使用条件聚合:

select t.category_name,
       countif( t2.category_name = 'category1' ) / count(*) as category1,
       countif( t2.category_name = 'category2' ) / count(*) as category2,
       countif( t2.category_name = 'category3' ) / count(*) as category3,
       countif( t2.category_name = 'category4' ) / count(*) as category4,
       countif( t2.category_name = 'category5' ) / count(*) as category5
from t join
     t t2
     on t.item = t2.item
group by t.category_name;

如果将值放在行而不是列中,这会更简单:

select t.category_name, t2.category_name,
       count(*) / sum(count(*)) over (partition by t.category_name) as ratio
from t join
     t t2
     on t.item = t2.item
group by t.category_name, t2.category_name;

【讨论】:

感谢您的快速回答。有什么方法可以减轻必须编写每个类别名称的情况,例如使用某种映射函数来获取不同的类别名称?不过,我会尝试这个答案,如果它得到我想要的东西,请告诉你。非常感谢! @yaboy618 。 . .如果您将值放在行中而不是列中。 嘿,再次感谢您的回答。我尝试运行它,但遇到了一个问题,因为对于 category_name,其中一些有重复项,所以我收到错误“不支持结果中的重复列名”。你知道有什么办法吗? @yaboy618 。 . .此代码中没有重复的列名。每个 column_name 的计算应该只有一个。 我尝试尽我所能通过连接另一个区分某些类别名称的列值来缓解可能的问题,因此肯定没有重复。有没有办法检查重复项,或者您知道我遇到此问题的任何可能原因吗?

以上是关于BigQuery 计算多列值之间的重叠百分比的主要内容,如果未能解决你的问题,请参考以下文章

多边形重叠百分比

用 R 中的多列按组计算百分比

如何计算 R 中多列的组内百分比变化?

SQL:如何使用多列分区计算百分比增加

如何计算多边形之间的所有成对交互以及 R 中 sf 的百分比覆盖率?

计算边界框重叠的百分比,用于图像检测器评估