![e6fa6c5d3469fe7c43a5527b640af9e4.png](https://i-blog.csdnimg.cn/blog_migrate/7b8c6a72ecad0c03984350525c60d6b8.jpeg)
目录:
- 项目介绍
- 定义阐述
- 分析流程
- 总结展示
一、项目介绍
背景:
当我们在做用户分析时,会遇到这样的一个问题,一个用户使用APP时,会留下一连串的使用数据,可能是一月份的购买了1次,二月份购买了2次,三四月没有购买,五月份又购买了。也就是对于用户来说他的数据是一个时间面数据,而且不同用户的时间面是不相同的,开始时间经历时间都不一样,而如果我们分析的时候不考虑到这个因素而直接进行分析,显然是不够合理的,因为新用户和老用户经历的产品运营情况是不一样的。
那我们应该如何处理呢,这个时候就有一种分析方法,可以帮助我们在时间轴上对齐用户,这就是群组分析。
二、定义阐述
(1)群组分析:
用户在产品使用中都有一个用户行为流程,不同时期的用户表现情况可能不一样,群组分析的主要目的是分析相似群体随时间的变化,核心就是对比、分析不同时间群组的用户,在相同周期下的行为差异, 所以也称同期群分析。
(2)群组分析的作用:
1. 对处于相同生命周期阶段的用户进行垂直分析(横向比较),从而比较得出相似群体随时间的变化
2. 通过比较不同的同期群,可以从总体上看到,应用的表现是否越来越好了。从而验证产品改进是否取得了效果。
三、分析流程
import pandas as pd
import numpy as np
import sqlalchemy
# 读取数据
engine = sqlalchemy.create_engine('mysql+pymysql://froghd:Froghd@123@localhost:3306/froghd')
sql_cmd = "select * from groups_data"
# 执行sql语句,获取数据
df = pd.read_sql(sql=sql_cmd, con=engine)
#数据显示有用户的OrederId、OrderDate、UserId、TotalCharges(我们只会用到这四个),
#其他的字段没有用
df #一共118条信息,8列
![c1df60c67c25a96c50d90f7a9ec77165.png](https://i-blog.csdnimg.cn/blog_migrate/a5365e2797b5ad02d193930082b906ca.jpeg)
#重命名
df.rename(columns={
"orderid":"订单编号",
"orderdate":"下单日",
"userid":"用户id",
"totalcharges":"销售额"
},inplace=True)
df.head()
![635ea5ecc56ad9dbce898222586898a7.png](https://i-blog.csdnimg.cn/blog_migrate/4024a6d10c4f22fb723f7f6bf430c247.png)
# 生成一个新字段,用户订单月份,只保留下单日的月份信息
df["订单月份"] = df['下单日'].apply(lambda x:x.strftime("%Y-%m"))
df.head()
![210fbe2913d0e5569ddc961bce0248b4.png](https://i-blog.csdnimg.cn/blog_migrate/19b9e09eb2001c23f9b1f58e43b5aa25.png)
# 设置userid为索引
df.set_index("用户id",inplace=True)
# 这里的level=0表示第一层索引即userid,并且每次分组之后都会形成很多个dataframe
# 按照每个用户的订单的最早时期,生成用户群组
df["首次购买日"]=df.groupby('用户id')["下单日"].min().apply(lambda x:x.strftime("%Y-%m"))
df.reset_index(inplace=True)
df.head()
![ca2093abd142cc142d3147003642fc3b.png](https://i-blog.csdnimg.cn/blog_migrate/1a22703aee7a4a99aeea385cb04cade4.png)
拆解:
# 设置userid为索引
df.set_index("用户id",inplace=True)
df.head()
![edc6994f42d7423ed56ea812d74fcb80.png](https://i-blog.csdnimg.cn/blog_migrate/e6d1baf72ae099a8b48074c99d767ceb.png)
#找出每个用户的最早下单日
df.groupby('用户id')["下单日"].min() #也可以写成:df.groupby(level=0)["orderdate"].min()
![e4da81a8917344eb68a383a786cd13d4.png](https://i-blog.csdnimg.cn/blog_migrate/0b7b475f013bc4d8a3cff528585aff29.png)
#新增字段 最早消费日,同样只保留月份
df["首次购买日"]=df.groupby('用户id')["下单日"].min().apply(lambda x:x.strftime("%Y-%m"))
df.reset_index(inplace=True) #重置索引
df.head()
![45170d9d163ff9c16bceccf3d3c48f98.png](https://i-blog.csdnimg.cn/blog_migrate/829650c5fd0c954322d166b19ccda86e.png)
拆解结束
# 根据订单月份和首次消费日进行分组
grouped = df.groupby(["首次购买日","订单月份"])
# 求每个用户群下每一个月份的用户数量、订单数量、购买金额
cohorts = grouped.agg({"用户id":pd.Series.nunique,
"订单编号":pd.Series.nunique,
"销售额":np.sum})
# 重命名
cohorts.rename(columns={"用户id":"用户数量",
"订单编号":"销售量"},inplace=True)
cohorts.head()
拆解:
# 根据订单月份和首次消费日进行分组
grouped=df.groupby(['首次购买日','订单月份'])
# 求每个用户最早消费日下 每一个月份的用户数量、订单数量、购买金额
cohorts = grouped.agg({"用户id":pd.Series.nunique,
"订单编号":pd.Series.nunique,
"销售额":np.sum})
# 重命名
cohorts.rename(columns={"用户id":"用户数量",
"订单编号":"销售量"},inplace=True)
cohorts
![aa8f748c1ac68f9927eb8fee3b84d5ee.png](https://i-blog.csdnimg.cn/blog_migrate/c34f2a5ca79d05a97c84e6a9bf0a0436.png)
拆解结束
# 把每个群组继续购买的日期字段进行改变
def cohort_period(df): #定义 订单编码
# 给首次购买日期进行编号,第二次购买为2,第三次购买为3
df["首次购买日编号"] = np.arange(len(df)) + 1
return df
# 注意的是apply后面传入的是一个个dataframe
cohorts = cohorts.groupby(level=0).apply(cohort_period)
cohorts.head()
![c2b02a77e7cabab77afc8076e2c1abcd.png](https://i-blog.csdnimg.cn/blog_migrate/dc81afa09c57ff240db96e32852960c9.png)
# 得到每个群组的用户量
# 重新设置索引
cohorts.reset_index(inplace=True)
cohorts
![7f447070c2aa70b898daa537f4246f39.png](https://i-blog.csdnimg.cn/blog_migrate/113a39805d67845e28ea2ebce4291bd8.png)
#重新设置索引
cohorts.set_index(["首次购买日","首次购买日编号"],inplace=True)
cohorts
![71109d9f06981d0162706a97c8b39c11.png](https://i-blog.csdnimg.cn/blog_migrate/a841e543d1f530bfd9bb69c61dcf9e20.png)
# 得到每个群组的用户量,就是第一天的用户数据量,用作留存率的分母
cohort_group_size = cohorts["用户数量"].groupby(level=0).first()
cohort_group_size.head()
![76ecd8fdb4243ccaf63446797ebb9321.png](https://i-blog.csdnimg.cn/blog_migrate/a9fda02629cf412d47ab0ad239dafb5e.png)
# 计算每个群组的留存
# unstack 是把index转化为column,https://www.jianshu.com/p/5ab1019836c9
cohorts["用户数量"].unstack(0).head()
![1fd1fa4c7d1b2e0a76f26dc9bcf54e3d.png](https://i-blog.csdnimg.cn/blog_migrate/5a7455aebf5bc668f1c2e34d11792dae.png)
# 计算留存 divide除
user_retention = cohorts["用户数量"].unstack(0).divide(cohort_group_size,axis=1)
user_retention.head()
![9f7ba62b0da62673c06825fc88d3d74f.png](https://i-blog.csdnimg.cn/blog_migrate/837446f4ac1b79e8f1f75220f8cbd5a8.png)
四、总结展示
# 折线图展示
import matplotlib.pyplot as plt
import matplotlib as mpl
pd.set_option("max_columns",50)
mpl.rcParams["lines.linewidth"] = 2
%matplotlib inline
user_retention[["2009-01","2009-02","2009-03"]].plot(figsize=(5,3))
plt.title("user tetention")
plt.xticks(np.arange(1,12.1,1))
plt.xlim(1,12)
plt.ylabel("%of cohort")
![2e05dbe554697491edf75c0ec2924bd2.png](https://i-blog.csdnimg.cn/blog_migrate/ac0da234e77994dedae06542e6b324b1.png)
# 热力图展示
import seaborn as sns
sns.set(style="white")
plt.figure(figsize=(8,4))
plt.title("co:user retention")
sns.heatmap(user_retention.T,mask=user_retention.T.isnull(),annot=True,fmt=".0%")
![700eba64cdec616e18671a5ed3ba5126.png](https://i-blog.csdnimg.cn/blog_migrate/e4e0281aff8da5228622ecd249a33c94.png)