SQL-计算留存率cohort

2023-11-16

目录

1、留存率cohort介绍及其业务价值

2、计算思路

3、实操

3.1、日对日留存cohort

3.2、周对周留存cohort

3.3、月对月留存cohort


1、留存率cohort介绍及其业务价值

留存率cohort也叫做同期群留存分析,将同一时间范围内的用户分为一组,计算这批人在之后的留存情况,一般结果的样式如下(这里省略数值,):

新增留存

日期 新增客户数 次1日留存 次2日留存 次3日留存 ....
20220101 1000 50% 40% 39%
20220102 981 59% 50%
20220103 819 61%

老客留存(数据略)

日期 老客户数 次1留存 次2留存 次3留存 ....

根据留存cohort图可以得到两方面的信息

1、从横向看,可以得到一批用户在长时间范围内的衰减情况

以上述示例来看:用户在新增后的前两天是快速衰减的(第一天之后有50%的用户不再来了,第三天又损失了40%),到第四天会逐步稳定下来,可见前三天是产品抓住用户的关键窗口期。

2、从纵向看,可以看出不同时间周期来的用户,他们的留存情况是否有改善

以上述示例来看:用户的次日留存在逐步改善(从50%-59%-61%)这可能是产品上做了优化调整,也可能是投放侧在用户的选择上更精准了(对应的用户新增数量也在减少)

2、计算思路

如果通过SQL得到留存cohort数据呢?

Step1:计算留存率分母数据,即每日的用户量

通常来说,新老用户的留存率差异是非常大的,所以需要进行区分。有现成的用户标签是最好的,如果没有可以用窗口函数计算用户是第几次来,第一次来的用户就是当日新增用户。

step2:计算留存率分子

这里计算的间隔时间可以是日,周,月各种时间粒度

关于SQL时间计算的相关知识点可以参考:

SQL-时间处理汇总_格勒王的博客-CSDN博客常见日期提取,日期格式转换,日期加减计算等时间处理方法汇总https://blog.csdn.net/weixin_47198715/article/details/130823960?spm=1001.2014.3001.5502

日期 间隔时间 留存客户数
20220101 1 500
20220101 2 400
20220101 3 390
20220102 ... ...

step3:将上面两步骤数据合并

3、实操

3.1、日对日留存cohort

with tmp as 
(select 
  date,--yyyy-mm-dd格式,如果是yyyymmdd需要转换
  user_id
from table_name
where is_new=1#筛选新用户
)


select 
a.date,
gap,
`客户数`,
`留存客户数`,
round(`留存客户数`/`客户数`,2) as `留存率`
from 
 (--计算留存率分母
    SELECT DATE,COUNT(DISTINCT user_id) AS `客户数`
  FROM  tmp
  GROUP BY DATE)a--计算每日新增客户数
LEFT JOIN 
 (--计算留存率分子
  SELECT
  begin_date,
  gap,
  count(distinct user_id) as `留存客户数`
  from 
    (SELECT
    a.user_id,
    a.date as begin_date,
    b.date as stay_date,
    datediff(b.date,a.date)as gap--计算间隔天数
    from 
      (select *
      from tmp)a
    left join 
      (select *
      from tmp)b
    on a.user_id=b.user_id)a
    where gap>=1--筛选间隔天数大于等于1的记录
  group by begin_date,gap)b
on a.date=b.begin_date

3.2、周对周留存cohort

和日对日留存的区别在于,日对日留存只需要计算两次日期的间隔天数即可

而周对周的留存需要计算两个日期的间隔周

有两种方法可以实现周对周留存率的计算

1、先把每天的日期对应到当周的周一,再计算周一之间的日期差除以7

2、使用weekofyear函数,但是这里如果涉及到跨年,需要在处理的时候注意

weekofyear('yyyy-mm-dd')

两个日期周数相差:公式:(52-新增周)+(留存日年份-新增日年份)*留存日周数

如2022年第51周新增的用户,在2023年第1周的留存,计算两个日期的周数差:

(52-51)+(2023-2022)*1=2(也就是次2周留存)

我们以第一种方法示例


with tmp as 
(select 
  date,--yyyy-mm-dd格式,如果是yyyymmdd需要转换
  date_add(DATE,2-if(dayofweek(DATE)=1,8,dayofweek(DATE))) as monday,
  user_id
from table_name
where is_new=1#筛选新用户
)


select 
a.monday,
gap,
`客户数`,
`留存客户数`,
round(`留存客户数`/`客户数`,2) as `留存率`
from 
 (--计算留存率分母
    SELECT monday,COUNT(DISTINCT user_id) AS `客户数`
  FROM  tmp
  GROUP BY monday)a--计算每日新增客户数
LEFT JOIN 
 (--计算留存率分子
  SELECT
  begin_date,
  gap,
  count(distinct user_id) as `留存客户数`
  from 
    (SELECT
    a.user_id,
    a.monday as begin_week,
    b.monday as stay_week,
    datediff(b.monday,a.monday)/7 as gap--计算间隔周数
    from 
      (select *
      from tmp)a
    left join 
      (select *
      from tmp)b
    on a.user_id=b.user_id)a
    where gap>=1--筛选间隔天数大于等于1的记录
  group by begin_date,gap)b
on a.monday=b.begin_week

3.3、月对月留存cohort

计算两个日期之间的月份差可以使用month_bvetween(date1,date2)函数,但是这里计算出来的月份差非整数,如20230301和20230430之间的月份差也是1

在计算月留存时,我们需要对其向下取整,即floor(month_bvetween(date1,date2))

with tmp as 
(select 
  date,--yyyy-mm-dd格式,如果是yyyymmdd需要转换
  substr(DATE,1,7) AS month,
  user_id
from table_name
where is_new=1#筛选新用户
)


select 
a.month,
gap,
`客户数`,
stay_num
from 
 (--计算留存率分母
    SELECT month,COUNT(DISTINCT user_id) AS `客户数`
  FROM  tmp
  GROUP BY month)a--计算每日新增客户数
LEFT JOIN 
 (--计算留存率分子
  SELECT
  begin_date,
  gap,
  count(distinct user_id) as `留存客户数`
  from 
    (SELECT
    a.user_id,
    a.month as begin_month,
    floor(months_between(b.date,a.date))as gap--计算间隔天数
    from 
      (select *
      from tmp)a
    left join 
      (select *
      from tmp)b
    on a.user_id=b.user_id)a
    where gap>=1--筛选间隔天数大于等于1的记录
  group by begin_month,gap)b
on a.month=b.begin_month

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SQL-计算留存率cohort 的相关文章

  • 模式更新后 jOOQ 生成的类的运行时验证?

    我用org jooq util DefaultGenerator在构建过程中生成 jOOQ 类来表示我的数据库模式 当应用程序运行时 架构预计会在应用程序不知情的情况下发生更改 此类更改可能与已生成的代码兼容 也可能不兼容 如何在运行时检测
  • 插入多行并返回主键时 Sqlalchemy 的奇怪行为

    插入多行并返回主键时 我注意到一些奇怪的事情 如果我在 isert 查询中添加使用参数值 我会得到预期的行为 但是当将值传递给游标时 不会返回任何内容 这可能是一个错误还是我误解了什么 我的sqlachemy版本是0 9 4 下面如何重现错
  • Oracle 中的 SQL 调优 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何文章 链接可以让我找到 SQL 调优 Oracle 的示例 如果能用例子来解释那就太好了 我需
  • 如何有效地从 DB2 表中删除所有行

    我有一个大约有 50 万行的表 我想删除所有行 如果我做简单的delete from tbl 事务日志已满 我不关心这种情况下的事务 无论如何我都不想回滚 我可以删除许多事务中的行 但是有更好的方法吗 如何有效地从 DB2 中的表中删除所有
  • 使用 MS Access 获取行的第一个实例

    EDITED 我有这个查询 我想SELECT表中记录的第一个实例petTable SELECT id pet ID FIRST petName First Description FROM petTable GROUP BY pet ID
  • SQL参数化查询不显示结果

    我的 DataAcess 类中有以下函数 但它没有显示任何结果 我的代码如下 public List
  • 如何将可视选择的文本通过管道传输到 UNIX 命令并将输出附加到 Vim 中的当前缓冲区

    使用 Vim 我尝试将在可视模式下选择的文本通过管道传输到 UNIX 命令 并将输出附加到当前文件的末尾 例如 假设我们有一个 SQL 命令 例如 SELECT FROM mytable 我想做如下的事情
  • 将 UUID 存储为 base64 字符串

    我一直在尝试使用 UUID 作为数据库键 我希望占用尽可能少的字节数 同时仍然保持 UUID 表示形式的可读性 我认为我已经使用 base64 将其减少到 22 个字节 并删除了一些尾随的 这些 对于我的目的来说似乎没有必要存储 这种方法有
  • Oracle:使用SQL或PL/SQL查找动态SQL中的错误位置

    如何在 PL SQL 或 SQL 中找到动态 SQL 语句中的错误位置 从 SQL Plus 中 我看到了错误的位置 例如 无效的 SQL DML 语句 SYS orcl gt SELECT 2 X 3 FROM 4 TABLEX 5 TA
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • sql server 按组排名

    问题看似简单 但我却无法理解 这是针对 sql 服务器的 what I have in a table What I need as a output cksum id cksum id 2162514679 204 2162514679
  • 单独的逗号分隔值并存储在sql server的表中

    我有一个存储过程 它将逗号分隔的值作为输入 我需要将其分开并需要将其作为单独的行存储在表中 令 SP 的输入为 Rule ID ListType ID Values 1 2 319 400 521 8465 2013 我需要将它存储在一个名
  • 初级SQL部分:避免重复表达式

    我对 SQL 完全陌生 但我们可以说StackExchange 数据浏览器 https data stackexchange com 我只想按信誉列出前 15 位用户 我写了这样的内容 SELECT TOP 15 DisplayName I
  • 如何 md5 所有列(无论类型如何)

    我想创建一个 sql 查询 或 plpgsql 它将 md5 所有给定的行 无论类型如何 但是 在下面 如果 1 为空 则哈希为空 UPDATE thetable SET hash md5 accountid accounttype cre
  • 如何将表中不存在但原始SQL中存在的实体字段设置为别名?

    假设我们有一个这样的查询 SELECT CUSTOM EXPRESSION as virtualfield FROM users 用户的实体本身具有 虚拟字段 但映射注释没有 因为表没有该字段 假设它作为原始 SQL 执行 我们如何使用上面
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 执行带有 EXCEPTION 的 PostgreSQL 查询会导致两条不同的错误消息

    我有一个 PostgreSQL 查询 其中包含事务和列重复时的异常 BEGIN ALTER TABLE public cars ADD COLUMN top speed text EXCEPTION WHEN duplicate colum
  • 使用间隔阈值对不同的连续时间戳记录进行分组

    我有一系列间歇性间隔的带有时间戳的 GPS 坐标 我正在使用 PostGIS 将它们渲染到地图画布上 为了渲染它们 需要使用 PostGIS 中的 ST MakeLine 聚合函数将点聚合成线 从而在地图上留下 GPS 数据丢失的间隙 数据
  • SQL不允许表中有重复记录

    如何使其不添加重复项 我想让它通过 ID 之外的所有其他列进行检查 我希望这个无效 ID col1 col2 col3 1 first middle last ID col1 col2 col3 2 first middle last 我希
  • 在 Oracle 行的多个列上使用透视

    我在 Oracle 表中有以下示例数据 tab1 我正在尝试将行转换为列 我知道如何在某一列上使用 Oracle 数据透视表 但是否可以将其应用于多个列 样本数据 Type weight height A 50 10 A 60 12 B 4

随机推荐

  • tronweb 波场导入助记词生成地址

    创建助记词 let tronWeb require tronweb let mne tronWeb createRandom 导入助记词 let tronWalletImport tronWeb fromMnemonic m m 44 19
  • echarts laben 饼图指示线条+文字颜色继承

    注意 label并不支持HTML片段 版本5 30 label show true alignTo edge position outside formatter name b n time c 小时 formatter b b n cst
  • Vue3挂载全局方法及组件中如何使用

    文章目录 前言 一 在mian ts mian js 中配置全局变量 1 如何封装 二 如何调用 1 template中调用 2 在script标签中如何拿到 前言 在Vue3项目中 需要频繁使用某一个方法 配置到全局感觉会方便很多 例如
  • Nodejs中electron IpcMain & IpcRenderer通信

    1 事情起因 原因是本人在使用nedb数据库进行数据插入的时候 发现莫名其妙多出了很多很多的数据 很容易想到 那么就是插入语句被调用了N多次 并且每一次插入的数据都是之前已经插入过的数据 比如说第一次插入的是 id 1 name xx 第二
  • MongoDB 聚合

    MongoDB 聚合有什么用 实际项目中 我们经常需要将多个文档甚至是多个集合汇总到一起计算分析 比如求和 取最大值 并返回计算后的结果 这个过程被称为 聚合操作 根据官方文档介绍 我们可以使用聚合操作来 将来自多个文档的值组合在一起 对集
  • 我赌你不懂系列:char占几个字节

    前言 我百度搜索 char占几个字节 得到下面的答案 image png char用于C或C 中定义字符型变量 只占一个字节 取值范围为 128 127 27 27 1 char类型占1字节 就是8位 所能存储的正整数是 0111 1111
  • linux status文件,linux的 functions之status函数详解

    检测一个二进制可运行程序是否运行 使用方法 status p pidfile program status local base pid pid file Test syntax 测试调用该函数时的参数格式 if 0 then echo U
  • Mpvue-echarts发布自定义事件

    引入库 npm install mpvue echarts 直接上源码
  • MyBatis学习-getMapper接口绑定方案及多参数传递

    一 采用接口绑定方案的作用 创建一个接口后 MyBatis框架将mapper xml通过反射机制生成接口的实现类 通过调用接口对象就可以获取mapper xml中编写的sql 方便sql语句的调用 方便多个参数的传递 在未使用接口绑定方案前
  • Layui实现登录注册共用页面

    正文 创建一个Spring Boot项目 勾选Web Thymeleaf即可 pom文件中导入图片验证码的依赖
  • shiro通过注解方式自定义控制接口无需认证访问的解决过程

    1 需求背景 用过Shiro的小伙伴都知道 shiro提供两种权限控制方式 通过过滤器或注解 我们项目是springboot vue前后分离项目 后台对于权限控制一直使用的是过滤器的方式 并且还有自定义的过滤器 大概如下 Bean shir
  • 博途V16:找不到step7basic许可,解决办法

    1 启动服务项 打开服务 找到Automation License Manager Service服务项 如果未启动 启动该项 并 启动方式改为自动 这应该就行了 打开博途试试 2 再次装在秘钥 至于是不是这样装载我也不确定
  • Temporary failure in name resolution

    File usr local lib python2 7 dist packages pip 9 0 1 py2 7 egg pip vendor cachecontrol adapter py line 47 in send resp s
  • 快速解决Row size too large. The maximum row size for the used table type, not counting BLOBs, is 8126问题

    42000 1118 Row size too large The maximum row size for the used table type not counting BLOBs is 8126 This includes stor
  • 前端工作过程遇到的问题总结(九)

    目录 JS中every 和some 的用法 置换元素和非置换元素 ES6的map数据类型转换 关于原生方法增加参数的问题 element增加参数 element ui中的表格el table滚动条样式修改 vue设置路由title 实现在v
  • Markdown表格合并单元格

    Markdown表格合并单元格 Markdown表格语法介绍 基本语法 属性1 属性2 属性3 value1 value1 value1 value2 value2 value2 实例 实现 属性名 含义 常用属性值 border 设置表格
  • 字节跳动面试官总结的SQL调优教程,让你调优就跟吃饭喝水一样简单,教你抓住SQL的本质!

    前言 SOL 优化并不简单 做好 SOL 优化需要掌握数据库体系结构 表和索引设计 高效 SOL法 高级 SOL 语法 多种优化工具等知识 甚至还得分析业务特点 以及了解优化器的缺点 只有建立 SOL 优化方法论体系 才能够迅速找到最适合的
  • Maven引入log4j日志

    1 1依赖导入
  • 6.4行为型模式——责任链模式

    在现实生活中 常常会出现这样的事例 一个请求有多个对象可以处理 但每个对象的处理条件或权限不同 例如 公司员工请假 可批假的领导有部门负责人 副总经理 总经理等 但每个领导能批准的天数不同 员工必须根据自己要请假的天数去找不同的领导签名 也
  • SQL-计算留存率cohort

    目录 1 留存率cohort介绍及其业务价值 2 计算思路 3 实操 3 1 日对日留存cohort 3 2 周对周留存cohort 3 3 月对月留存cohort 1 留存率cohort介绍及其业务价值 留存率cohort也叫做同期群留存