ermutuxia

2021-02-13   阅读量: 1941

用python进行用户留存率的查询计算

扫码加入数据分析学习群

学生:mysql用户留存率的查询 我想看当日注册的下单新用户 在次日的留存率 三日的留存率 七日的留存率

学生:急死了 都找不到人解决 明天又要给老板交作业 好烦

老师:数据有哪些字段

学生:统计日期 用户ID 提交资料日期 审核通过日期 销量

学生提交的数据我经过脱敏处理上传到云盘上了,有需要的同学可以下载

链接:https://pan.baidu.com/s/1dcKvc-bxa_qH5yjUt61UtQ

提取码:7cmh

复制这段内容后打开百度网盘手机App,操作更方便哦

老师:告诉我计算留存率的方法,比如1日留存率是不是在注册后第一日有消费则是留存

学生:次日留存就是 注册当日有消费 并且次日有消费

老师:好的,必须用mysql吗?我mysql不怎么样。

老师:我看这个数据量比较小,能不能放到python里面,加工后返给数据库。

学生:不用mysql也可以

老师:好,我想下怎么计算

学生:我现在很着急想要这个 好烦 还在加班

老师:好,必须完成任务

学生:你可以把审核通过日期为-1的数据清洗一下 -1表示审核不通过

老师:我这个电脑太不给力,一打开程序和视频就快死机了,你别着急呀,肯定能做出来

学生:不着急 嗯

老师:销量为0是没有消费的意思吗

学生:是的

老师:团长id是唯一的一个人的识别码吗

学生:是的

老师:你的数据是2021年1月1日到2021年1月31日全部用户的观测数据,我们切取其中一个用户的数据看一下,比如团长ID取值位1的用户。这个用户的审核通过日期从2020年10月27日,我们是无法观察到他第一天的消费记录的,因此后面分析的时候要注意。

老师:那应该是只研究1月份注册的用户对吧,以前注册的用户是不需要研究的

学生:对的

老师:好的

老师:我们只需要计算2021年1月通过审核的用户的留存率对吧?

学生:是的。

老师:你的数据还是有问题,那些只观察了0天的怎么算

学生:我天 那就最后一周的不算

老师:好的

学生:你用python跑的?

老师:是的,mysql几乎没用过

老师:必须要有一个完整的7日观测周期,你如果需要1月份的完整数据,那就要求审核通过日期在1月1日-1月31日,统计日期为1月1日-2月7日。你筛选下数据发给我应该没问题,必须要有完整的7日观测日期

学生:这是1月1到31的数据,观测就到1月份的最后一个能观测的周期就好了,也就是到24号新注册的用户

老师:那就是观测1月1日到1月24日审核通过的用户的情况

学生:对 这个周期内新注册用户的留存率

老师:好,计算是否1日留存 次日留存 还有7日留存吗

学生:就次日留存 和7日留存就好了

老师:那个7日算当天吗

学生:

你是算还是不算的

可以以你定义的来

老师:

我算的是0-6天,

但是这个数值怎么那么大

次日留存率为 0.3364916436373207

7日留存率为 0.14238715620476378


学生:

那就这样~

老师:

tu3.png

我算的是人数

你这个留存率怎么这么高

学生:嗯 有的

老师:

我给你代码,你直接跑呀,我觉得速度还可以

你电脑肯定更没有问题

import pandas as pd
import numpy as np
#导入数据生成数据框data1
#可能由于一些原因导致审核通过日期等变量导入到python中之后变成了字符串变量
data1=pd.read_csv("D:\\A2021_02_13.csv")
#先把审核通过的数据挑出来生成新数据框data2
#这个数据是从mysql数据库中导出的,在mysql数据库中如果审核没有通过,则审核通过日期用-1表示
#这种为-1的数据不是我们需要的,
#所以我们把非-1数据挑选出来。
data2=data1[data1["审核通过日期"]!="-1"]
#需要说明的是学生在mysql数据库中提取这个数据的时候帮我进行了筛选,所以我们这个数据中不存在-1的情况
#我之所以还是把这个命令放到这里,是希望大家在做数据清洗的时候需要注意到这样的业务问题。
#将统计日期和提交资料日期字符串变成日期时间型
data2["统计日期"]=data2["统计日期"].map(lambda x :pd.Timestamp(x))
data2["提交资料日期"]=data2["提交资料日期"].map(lambda x :pd.Timestamp(x))
data2["审核通过日期"]=data2["审核通过日期"].map(lambda x :pd.Timestamp(x))
#根据审核通过日期对数据进行筛选
data3=data2[(data2["审核通过日期"]>=pd.Timestamp("2021-01-01")) & (data2["审核通过日期"]<=pd.Timestamp("2021-01-24"))]
#计算每个人在每个统计日期的总销量
#团长ID为每一个人的唯一标识ID
#每一个人是有很多行记录的
#在一个审核通过日期,可能有很多人通过审核
pivot_table1=data3.pivot_table(values=["销量"],
                       index=["审核通过日期","团长ID","统计日期"],
                       aggfunc={   "销量":np.sum})
#更改变量名字,为了以后的调用方便
pivot_table1.columns=["销量"]
#将索引变成普通字段
pivot_table1.reset_index(inplace=True)
#生成是否消费字段
pivot_table1["是否消费"]=pivot_table1["销量"].map(lambda x:1 if x>0 else 0)
def f1(x):
    aa=x["统计日期"]-x["审核通过日期"]
    bb=aa.days
    return bb
pivot_table1["距离审核通过第多少天"]=pivot_table1.apply(f1,axis=1)
#生成去重后的团长ID数据框,在这个数据框里面团长id是没有重复值的,字段包含团长id,审核通过日期,还有一列取值全为1的字段temp
#这个temp是为了以后方便汇总计算
QQQ=pivot_table1.drop_duplicates(subset=["团长ID"])
QQQ["temp"]=1
QQQ1=QQQ.loc[:,["团长ID","审核通过日期","temp"]]
QQQ1.set_index(["团长ID"],inplace=True)
#计算每一个团长第0天是否有消费
#我们的数据是存在一些问题的,比如有的人的第0填的消费记录是缺失值,也就是没有这一行记录
#后面几天可能又有了。
temp0=pivot_table1[pivot_table1["距离审核通过第多少天"]==0]
temp0.rename(columns={"是否消费":"第0天是否消费"},inplace=True)
temp0.set_index(["团长ID"],inplace=True)
temp0.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
#计算每一个团长第一天是否有消费
temp1=pivot_table1[pivot_table1["距离审核通过第多少天"]==1]
temp1.rename(columns={"是否消费":"第1天是否消费"},inplace=True)
temp1.set_index(["团长ID"],inplace=True)
temp1.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
#计算每一个团长第2天是否有消费
temp2=pivot_table1[pivot_table1["距离审核通过第多少天"]==2]
temp2.rename(columns={"是否消费":"第2天是否消费"},inplace=True)
temp2.set_index(["团长ID"],inplace=True)
temp2.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
temp3=pivot_table1[pivot_table1["距离审核通过第多少天"]==3]
temp3.rename(columns={"是否消费":"第3天是否消费"},inplace=True)
temp3.set_index(["团长ID"],inplace=True)
temp3.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
temp4=pivot_table1[pivot_table1["距离审核通过第多少天"]==4]
temp4.rename(columns={"是否消费":"第4天是否消费"},inplace=True)
temp4.set_index(["团长ID"],inplace=True)
temp4.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
temp5=pivot_table1[pivot_table1["距离审核通过第多少天"]==5]
temp5.rename(columns={"是否消费":"第5天是否消费"},inplace=True)
temp5.set_index(["团长ID"],inplace=True)
temp5.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
temp6=pivot_table1[pivot_table1["距离审核通过第多少天"]==6]
temp6.rename(columns={"是否消费":"第6天是否消费"},inplace=True)
temp6.set_index(["团长ID"],inplace=True)
temp6.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
temp7=pivot_table1[pivot_table1["距离审核通过第多少天"]==7]
temp7.rename(columns={"是否消费":"第7天是否消费"},inplace=True)
temp7.set_index(["团长ID"],inplace=True)
temp7.drop(columns=["距离审核通过第多少天","统计日期","销量","审核通过日期"],inplace=True,axis=1)
#将多个数据横向拼接在一起,这里没有写成循环的命令,是为了让大家更好理解这个拼接过程
#这个拼接过程相当于把一个长数据变成一个宽数据
ddd=pd.concat([QQQ1,temp0,temp1,temp2,temp3,temp4,temp5,temp6,temp7],axis=1)
ddd.sort_values(by=["审核通过日期"],ascending=[True],inplace=True)
#计算是否一日留存
ddd["是否一日留存"]=ddd.apply(lambda x:1 if x["第0天是否消费"]==1 else 0,axis=1)
#计算是否次日留存
ddd["是否次日留存"]=ddd.apply(lambda x:1 if x["第0天是否消费"]==1 and x["第1天是否消费"]==1 else 0,axis=1)
#计算是否次日留存
ddd["是否7日留存"]=ddd.apply(lambda x:1 if x["第0天是否消费"]==1 and x["第1天是否消费"]==1 and x["第2天是否消费"]==1 and x["第3天是否消费"]==1 and x["第4天是否消费"]==1 and x["第5天是否消费"]==1 and x["第6天是否消费"]==1 else 0,axis=1)
result=ddd.loc[:,["temp","是否次日留存","是否7日留存"]].sum(axis=0)
print("次日留存率为",result["是否次日留存"]/result["temp"])
print("7日留存率为",result["是否7日留存"]/result["temp"])


学生:

我可以试一下

老师:

学生:但是这个7日留存率 有这么低吗

老师:

你可以把ddd那个数据导出来看有没有问题

ddd里面有每个人每日的消费情况

7日留存那就是7天都消费

tu4.png

学生:

这个7日留存率 是所有的用户的 还是说啥

噢噢 了解了

老师:你看下ddd那个数据框就知道了

学生:我懂了

老师:

这个是1月1日到1月24日所有新用户的消费情况

没啥问题我去睡觉啦

那么也就是这个次日留存 就是首日下单 次日也下单是吧

学生:

如果有一天没下单 就不算是不是

老师:是的

学生:

好的 老师辛苦了 我研究一下

老师新年快乐呀 大年三十的还请您搞这么晚 哎



添加CDA认证专家【维克多阿涛】,微信号:【cdashijiazhuang】,提供数据分析指导及CDA考试秘籍。已助千人通过CDA数字化人才认证。欢迎交流,共同成长!
1244.7319 3 2 关注作者 收藏

评论(2)

85691082
2021-03-15

简单易懂,棒棒哒。

0.0000 0 0 回复
ermutuxia
2021-02-14

在理解意思的情况下,下面这个代码更简洁一些

import pandas as pd

import numpy as np

#导入数据生成数据框data1

#可能由于一些原因导致审核通过日期等变量导入到python中之后变成了字符串变量

data1=pd.read_csv("D:\\A2021_02_13.csv")


#先把审核通过的数据挑出来生成新数据框data2

#这个数据是从mysql数据库中导出的,在mysql数据库中如果审核没有通过,则审核通过日期用-1表示

#这种为-1的数据不是我们需要的,

#所以我们把非-1数据挑选出来。


data2=data1[data1["审核通过日期"]!="-1"]

#需要说明的是学生在mysql数据库中提取这个数据的时候帮我进行了筛选,所以我们这个数据中不存在-1的情况

#我之所以还是把这个命令放到这里,是希望大家在做数据清洗的时候需要注意到这样的业务问题。



#将统计日期和提交资料日期字符串变成日期时间型

data2["统计日期"]=data2["统计日期"].map(lambda x :pd.Timestamp(x))

data2["提交资料日期"]=data2["提交资料日期"].map(lambda x :pd.Timestamp(x))

data2["审核通过日期"]=data2["审核通过日期"].map(lambda x :pd.Timestamp(x))


#计算重要变量

def f1(x):

aa=x["统计日期"]-x["审核通过日期"]

bb=aa.days

return bb

data2["距离审核通过第多少天"]=data2.apply(f1,axis=1)


#根据审核通过日期对数据进行筛选

data3=data2[(data2["审核通过日期"]>=pd.Timestamp("2021-01-01")) & (data2["审核通过日期"]<=pd.Timestamp("2021-01-24"))]


#计算每个人在每个统计日期的总销量

#团长ID为每一个人的唯一标识ID

#每一个人是有很多行记录的

#在一个审核通过日期,可能有很多人通过审核

pivot_table1=data3.pivot_table(values=["销量"],

index=["审核通过日期","团长ID","距离审核通过第多少天"],

aggfunc={ "销量":np.sum})


#更改变量名字,为了以后的调用方便

pivot_table1.columns=["销量"]

#将索引变成普通字段

pivot_table1.reset_index(inplace=True)

#生成是否消费字段

pivot_table1["是否消费"]=pivot_table1["销量"].map(lambda x:1 if x>0 else 0)


#只保留"距离审核通过第多少天"小于等于7的情况

pivot_table1.drop(index=pivot_table1.index[pivot_table1["距离审核通过第多少天"]>7],axis=0,inplace=True)


pivot_table1.set_index(["审核通过日期","团长ID","距离审核通过第多少天"],drop=True,inplace=True)


#将序列由长变宽

ddd=pivot_table1["是否消费"].unstack(level=-1)



#计算是否一日留存

ddd["是否一日留存"]=ddd.apply(lambda x:1 if x[0]==1 else 0,axis=1)

#计算是否次日留存

ddd["是否次日留存"]=ddd.apply(lambda x:1 if x[0]==1 and x[1]==1 else 0,axis=1)


#计算是否次日留存

ddd["是否7日留存"]=ddd.iloc[:,:7].sum(axis=1)==7

ddd["temp"]=1


result=ddd.loc[:,["temp","是否次日留存","是否7日留存"]].sum(axis=0)


print("次日留存率为",result["是否次日留存"]/result["temp"])

print("7日留存率为",result["是否7日留存"]/result["temp"])


44.7319 2 0 回复

推荐课程