查询一段时间内的 DAU/MAU(每天)
Posted
技术标签:
【中文标题】查询一段时间内的 DAU/MAU(每天)【英文标题】:Querying DAU/MAU over time (daily) 【发布时间】:2014-06-30 16:04:24 【问题描述】:我有一个包含 user_id 和日期列的每日会话表。我想每天绘制 DAU/MAU(日活跃用户/月活跃用户)。例如:
Date MAU DAU DAU/MAU
2014-06-01 20,000 5,000 20%
2014-06-02 21,000 4,000 19%
2014-06-03 20,050 3,050 17%
... ... ... ...
计算每日活跃度很容易计算,但计算每月活跃度,例如在日期 30 天内登录的用户数量会导致问题。如果没有每天的左连接,这是如何实现的?
编辑:我正在使用 Postgres。
【问题讨论】:
你使用的是什么数据库,mysql 还是 Postgres? 【参考方案1】:假设您每天都有值,您可以使用子查询和range between
获得总计数:
with dau as (
select date, count(userid) as dau
from dailysessions ds
group by date
)
select date, dau,
sum(dau) over (order by date rows between -29 preceding and current row) as mau
from dau;
不幸的是,我认为您需要不同的用户,而不仅仅是用户数量。这使问题变得更加困难,尤其是因为 Postgres 不支持 count(distinct)
作为窗口函数。
我认为您必须为此进行某种自我加入。这是一种方法:
with dau as (
select date, count(distinct userid) as dau
from dailysessions ds
group by date
)
select date, dau,
(select count(distinct user_id)
from dailysessions ds
where ds.date between date - 29 * interval '1 day' and date
) as mau
from dau;
【讨论】:
当我运行此查询多天时,结果集中的mau
列不会改变,理想情况下它应该改变,因为mau
每天应该不同。有关如何解决此问题的任何指示?
@Patthebug 。 . .尝试使用示例数据和所需结果提出一个新问题。
这是新问题:***.com/questions/53839230/…
@GordonLinoff,像往常一样,非常优雅的解决方案,谢谢!
这个解决方案的前提忽略了MAU不是一个月DAU的总和。否则,如果您在一个月内每天都有同一个用户,您的 MAU 将为 30,而实际上应该为 1。【参考方案2】:
这个使用 COUNT DISTINCT 来获得滚动的 30 天 DAU/MAU:
(计算 reddit 在 BigQuery 中的用户参与度 - 但 SQL 标准足以用于其他数据库)
SELECT day, dau, mau, INTEGER(100*dau/mau) daumau
FROM (
SELECT day, EXACT_COUNT_DISTINCT(author) dau, FIRST(mau) mau
FROM (
SELECT DATE(SEC_TO_TIMESTAMP(created_utc)) day, author
FROM [fh-bigquery:reddit_comments.2015_09]
WHERE subreddit='AskReddit') a
JOIN (
SELECT stopday, EXACT_COUNT_DISTINCT(author) mau
FROM (SELECT created_utc, subreddit, author FROM [fh-bigquery:reddit_comments.2015_09], [fh-bigquery:reddit_comments.2015_08]) a
CROSS JOIN (
SELECT DATE(SEC_TO_TIMESTAMP(created_utc)) stopday
FROM [fh-bigquery:reddit_comments.2015_09]
GROUP BY 1
) b
WHERE subreddit='AskReddit'
AND SEC_TO_TIMESTAMP(created_utc) BETWEEN DATE_ADD(stopday, -30, 'day') AND TIMESTAMP(stopday)
GROUP BY 1
) b
ON a.day=b.stopday
GROUP BY 1
)
ORDER BY 1
我在How to calculate DAU/MAU with BigQuery (engagement)走得更远
【讨论】:
【参考方案3】:我已经在my blog 上写过这个。
如您所见,DAU 很简单。您可以通过首先创建一个带有布尔值的视图来解决 MAU,当用户激活和取消激活时,如下所示:
CREATE OR REPLACE VIEW "vw_login" AS
SELECT *
, LEAST (LEAD("date") OVER w, "date" + 30) AS "activeExpiry"
, CASE WHEN LAG("date") OVER w IS NULL THEN true ELSE false AS "activated"
, CASE
WHEN LEAD("date") OVER w IS NULL THEN true
WHEN LEAD("date") OVER w - "date" > 30 THEN true
ELSE false
END AS "churned"
, CASE
WHEN LAG("date") OVER w IS NULL THEN false
WHEN "date" - LAG("date") OVER w <= 30 THEN false
WHEN row_number() OVER w > 1 THEN true
ELSE false
END AS "resurrected"
FROM "login"
WINDOW w AS (PARTITION BY "user_id" ORDER BY "date")
这会在每个用户每天活跃、流失和重新激活时创建布尔值。
然后每天做同样的汇总:
CREATE OR REPLACE VIEW "vw_activity" AS
SELECT
SUM("activated"::int) "activated"
, SUM("churned"::int) "churned"
, SUM("resurrected"::int) "resurrected"
, "date"
FROM "vw_login"
GROUP BY "date"
;
最后通过计算列的累积总和来计算活动 MAU 的运行总数。您需要加入 vw_activity 两次,因为第二次加入到用户变为非活动的那一天(即自上次登录后 30 天)。
为了确保所有日期都存在于您的数据集中,我添加了一个日期系列。您也可以不使用它,但您可能会在数据集中跳过几天。
SELECT
d."date"
, SUM(COALESCE(a.activated::int,0)
- COALESCE(a2.churned::int,0)
+ COALESCE(a.resurrected::int,0)) OVER w
, d."date", a."activated", a2."churned", a."resurrected" FROM
generate_series('2010-01-01'::date, CURRENT_DATE, '1 day'::interval) d
LEFT OUTER JOIN vw_activity a ON d."date" = a."date"
LEFT OUTER JOIN vw_activity a2 ON d."date" = (a2."date" + INTERVAL '30 days')::date
WINDOW w AS (ORDER BY d."date") ORDER BY d."date";
您当然可以在单个查询中执行此操作,但这有助于更好地理解结构。
【讨论】:
【参考方案4】:您没有向我们展示您的完整表定义,但可能是这样的:
select date,
count(*) over (partition by date_trunc('day', date) order by date) as dau,
count(*) over (partition by date_trunc('month', date) order by date) as mau
from sessions
order by date;
要在不重复窗口函数的情况下获得百分比,只需将其包装在派生表中即可:
select date,
dau,
mau,
dau::numeric / (case when mau = 0 then null else mau end) as pct
from (
select date,
count(*) over (partition by date_trunc('day', date) order by date) as dau,
count(*) over (partition by date_trunc('month', date) order by date) as mau
from sessions
) t
order by date;
这是一个示例输出:
postgres=> 从会话中选择 *; 会话日期 |用户身份 --------------+---------- 2014-05-01 | 1 2014-05-01 | 2 2014-05-01 | 3 2014-05-02 | 1 2014-05-02 | 2 2014-05-02 | 3 2014-05-02 | 4 2014-05-02 | 5 2014-06-01 | 1 2014-06-01 | 2 2014-06-01 | 3 2014-06-02 | 1 2014-06-02 | 2 2014-06-02 | 3 2014-06-02 | 4 2014-06-03 | 1 2014-06-03 | 2 2014-06-03 | 3 2014-06-03 | 4 2014-06-03 | 5 (20 行) postgres=> 选择 session_date, postgres-> dau, postgres-> 茂, postgres-> round(dau::numeric / (当 mau = 0 然后为 null 否则 mau end),2) as pct postgres-> 从 ( postgres(> 选择 session_date, postgres(> count(*) over (partition by date_trunc('day', session_date) order by session_date) 作为 dau, postgres(> count(*) over (partition by date_trunc('month', session_date) order by session_date) as mau postgres(> 来自会话 postgres(>) t postgres-> 按 session_date 排序; 会话日期 |头|茂 |百分比 --------------+------+------+------ 2014-05-01 | 3 | 3 | 1.00 2014-05-01 | 3 | 3 | 1.00 2014-05-01 | 3 | 3 | 1.00 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-06-01 | 3 | 3 | 1.00 2014-06-01 | 3 | 3 | 1.00 2014-06-01 | 3 | 3 | 1.00 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 (20 行) postgres=>【讨论】:
看起来不错 - 问题:MAU 是日历月还是每天的前一个月?理想情况是当天的前一个月。 我已经运行了这个查询,但它不起作用。 MAU从月初的0增加到月底的累计用户总数。此外,包装器需要按日期、dau、mau 分组。 @DavidBailey:那么您需要提供更多详细信息——尤其是表结构和更多示例数据。不,包装器不需要分组,因为我使用的是生成累积计数的窗口函数。不幸的是,SQL Fiddle 现在不起作用,因为那时我会提供一个“实时”示例。我已经添加了一个 psql 会话的脚本,以向您展示我的示例表。 您假设的数据结构是正确的 :) 在实时示例中,每月活跃用户为 5 月 1 日为 3,5 月 2 日为 8,6 月 1 日为 3。目前尚不清楚这代表什么...... 好吧,sessions
表中有 5 月 1 日和 5 月 2 日 5 日的三个条目。所以结果显示 5 月 1 日的 DAU 为 3,5 月 2 日的 DAU 为 5,但 MAU 进行了累积计数,这意味着 5 月 2 日有 8 个会话。以上是关于查询一段时间内的 DAU/MAU(每天)的主要内容,如果未能解决你的问题,请参考以下文章
REDIS05_HyperLogLog的概述基本命令UVPVDAUMAU首页UV如何进行统计处理