# 留存分析
SQL='''select *,
concat(ROUND(100*次日留存用户数/日活跃用户数),"%") 次日留存率,
concat(ROUND(100*三日留存用户数/日活跃用户数),"%") 三日留存率,
concat(ROUND(100*七日留存用户数/日活跃用户数),"%") 七日留存率
from(
select date(a.`付款时间`) 日期 ,DATE_FORMAT(a.`付款时间`,"%H") 时间,
COUNT(DISTINCT a.`用户ID`) 日活跃用户数,
COUNT(DISTINCT b.`用户ID`) 次日留存用户数,
COUNT(DISTINCT c.`用户ID`) 三日留存用户数,
COUNT(DISTINCT d.`用户ID`) 七日留存用户数
from paper_data a
left join paper_data b
on a.`用户ID`=b.`用户ID`
and date(b.`付款时间`)=date(a.`付款时间`)+1
left join paper_data c
on a.`用户ID`=c.`用户ID`
and date(c.`付款时间`)=date(a.`付款时间`)+3
left join paper_data d
on a.`用户ID`=d.`用户ID`
and date(d.`付款时间`)=date(a.`付款时间`)+7
where a.`付款时间` between "2020-01-01" and "2020-01-31"
GROUP BY date(a.`付款时间`)
)f;'''
liuchun = get_mysql_data(DB, SQL)
liuchun
x=liuchun['日期']
y1=liuchun['次日留存率']
y2=liuchun['三日留存率']
y3=liuchun['七日留存率']
plt.figure(figsize=(25,10),dpi=80)
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
rects1=plt.plot(x,y1,color="red",alpha=0.5,linestyle="-",linewidth=3,label="次日留存率")
rects2=plt.plot(x,y2,color="g",alpha=0.5,linestyle="-",linewidth=3,label="三日留存率")
rects3=plt.plot(x,y3,color="b",alpha=0.5,linestyle="-",linewidth=3,label="七日留存率")
rects4=plt.legend(prop=my_font,loc="best")
for i in range(len(x)):
plt.text(x[i],y1[i],y1[i],fontsize=15,ha="center")
for i in range(len(x)):
plt.text(x[i],y2[i],y2[i],fontsize=15,ha="center")
for i in range(len(x)):
plt.text(x[i],y3[i],y3[i],fontsize=15,ha="center")
plt.show()
订单分析
订单分析(可以把订单换成其他的,比如用户数,销售量等数据)
# 1.根据下单的时间来统计订单量
# (1)年维度
SQL='''select date_format(付款时间,"%Y")下单年 ,count(订单ID) as 订单量 from paper_data group by date_format(付款时间,"%Y")'''
order_num_year=get_mysql_data(DB, SQL)
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
x=order_num_year["下单年"]
y=order_num_year["订单量"]
plt.figure(figsize=(20,8),dpi=80)
rects = plt.bar(x,y,width=0.3,color="b",label="2019")
# 标注
for rect in rects:
height=rect.get_height()
plt.text(rect.get_x()+rect.get_width()/2,height+0.3,str(height),ha="center")
plt.title("年度订单量",FontProperties=my_font)
plt.xlabel("年份",FontProperties=my_font)
plt.ylabel("订单量",FontProperties=my_font)
plt.legend(prop=my_font,loc="best")
plt.show()
# 订单分析
# 1.根据下单的时间来统计订单量
# (2)年月维度
SQL='''select date_format(付款时间,"%Y/%m")下单年月 ,count(订单ID) as 订单量 from paper_data group by date_format(付款时间,"%Y/%m")'''
order_num_month=get_mysql_data(DB, SQL)
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
x=order_num_month["下单年月"]
y=order_num_month["订单量"]
plt.figure(figsize=(20,8),dpi=80)
rects = plt.bar(x,y,width=0.3,color=["r","g","b"])
# 标注
for rect in rects:
height=rect.get_height()
plt.text(rect.get_x()+rect.get_width()/2,height+0.3,str(height),ha="center")
plt.show()
# (3)年月、城市维度(折线图)
SQL='''select date_format(下单时间,"%Y/%m")下单年月 ,城市,count(订单ID) as 订单量
from paper_data
group by date_format(下单时间,"%Y/%m"),城市
order by date_format(下单时间,"%Y/%m");
'''
order_num_month1=get_mysql_data(DB, SQL)
#把每个城市的数据取出来
order_month_city=order_num_month1.groupby(["城市"])
order_shanghai=order_month_city.get_group("上海").reset_index(drop="true")
order_beijing=order_month_city.get_group("北京").reset_index(drop="true")
order_hangzhou=order_month_city.get_group("杭州").reset_index(drop="true")
order_xian=order_month_city.get_group("西安").reset_index(drop="true")
# matplotlib画图部分
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
plt.figure(figsize=(25,10),dpi=80)
plt.plot(order_shanghai["下单年月"],order_shanghai["订单量"],marker='o',color="r",label="上海订单量")
plt.plot(order_beijing["下单年月"],order_beijing["订单量"],marker='v',color="g",label="北京订单量")
plt.plot(order_hangzhou["下单年月"],order_hangzhou["订单量"],marker='^',color="b",label="杭州订单量")
plt.plot(order_xian["下单年月"],order_xian["订单量"],marker='.',color="y",label="西安订单量")
plt.xlabel("时间",FontProperties=my_font)
plt.ylabel("订单量",FontProperties=my_font)
plt.title("各城市每月订单量",FontProperties=my_font)
plt.legend(prop=my_font,loc="best")
# df.idxmax()默认值是0,求列最大值的行索引,1就是反过来
def city_max(df):
return df.idxmax()
# 最大值标记
plt.text(order_shanghai["下单年月"].iloc[city_max(order_shanghai["订单量"])],order_shanghai.max()["订单量"]+5,"上海最大订单:{}".format(order_shanghai.max()["订单量"]),fontsize=12,ha="center",color="r",FontProperties=my_font)
plt.text(order_beijing["下单年月"].iloc[city_max(order_beijing["订单量"])],order_beijing.max()["订单量"]+5,"北京最大订单:{}".format(order_beijing.max()["订单量"]),fontsize=12,ha="center",color="g",FontProperties=my_font)
plt.text(order_hangzhou["下单年月"].iloc[city_max(order_hangzhou["订单量"])],order_hangzhou.max()["订单量"]+5,"杭州最大订单:{}".format(order_hangzhou.max()["订单量"]),fontsize=12,ha="center",color="b",FontProperties=my_font)
plt.text(order_xian["下单年月"].iloc[city_max(order_xian["订单量"])],order_xian.max()["订单量"]+5,"西安最大订单:{}".format(order_xian.max()["订单量"]),fontsize=12,ha="center",color="y",FontProperties=my_font)
plt.show()
# -----------------------------
# 每年、每月的一样的做法,改下日期字段就好
# (3)年城市维度(柱状图)
SQL='''select date_format(付款时间,"%Y")下单年,城市,count(订单ID) as 订单量
from paper_data
group by date_format(付款时间,"%Y"),城市
order by date_format(付款时间,"%Y");
'''
order_num_year=get_mysql_data(DB, SQL)
# order_num_month.describe()
# 按城市拆分数据
order_year_city=order_num_year.groupby(["城市"])
order_shanghai=order_year_city.get_group("上海").reset_index(drop="true")
order_beijing=order_year_city.get_group("北京").reset_index(drop="true")
order_hangzhou=order_year_city.get_group("杭州").reset_index(drop="true")
order_xian=order_year_city.get_group("西安").reset_index(drop="true")
# -----matplotlib画图部分------
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
plt.figure(figsize=(25,10),dpi=80)
# 设置柱子位置,通过偏移量a来实现,
# 由于时间是datetime类型或者object(str)类型的不能直接加偏移量所以这里用长度长度实现柱子的位置,然后把刻度值重新设置
a=0.2
x_01=list(range(len(order_shanghai["下单年"])))
x_02=[i+a for i in x_01]
x_03=[i-2*a for i in x_01]
x_04=[i-a for i in x_01]
# 画图
rects1=plt.bar(x_01,order_shanghai["订单量"],a,color="r",align='edge',label="上海订单量")
rects2=plt.bar(x_02,order_beijing["订单量"],a,color="g",align='edge',label="北京订单量")
rects3=plt.bar(x_03,order_hangzhou["订单量"],a,color="b",align='edge',label="杭州订单量")
rects4=plt.bar(x_04,order_xian["订单量"],a,color="y",align='edge',label="西安订单量")
# x轴刻度
plt.xticks(x_01,labels=order_shanghai["下单年"])
# 轴名称
plt.xlabel("年份",FontProperties=my_font)
plt.ylabel("订单量",FontProperties=my_font)
# 标题
plt.title("各城市每月订单量",FontProperties=my_font)
# 图例
plt.legend(prop=my_font,loc="best")
# 标记
def mark_bar(rects):
for rect in rects:
height=rect.get_height()
plt.text(rect.get_x()+rect.get_width()/2,height+0.3,str(height),fontsize=15,ha="center")
mark_bar(rects1)
mark_bar(rects2)
mark_bar(rects3)
mark_bar(rects4)
plt.show()
# 3、小时订单量分布情况
SQL= '''SELECT date_format(下单时间,"%H") 下单时, count(`订单ID`) 订单量
from paper_data
where date_format(下单时间,"%Y-%m")="2020-01"
GROUP BY date_format(下单时间,"%H")
ORDER BY date_format(下单时间,"%H")
;'''
h_dingdan=get_mysql_data(DB, SQL)
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
plt.figure(figsize=(25,10),dpi=80)
x=h_dingdan["下单时"]
y=h_dingdan["订单量"]
plt.plot(x,y,color="r",label="每小时订单量")
# 设置标记
for a,b in zip(x,y):
#a x坐标, b坐标, (a,b) 一个点
# ha 水平方向 va 垂直方向 fontsize 大小
plt.text(a,b,b ,ha='center',va='bottom', fontsize=12)
#设置刻度
xtick_labels = ['{}:00'.format(i) for i in x]
plt.xticks(x,xtick_labels)
#设置坐标名称
plt.xlabel("时间",FontProperties=my_font)
plt.ylabel("订单量",FontProperties=my_font)
#设置表名
plt.title("1月每小时订单量",FontProperties=my_font)
#设置图例
plt.legend(prop=my_font,loc="best")
plt.show()
RFM模型
#RMF分层
SQL='''SELECT 用户ID,COUNT(订单ID) AS F,round(sum(实收金额),2) AS M,date_format(max(付款时间),"%Y/%m/%d") as R
FROM paper_data GROUP BY 用户ID ORDER BY 用户ID
;'''
User_layer=get_mysql_data(DB, SQL)
# RMF评分公式
# User_layer["R1"]=((pd.to_datetime("2020/03-01")-pd.to_datetime(User_layer["R"])) 后面的处理方式是为了把timedelta类型转换成Int类型
User_layer["R1"]=((pd.to_datetime("2020/03-01")-pd.to_datetime(User_layer["R"]))/pd.Timedelta(1,"D")).fillna(0).astype(int)
User_layer["RR"]=(pd.to_datetime(User_layer["R"])-pd.to_datetime(min(User_layer["R"])))/((pd.to_datetime(max(User_layer["R"]))-pd.to_datetime(min(User_layer["R"]))))
User_layer["FF"]=(User_layer["F"]-min(User_layer["F"]))/(max(User_layer["F"])-min(User_layer["F"]))
User_layer["MM"]=(User_layer["M"]-min(User_layer["M"]))/(max(User_layer["M"])-min(User_layer["M"]))
User_layer["SCORE"]=100*(0.25*User_layer["FF"]+0.6*User_layer["MM"]+0.15*User_layer["RR"])
User_layer
# df遍历后的结果最好用list/dict存放起来。df本身一个一个添加数据遍历效率不高。而且难取数,最好用List/dict整体取赋值
list_level=[]
for index ,rows in User_layer.iterrows():
# print("索引是:",index,"数据是:",rows)
if rows["SCORE"]<=User_describe.loc["25%"]["SCORE"]:
rows["level"]="四等"
elif rows["SCORE"]<=User_describe.loc["50%"]["SCORE"]:
rows["level"]="三等"
elif rows["SCORE"]<=User_describe.loc["75%"]["SCORE"]:
rows["level"]="二等"
else:
rows["level"]="一等"
list_level.append(rows["level"])
User_layer['level']=list_level
User_layer
用户ID
F
M
R
R1
RR
FF
MM
SCORE
level
0
1
32
3517.0
2021/01/25
-330
0.980519
0.673913
0.663953
71.392801
一等
1
10
29
2600.0
2021/01/23
-328
0.977922
0.608696
0.490345
59.306896
一等
2
100
31
3906.0
2021/01/11
-316
0.962338
0.652174
0.737599
74.995376
一等
3
1000
24
2255.0
2021/01/07
-312
0.957143
0.500000
0.425028
52.358847
二等
4
10009
1
71.0
2019/04/09
327
0.127273
0.000000
0.011549
2.602010
四等
...
...
...
...
...
...
...
...
...
...
...
5924
9977
1
19.0
2020/10/27
-240
0.863636
0.000000
0.001704
13.056779
三等
5925
998
34
3634.0
2021/02/06
-342
0.996104
0.717391
0.686104
74.042566
一等
5926
999
33
3356.0
2021/01/27
-332
0.983117
0.695652
0.633472
70.146388
一等
5927
9993
1
125.0
2020/11/04
-248
0.874026
0.000000
0.021772
14.416713
三等
5928
9994
1
135.0
2020/08/22
-174
0.777922
0.000000
0.023665
13.088748
三等
5929 rows × 10 columns
用户标签
# 用户打标签
SQL='''select 用户ID,count(*) as order_num,count(date_format(付款时间,"%M")) 月数,
sum(case when 取车类型='自取' then 1 else 0 end) as 自取次数,
sum(case when 优惠金额>1 then 1 else 0 end) as 优惠券使用次数,
sum(case when 非网点还车费>0 then 1 else 0 end) as 还车次数
from paper_data
group by 用户ID
;
'''
bq=get_mysql_data(DB, SQL)
data_total=pd.merge(User_layer,bq,on="用户ID")
data_total
# print(data_total["FF"].dtypes,data_total["自取次数"].dtypes)
for index,row in data_total.iterrows():
# 取车
if row["FF"]==0:
row["FF"]= row["FF"]+0.0001
# Int/浮点数会报错,注意转换类型
if float(row["自取次数"])/row["FF"]>0.5:
data_total["取车偏好"]="时租居多"
else:
data_total["取车偏好"]="日租居多"
# 网点还车
if float(row["还车次数"])/row["FF"]>0.5:
data_total["还车偏好"]="非网点还车"
else:
data_total["还车偏好"]="网点还车"
# 月均订单
if row["FF"]/row["月数"]>6:
data_total["月均单量"]="月均单量大于6"
else:
data_total["月均单量"]="月均单量小于6"
data_total
用户ID
F
M
R
R1
RR
FF
MM
SCORE
level
order_num
月数
自取次数
优惠券使用次数
还车次数
取车偏好
还车偏好
月均单量
0
1
32
3517.0
2021/01/25
-330
0.980519
0.673913
0.663953
71.392801
一等
32
32
11
23
13
时租居多
网点还车
月均单量小于6
1
10
29
2600.0
2021/01/23
-328
0.977922
0.608696
0.490345
59.306896
一等
29
29
16
24
12
时租居多
网点还车
月均单量小于6
2
100
31
3906.0
2021/01/11
-316
0.962338
0.652174
0.737599
74.995376
一等
31
31
12
21
7
时租居多
网点还车
月均单量小于6
3
1000
24
2255.0
2021/01/07
-312
0.957143
0.500000
0.425028
52.358847
二等
24
24
13
15
11
时租居多
网点还车
月均单量小于6
4
10009
1
71.0
2019/04/09
327
0.127273
0.000000
0.011549
2.602010
四等
1
1
1
0
1
时租居多
网点还车
月均单量小于6
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
5924
9977
1
19.0
2020/10/27
-240
0.863636
0.000000
0.001704
13.056779
三等
1
1
0
1
0
时租居多
网点还车
月均单量小于6
5925
998
34
3634.0
2021/02/06
-342
0.996104
0.717391
0.686104
74.042566
一等
34
34
18
24
13
时租居多
网点还车
月均单量小于6
5926
999
33
3356.0
2021/01/27
-332
0.983117
0.695652
0.633472
70.146388
一等
33
33
12
19
10
时租居多
网点还车
月均单量小于6
5927
9993
1
125.0
2020/11/04
-248
0.874026
0.000000
0.021772
14.416713
三等
1
1
0
1
1
时租居多
网点还车
月均单量小于6
5928
9994
1
135.0
2020/08/22
-174
0.777922
0.000000
0.023665
13.088748
三等
1
1
1
0
0
时租居多
网点还车
月均单量小于6
5929 rows × 18 columns
用户生命周期
def user_life(data,a,b,c):
list_1=[]
for index,row in data.iterrows():
# print(row[a])
# print(index)
if row[a]==1:
row[c]="引入期"
elif row[a]>1 and row[a]<3 and row[b]<=200:
row[c]="成长期"
elif row[a]>=3 and row[b]<=300:
row[c]="休眠期"
elif row[b]>300:
row[c]="流失期"
else:
row[c]="其他"
# print(row[c])
list_1.append(row)
data_1=pd.DataFrame(list_1)
# print(data_1)
return data_1
Userlife=pd.DataFrame()
Userlife=user_life(data_total,"F","R1","生命周期")
Userlife.head(10)
用户ID
F
M
R
R1
RR
FF
MM
SCORE
level
order_num
月数
自取次数
优惠券使用次数
还车次数
取车偏好
还车偏好
月均单量
生命周期
0
1
32
3517.0
2021/01/25
-330
0.980519
0.673913
0.663953
71.392801
一等
32
32
11
23
13
时租居多
网点还车
月均单量小于6
休眠期
1
10
29
2600.0
2021/01/23
-328
0.977922
0.608696
0.490345
59.306896
一等
29
29
16
24
12
时租居多
网点还车
月均单量小于6
休眠期
2
100
31
3906.0
2021/01/11
-316
0.962338
0.652174
0.737599
74.995376
一等
31
31
12
21
7
时租居多
网点还车
月均单量小于6
休眠期
3
1000
24
2255.0
2021/01/07
-312
0.957143
0.500000
0.425028
52.358847
二等
24
24
13
15
11
时租居多
网点还车
月均单量小于6
休眠期
4
10009
1
71.0
2019/04/09
327
0.127273
0.000000
0.011549
2.602010
四等
1
1
1
0
1
时租居多
网点还车
月均单量小于6
引入期
5
1001
28
3074.0
2021/01/24
-329
0.979221
0.586957
0.580083
64.167223
一等
28
28
12
18
12
时租居多
网点还车
月均单量小于6
休眠期
6
10014
1
49.0
2020/08/25
-177
0.781818
0.000000
0.007384
12.170287
三等
1
1
1
1
0
时租居多
网点还车
月均单量小于6
引入期
7
10016
1
67.0
2020/07/19
-140
0.733766
0.000000
0.010791
11.653976
三等
1
1
0
1
0
时租居多
网点还车
月均单量小于6
引入期
8
1002
29
3316.0
2021/01/03
-308
0.951948
0.608696
0.625899
67.050569
一等
29
29
12
19
15
时租居多
网点还车
月均单量小于6
休眠期
9
10023
1
81.0
2020/09/09
-192
0.801299
0.000000
0.013442
12.825993
三等
1
1
1
1
0
时租居多
网点还车
月均单量小于6
引入期
"休眠期" in Userlife["生命周期"].values
True
zq=Userlife.groupby("生命周期")
yr=zq.get_group("引入期")["生命周期"].count()
cz=zq.get_group("成长期")["生命周期"].count()
xm=zq.get_group("休眠期")["生命周期"].count()
ls=zq.get_group("流失期")["生命周期"].count()
x=["引入期","成长期","休眠期","流失期"]
y=[yr,cz,xm,ls]
for i in range(len(x)):
print(x[i],y[i])
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 18)
plt.figure(figsize=(25,10),dpi=80)
plt.rcParams['font.sans-serif']=['STSONG'] #中文标签设置
# plt.rcParams['axes.unicode_minus']=False #用来正常显示坐标负号
rects=plt.bar(x,y,0.3,color=["r","g","b","y"])
plt.xticks(x,x,fontproperties=my_font)
for rect in rects:
height=rect.get_height()
plt.text(rect.get_x()+rect.get_width()/2,height+1,str(height),fontsize=18,ha="center")
plt.show()
引入期 2528
成长期 895
休眠期 2414
流失期 32
用户评分统计
# 评分
#0分代表未参与评分,可以统计用户的参与度
SQL='''select date_format(付款时间,"%Y/%m") 付款年月,评分,count(distinct 用户ID) as user_num,count(*) as order_num
from paper_data group by date_format(付款时间,"%Y/%m"),评分;'''
pinfen_num=get_mysql_data(DB,SQL)
pinfen_num["评分"]=pinfen_num["评分"].astype(int)
pinfen_num["参与度"]=pinfen_num["user_num"]/pinfen_num["order_num"]
pinfen_num.dtypes