Как рассчитать ежемесячное удержание пользователя в bigquery? - PullRequest
0 голосов
/ 12 января 2020

У меня есть необработанные данные, как показано ниже. В каждой строке указывается запись транзакции пользователя и месяц, когда они совершили транзакцию. enter image description here

Я хочу рассчитать количество пользователей, сделавших заказ за месяц, и количество повторных пользователей (RETENTION) за последний месяц, тогда я могу узнать, сколько% пользователей является повторным пользователем.

Желаемый результат должен выглядеть следующим образом enter image description here

Как я могу сделать это в bigquery?

Ответы [ 2 ]

1 голос
/ 12 января 2020

Один из способов сделать это - это самостоятельное соединение с той же таблицей и задержкой в ​​1 месяц. Таким образом, мы сопоставляем комбинации пользователя и месяца с пользователем и предыдущим месяцем, чтобы определить, является ли он возвращающимся пользователем. Например, используя строку 2M publi c table bigquery-public-data.hacker_news.stories и конкретного пользователя:

enter image description here

Обратите внимание, что prev_month равно null (мы использовали LEFT OUTER JOIN) для 2014-02-01 , поскольку пользователь был не активным в течение 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)

и фрагмент результатов:

enter image description here

, которые являются правильными результатами, т. Е. Для 2007-03-01:

enter image description here

Производительность не слишком велика, но в этом случае мы выбираем только поля, необходимые для агрегированных данных, поэтому отсканированные данные невелики, а время выполнения не слишком велико (~ 5 с).

Альтернативой является использование EXISTS() внутри COUNTIF() вместо объединения, но у меня это занимает больше времени (~ 7 с). Запрос

0 голосов
/ 12 января 2020

Если вы просто смотрите на предыдущий месяц, выполните следующие действия:

  • Преобразование месяцев в числа.
  • Агрегирование данных на уровне пользователя / месяца.

Тогда вы можете просто использовать 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;
...