【问题标题】:BigQuery SQL: Average, geometric mean, remove outliers, medianBigQuery SQL:平均值、几何平均值、去除异常值、中值
【发布时间】:2018-08-23 08:27:17
【问题描述】:

我正在计算在 Stack Overflow 上获得回复的平均时间,结果毫无意义。

#standardSQL

WITH question_answers AS (
  SELECT * 
    , timestamp_diff(answers.first, creation_date, minute) minutes
  FROM (
    SELECT creation_date
      , (SELECT AS STRUCT MIN(creation_date) first, COUNT(*) c
         FROM `bigquery-public-data.stackoverflow.posts_answers` b
         WHERE a.id=b.parent_id
        ) answers
      , SPLIT(tags, '|') tags
    FROM `bigquery-public-data.stackoverflow.posts_questions` a
    WHERE EXTRACT(year FROM creation_date) > 2015
  ), UNNEST(tags) tag
  WHERE tag IN ('java', 'javascript', 'google-bigquery', 'firebase', 'php')
  AND answers.c > 0
)

SELECT tag
  , COUNT(*) questions
  , ROUND(AVG(minutes), 2) first_reply_avg_minutes
FROM question_answers
GROUP BY tag

我应该如何计算平均时间?

【问题讨论】:

    标签: sql google-bigquery


    【解决方案1】:

    2019 年更新:分享一些persisted public UDFs 怎么样?

    第一个,中位数:

    SELECT fhoffa.x.median([1,1,1,2,3,4,5,100,1000]) 
    
    3.0
    

    确实 - 在 Stack Overflow 上获得答案的平均时间超过 100 小时(>6000 分钟)似乎是错误的 - 并且很大程度上是由异常值驱动的。

    而不是做一个简单的AVG() 你可以得到:

    • 几何平均数:EXP(AVG(LOG(GREATEST(minutes,1))))
    • 去除异常值后的均值:AVG(q) FROM (SELECT q FROM QUANTILES(q, 100) LIMIT 80 OFFSET 2))
    • 中位数:all_minutes[OFFSET(CAST(ARRAY_LENGTH(all_minutes)/2 AS INT64))]

    如果您使用这些替代方案,结果会更有意义:

    正如您在此处看到的那样,在这种情况下,去除异常值会得到类似于几何平均值的结果 - 而中位数报告的数字甚至更低。使用哪一个?您的选择。

    WITH question_answers AS (
      SELECT * 
        , timestamp_diff(answers.first, creation_date, minute) minutes
      FROM (
        SELECT creation_date
          , (SELECT AS STRUCT MIN(creation_date) first, COUNT(*) c
             FROM `bigquery-public-data.stackoverflow.posts_answers` b
             WHERE a.id=b.parent_id
            ) answers
          , SPLIT(tags, '|') tags
        FROM `bigquery-public-data.stackoverflow.posts_questions` a
        WHERE EXTRACT(year FROM creation_date) > 2015
      ), UNNEST(tags) tag
      WHERE tag IN ('java', 'javascript', 'google-bigquery', 'firebase', 'php', 'sql', 'elasticsearch', 'apache-kafka', 'tensorflow')
      AND answers.c > 0
    )
    
    SELECT *  EXCEPT(qs, all_minutes)
      , (SELECT ROUND(AVG(q),2) FROM (SELECT q FROM UNNEST(qs) q ORDER BY q LIMIT 80 OFFSET 2)) avg_no_outliers 
      , all_minutes[OFFSET(CAST(ARRAY_LENGTH(all_minutes)/2 AS INT64)  )] median_minutes
    FROM (
      SELECT tag
        , COUNT(*) questions
        , ROUND(AVG(minutes), 2) avg_minutes
        , ROUND(EXP(AVG(LOG(GREATEST(minutes,1)))),2) first_reply_avg_minutes_geom
        , APPROX_QUANTILES(minutes, 100) qs
        , ARRAY_AGG(minutes IGNORE NULLS ORDER BY minutes) all_minutes
      FROM question_answers
      GROUP BY tag
    )
    
    ORDER BY 2 DESC
    

    奖金MEDIAN()UDF function from Elliott

    CREATE TEMP FUNCTION MEDIAN(arr ANY TYPE) AS ((
      SELECT
        IF(
          MOD(ARRAY_LENGTH(arr), 2) = 0,
          (arr[OFFSET(DIV(ARRAY_LENGTH(arr), 2) - 1)] + arr[OFFSET(DIV(ARRAY_LENGTH(arr), 2))]) / 2,
          arr[OFFSET(DIV(ARRAY_LENGTH(arr), 2))]
        )
      FROM (SELECT ARRAY_AGG(x ORDER BY x) AS arr FROM UNNEST(arr) AS x)
    ));
    

    【讨论】:

    • 您使用的中位数是离散的,不是插值/连续的,因此可能低于真正的中位数但可能不显着,可能在标准 SQL 中使用PERCENTILE_CONT(x, 0.5) OVER() AS median 更好,请参阅cloud.google.com/bigquery/docs/reference/standard-sql/…跨度>
    • 您排除异常值的方式假设是右偏分布;给定响应时间的公平假设类似于延迟建模/持续时间分析。它可能类似于广义 gamma 分布,但在考虑使用哪种集中趋势度量之前,值得将这些百分位数分箱并绘制图表以大致了解分布的样子。使用集中趋势(如中位数)和规模(如 MAD 而不是 StdDev)的稳健测量已经减少了异常值的影响。将异常值修剪到百分位数 3-83 会去除很多尾巴!
    • 请您解释一下为什么您选择在 3-83 之间修剪分位数,这不会影响结果以获得受分位数最低值影响的平均值吗?为什么不使用除第一个和最后一个分位数之外的全部范围?其中仅包括极值...
    猜你喜欢
    • 1970-01-01
    • 2020-03-07
    • 2022-11-03
    • 2010-11-01
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多