如何计算bigquery中的每月留存用户?

How to count monthly retention user in bigquery?

我有如下原始数据。每行是用户的一笔交易记录,以及他们进行交易的月份


我想要的是计算一个月内下单的用户数和上个月的重复用户数(RETENTION),然后我可以知道有多少用户是重复用户。

期望的结果应该是这样的

如何在 bigquery 中实现?

实现它的一种方法是通过具有相同 table 和 1 个月延迟的自加入。这样,我们将 user&month 组合与 user&previous-month 进行匹配,以查看它是否是回头客。例如,使用 2M 行 public table bigquery-public-data.hacker_news.stories 和特定用户:

请注意,对于 2014-02-01prev_monthnull(我们使用 LEFT OUTER JOIN)该用户在 2014-01-01 期间 活跃。我们正在加入作者并滞后几个月:

FROM authors AS a 
LEFT OUTER JOIN authors AS b
ON a.author = b.author
AND a.month = DATE_ADD(b.month, INTERVAL 1 MONTH)

然后如果上个月不为空,我们将用户计为重复用户:

COUNT(a.author) AS num_users,
COUNTIF(b.month IS NOT NULL) AS num_returning_users

请注意,我们在这里不使用 DISTINCT,因为我们在将 orders 定义为 CTE 时已经按作者和月份组合分组。对于其他示例,您可能需要考虑到这一点。

完整查询:

WITH
  authors AS (
  SELECT
    author,
    DATE_TRUNC(DATE(time_ts), MONTH) AS month
  FROM
    `bigquery-public-data.hacker_news.stories`
  WHERE
    author IS NOT NULL
  GROUP BY 1,2)

SELECT
  *,
  ROUND(100*SAFE_DIVIDE(num_returning_users,
      num_users),2) AS retention
FROM (
  SELECT
    a.month,
    COUNT(a.author) AS num_users,
    COUNTIF(b.month IS NOT NULL) AS num_returning_users
  FROM
    authors AS a
  LEFT OUTER JOIN
    authors AS b
  ON
    a.author = b.author
    AND a.month = DATE_ADD(b.month, INTERVAL 1 MONTH)
  GROUP BY 1
  ORDER BY 1
  LIMIT 100)

和结果片段:

哪些是正确的结果,即 2007-03-01:

性能并不太花哨,但在这种情况下,我们仅选择聚合数据所需的字段,因此扫描数据较少且执行时间不会太长(~5 秒)。

另一种方法是在 COUNTIF() 中使用 EXISTS() 而不是连接 但对我来说需要更长的时间(~7s)。 Query

如果您只是查看上个月,请执行以下操作:

  • 将月份转换为数字。
  • 在 user/month 级别聚合数据。

那么你可以直接使用lag():

select month,
       count(*) as num_users,
       countif(prev_month_int = month_int - 1) as prev_num_users,
       countif(prev_month_int = month_int - 1) / count(*) as repeat_rate
from (select mu.*,
             lag(month_int) over (partition by userid order by month_int) as prev_month_int
      from (select month, userid, count(*) as num_orders,
                   cast(split(month, '-')[ordinal(1)] as int64) * 12 + cast(split(month, '-')[ordinal(2)] as int64) as month_int
            from t
            group by month, userid
           ) mu
     ) mu
group by month;