【问题标题】:BQ SQL solution solution for comparing rows based on variance基于方差比较行的BQ SQL解决方案
【发布时间】:2018-03-04 11:33:24
【问题描述】:

我正在尝试比较 BigQuery 中抓取的零售商品价格数据(约 2-3B 行,具体取决于时间段和零售商);旨在识别有意义的价格差异。例如,1.99 美元对 2.00 美元没有意义,但 1.99 美元对 2.50 美元是有意义的。有意义被量化为价格之间 2% 的差异。

一个项目的示例数据集如下所示:

ITEM       Price($)  Meaningful (This is the column I'm trying to flag) 
Apple      $1.99     Y (lowest price would always be flagged)
Apple      $2.00     N ($1.99 v $2.00)
Apple      $2.01     N ($1.99 v $2.01)  Still using $1.99 for comparison
Apple      $2.50     Y ($1.99 v $2.50)  Still using $1.99 for comparison
Apple      $2.56     Y ($2.50 v $2.56)  Now using $2.50 as new comp. price
Apple      $2.62     Y ($2.55 v $2.62)  Now using $2.56 as new comp. price

我希望仅使用 SQL 窗口函数(超前、滞后、分区结束等)将当前行的价格与下一行的价格进行比较来解决问题。但是,当我得到一个无意义的价格时,这不能正常工作,因为我总是希望将下一个值与最近的有意义的价格进行比较(参见上面的 2.50 美元行示例,与前一行中的 2.00 美元而不是 2.01 美元进行比较)

我的问题:

  • 是否可以在 BigQuery 中单独使用 SQL 来解决这个问题? (例如,我忽略了哪些创造性的 SQL 逻辑解决方案,例如基于方差量的分桶?)
  • 由于无法将存储过程与 BQ 一起使用,我有哪些编程选项? GCP Datalab 中的 Python/数据框? BQ UDF?

【问题讨论】:

  • "例如 $1.99 vs $2.00 没有意义,但是 $1.99 vs $2.50 是有意义的。"不幸的是,该描述可能对您有帮助,但对其他人没有意义。您关心的确切阈值是多少?它是以美元、百分比还是其他一些单位来衡量的?
  • 好点,我将它包含在表头描述中作为 (> 20% Diff) 但我将编辑要求更具体。对我来说,价格之间 20% 或更大的差异被认为是有意义的。
  • 您应该使用 lag 在每一行中保留“最后一个有意义的值”的“副本” - 然后进行比较
  • 有意义的标志列实际上并不存在于数据中,这基本上是我试图用 SQL 计算的。我最初尝试使用 LAG,但我的问题是如何根据“最后一个有意义的值”使滞后偏移动态化。例如,如果我在 2.50 美元行,我需要回顾 3 个价格以与“最后一个有意义的值”(1.99 美元)进行比较。但是,如果在 2.62 美元的行,我只需要回顾 1 行(2.56 美元)。我更新了我的数据集示例以纠正它,鉴于我提到的 20% 方差阈值,我的原始值不正确。

标签: sql google-bigquery bigdata gcp


【解决方案1】:

以下是 BigQuery 标准 SQL

#standardSQL
CREATE TEMPORARY FUNCTION x(prices ARRAY<FLOAT64>)
RETURNS ARRAY<STRUCT<price FLOAT64, flag STRING>>
LANGUAGE js AS """
  var result = [];
  var last = 0;
  var flag = '';
  for (i = 0; i < prices.length; i++){
    if (i == 0) {
      last = prices[i];
      flag = 'Y'
    } else {
      if ((prices[i] - last)/last > 0.02) {
        last = prices[i];
        flag = 'Y'
      } else {flag = 'N'}
    }
    var rec = [];
    rec.price = prices[i];
    rec.flag = flag;
    result.push(rec); 
  } 
  return result;
""";
SELECT item, rec.* 
FROM (
  SELECT item, ARRAY_AGG(price ORDER BY price) AS prices
  FROM `yourTable`
  GROUP BY item
), UNNEST(x(prices) ) AS rec
-- ORDER BY item, price  

您可以使用以下来自您问题的虚拟数据来玩/测试它

#standardSQL
CREATE TEMPORARY FUNCTION x(prices ARRAY<FLOAT64>)
RETURNS ARRAY<STRUCT<price FLOAT64, flag STRING>>
LANGUAGE js AS """
  var result = [];
  var last = 0;
  var flag = '';
  for (i = 0; i < prices.length; i++){
    if (i == 0) {
      last = prices[i];
      flag = 'Y'
    } else {
      if ((prices[i] - last)/last > 0.02) {
        last = prices[i];
        flag = 'Y'
      } else {flag = 'N'}
    }
    var rec = [];
    rec.price = prices[i];
    rec.flag = flag;
    result.push(rec); 
  } 
  return result;
""";
WITH `yourTable` AS (
  SELECT 'Apple' AS item, 1.99 AS price UNION ALL
  SELECT 'Apple', 2.00 UNION ALL
  SELECT 'Apple', 2.01 UNION ALL
  SELECT 'Apple', 2.50 UNION ALL
  SELECT 'Apple', 2.56 UNION ALL
  SELECT 'Apple', 2.62 
)
SELECT item, rec.* 
FROM (
  SELECT item, ARRAY_AGG(price ORDER BY price) AS prices
  FROM `yourTable`
  GROUP BY item
), UNNEST(x(prices) ) AS rec
ORDER BY item, price    

结果如下

item    price   flag     
----    -----   ----
Apple   1.99    Y    
Apple   2.0     N    
Apple   2.01    N    
Apple   2.5     Y    
Apple   2.56    Y    
Apple   2.62    Y    

【讨论】:

  • 否 - 还没有尝试过,但很快就会;看起来很好地使用了 UDF。感谢您的详尽回复,让我测试一下,我会投票!
  • 效果很好,米哈伊尔,谢谢。 UDF 的表现给我留下了深刻的印象。我一直觉得他们大大降低了 BQ 的速度,但事实证明并非如此。
  • 太棒了!很高兴我们做到了!同时,在使用 JS UDF 时,请注意可能会增加计费层级,因为通常 JS UDF 非常消耗资源。我认为在这种特殊情况下你应该很好 - 但仍然 - 你能检查一下你得到了什么计费等级>只是为了安全起见
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-16
相关资源
最近更新 更多