學習記錄——共享汽車分析
前言
- 本文僅記錄個人學習程序寫的代碼供自己復盤使用,如果對你有幫助和啟發那就更好了,
- 新人作品,歡迎討論和斧正,大神輕噴,
- 純代碼實作,無結論,
- 一些相似的維度舉一反三就行,
- 需要資料集練習的可以留言
目標
- python, mysql, matplotlib 代碼練習
- 常用資料指標的實作
資料匯入
python 連接資料庫 Mysql
#獲取資料方法
import pymysql
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib import font_manager
def get_mysql_data(DB, sql):
conn = pymysql.connect(host=DB['host'], port=DB['port'], user=DB['user'], password=DB['password'], database=DB['dbname'])
# 創建游標
cursor = conn.cursor()
# 執行sql陳述句
cursor.execute(sql)
# 調出資料
data = cursor.fetchall()
# cols為欄位資訊
cols = cursor.description
# 將資料轉換為DataFrame
col = []
for i in cols:
col.append(i[0])
# data轉成list形式
data = list(map(list, data))
data = pd.DataFrame(data,columns=col)
# 關閉游標以及連接
cursor.close()
conn.close()
return data
日期時間處理——獲取年月日時維度
def time_data(data):
data['下單年'] = data["下單時間"].astype("str").apply(lambda x: x.split('-')[0])
data['下單年月'] = data["下單時間"].astype("str").apply(lambda x: x.split('-')[0]+"/"+x.split('-')[1])
data['下單月'] = data["下單時間"].astype("str").apply(lambda x: x.split('-')[1])
data['下單日'] = data["下單時間"].astype("str").apply(lambda x: x.split('-')[2])
data['下單時'] = data["下單時間"].astype("str").apply(lambda x: x.split('-')[0])
data['付款年'] = data["付款日期"].astype("str").apply(lambda x: x.split('-')[0])
data['付款年月'] = data["付款日期"].astype("str").apply(lambda x: x.split('-')[0]+"/"+x.split('-')[1])
data['付款月'] =data["付款日期"].astype("str").apply(lambda x: x.split('-')[1])
data['付款日'] = data["付款日期"].astype("str").apply(lambda x: x.split('-')[2])
data['付款時'] = data["付款日期"].astype("str").apply(lambda x: x.split('-')[0])
return data
DB = { 'host':"127.0.0.1",'port':3306, 'user':'root','password':'password','dbname':'datebase'}
留存分析
# 留存分析
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
| 用戶ID | F | M | R | R1 | RR | FF | MM | SCORE | |
|---|---|---|---|---|---|---|---|---|---|
| 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 × 9 columns
# 根據權重SCORE四分位分組的依據
User_describe=User_layer.describe()
User_describe
| F | M | R1 | RR | FF | MM | SCORE | |
|---|---|---|---|---|---|---|---|
| count | 5929.000000 | 5929.000000 | 5929.000000 | 5929.000000 | 5929.000000 | 5929.000000 | 5929.000000 |
| mean | 10.979086 | 1148.957160 | -117.514083 | 0.704564 | 0.216937 | 0.215630 | 28.929667 |
| std | 13.656880 | 1440.872815 | 228.595026 | 0.296877 | 0.296889 | 0.272789 | 26.743116 |
| min | 1.000000 | 10.000000 | -345.000000 | 0.000000 | 0.000000 | 0.000000 | 0.185039 |
| 25% | 1.000000 | 112.000000 | -318.000000 | 0.489610 | 0.000000 | 0.019311 | 8.927758 |
| 50% | 2.000000 | 198.000000 | -208.000000 | 0.822078 | 0.021739 | 0.035593 | 14.496745 |
| 75% | 26.000000 | 2679.000000 | 48.000000 | 0.964935 | 0.543478 | 0.505301 | 58.371452 |
| max | 47.000000 | 5292.000000 | 425.000000 | 1.000000 | 1.000000 | 1.000000 | 97.960474 |
# 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
付款年月 object
評分 int32
user_num int64
order_num int64
參與度 float64
dtype: object
# 用戶評分參與度=有評分的用戶/下單用戶量
import numpy as np
# 按評分分組
pf_group=pinfen_num.groupby(["評分"])
# id_name
# 取出分組的值
# 方法一
list_pf=[]
for key,value in pf_group:
list_pf.append(value.reset_index(drop="true"))
# 方法二格式化成串列或者字典,詳見后面一個代碼塊
x=list_pf[0]["付款年月"]
y=[]
for i in range(len(list_pf)):
y.append(list_pf[i]["參與度"])
# print("x:",x.values)
# print("y:",y[1])
my_font = font_manager.FontProperties(fname="C:/Users/wty_pc/Anaconda3/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/STSONG.TTF",size = 15)
fig=plt.figure(figsize=(20,40),dpi=80)
colors=["r","g","b"]
for i in range(1,12):
ax=fig.add_subplot(11,1,i)
ax.plot(x,y[i-1],color=colors[np.random.randint(3)],label="{}分".format(i-1))
x_label="評分為{}的趨勢".format(i-1)
ax.set_xlabel(x_label,fontproperties=my_font)
ax.set_ylabel("參與度",fontproperties=my_font)
ax.legend()
#調整子圖間距
plt.tight_layout()
plt.show()

groupby取數的技巧
# groupby 格式化成串列 為轉換成字典做鋪墊
a=list(pinfen_num.groupby(["評分"]))
# 串列的每個元素是個元組,每個元組的值為分組鍵和值(datafarme)
a[0][1].reset_index(drop = True)
# print(type(a[1]))
| 付款年月 | 評分 | user_num | order_num | 參與度 | |
|---|---|---|---|---|---|
| 0 | 2019/01 | 0 | 230 | 241 | 0.954357 |
| 1 | 2019/02 | 0 | 206 | 213 | 0.967136 |
| 2 | 2019/03 | 0 | 239 | 258 | 0.926357 |
| 3 | 2019/04 | 0 | 209 | 216 | 0.967593 |
| 4 | 2019/05 | 0 | 213 | 223 | 0.955157 |
| 5 | 2019/06 | 0 | 217 | 230 | 0.943478 |
| 6 | 2019/07 | 0 | 229 | 239 | 0.958159 |
| 7 | 2019/08 | 0 | 216 | 222 | 0.972973 |
| 8 | 2019/09 | 0 | 214 | 229 | 0.934498 |
| 9 | 2019/10 | 0 | 232 | 244 | 0.950820 |
| 10 | 2019/11 | 0 | 225 | 235 | 0.957447 |
| 11 | 2019/12 | 0 | 239 | 247 | 0.967611 |
| 12 | 2020/01 | 0 | 271 | 287 | 0.944251 |
| 13 | 2020/02 | 0 | 187 | 200 | 0.935000 |
| 14 | 2020/03 | 0 | 221 | 230 | 0.960870 |
| 15 | 2020/04 | 0 | 216 | 230 | 0.939130 |
| 16 | 2020/05 | 0 | 219 | 227 | 0.964758 |
| 17 | 2020/06 | 0 | 207 | 218 | 0.949541 |
| 18 | 2020/07 | 0 | 224 | 232 | 0.965517 |
| 19 | 2020/08 | 0 | 239 | 250 | 0.956000 |
| 20 | 2020/09 | 0 | 224 | 233 | 0.961373 |
| 21 | 2020/10 | 0 | 240 | 250 | 0.960000 |
| 22 | 2020/11 | 0 | 220 | 227 | 0.969163 |
| 23 | 2020/12 | 0 | 218 | 230 | 0.947826 |
| 24 | 2021/01 | 0 | 238 | 251 | 0.948207 |
| 25 | 2021/02 | 0 | 56 | 57 | 0.982456 |
# groupby 格式化成字典
a=dict(list(pinfen_num.groupby(["評分"])))
# type(a[0])
a[0].reset_index(drop=True)
| 付款年月 | 評分 | user_num | order_num | 參與度 | |
|---|---|---|---|---|---|
| 0 | 2019/01 | 0 | 230 | 241 | 0.954357 |
| 1 | 2019/02 | 0 | 206 | 213 | 0.967136 |
| 2 | 2019/03 | 0 | 239 | 258 | 0.926357 |
| 3 | 2019/04 | 0 | 209 | 216 | 0.967593 |
| 4 | 2019/05 | 0 | 213 | 223 | 0.955157 |
| 5 | 2019/06 | 0 | 217 | 230 | 0.943478 |
| 6 | 2019/07 | 0 | 229 | 239 | 0.958159 |
| 7 | 2019/08 | 0 | 216 | 222 | 0.972973 |
| 8 | 2019/09 | 0 | 214 | 229 | 0.934498 |
| 9 | 2019/10 | 0 | 232 | 244 | 0.950820 |
| 10 | 2019/11 | 0 | 225 | 235 | 0.957447 |
| 11 | 2019/12 | 0 | 239 | 247 | 0.967611 |
| 12 | 2020/01 | 0 | 271 | 287 | 0.944251 |
| 13 | 2020/02 | 0 | 187 | 200 | 0.935000 |
| 14 | 2020/03 | 0 | 221 | 230 | 0.960870 |
| 15 | 2020/04 | 0 | 216 | 230 | 0.939130 |
| 16 | 2020/05 | 0 | 219 | 227 | 0.964758 |
| 17 | 2020/06 | 0 | 207 | 218 | 0.949541 |
| 18 | 2020/07 | 0 | 224 | 232 | 0.965517 |
| 19 | 2020/08 | 0 | 239 | 250 | 0.956000 |
| 20 | 2020/09 | 0 | 224 | 233 | 0.961373 |
| 21 | 2020/10 | 0 | 240 | 250 | 0.960000 |
| 22 | 2020/11 | 0 | 220 | 227 | 0.969163 |
| 23 | 2020/12 | 0 | 218 | 230 | 0.947826 |
| 24 | 2021/01 | 0 | 238 | 251 | 0.948207 |
| 25 | 2021/02 | 0 | 56 | 57 | 0.982456 |
pinfen_num.groupby(["評分"]).size()
評分
0 26
1 26
2 26
3 26
4 26
5 26
6 26
7 26
8 26
9 26
10 26
dtype: int64
pinfen_num.groupby(["評分"]).count()
| 付款年月 | user_num | order_num | 參與度 | |
|---|---|---|---|---|
| 評分 | ||||
| 0 | 26 | 26 | 26 | 26 |
| 1 | 26 | 26 | 26 | 26 |
| 2 | 26 | 26 | 26 | 26 |
| 3 | 26 | 26 | 26 | 26 |
| 4 | 26 | 26 | 26 | 26 |
| 5 | 26 | 26 | 26 | 26 |
| 6 | 26 | 26 | 26 | 26 |
| 7 | 26 | 26 | 26 | 26 |
| 8 | 26 | 26 | 26 | 26 |
| 9 | 26 | 26 | 26 | 26 |
| 10 | 26 | 26 | 26 | 26 |
# 每個評分最小參與度
pinfen_num["參與度"].groupby(pinfen_num["評分"]).min()
pinfen_num.groupby(pinfen_num["評分"]).min()["參與度"]
pinfen_num.groupby(pinfen_num["評分"])["參與度"].min()
評分
0 0.926357
1 0.923695
2 0.917749
3 0.925620
4 0.912000
5 0.934694
6 0.916318
7 0.929961
8 0.929577
9 0.940092
10 0.928571
Name: 參與度, dtype: float64
# 按評分選取多列
# pinfen_num.groupby(pinfen_num["評分"])[["參與度","order_num"]].min()
pinfen_num[["參與度","order_num"]].groupby(pinfen_num["評分"]).min()
| 參與度 | order_num | |
|---|---|---|
| 評分 | ||
| 0 | 0.926357 | 57 |
| 1 | 0.923695 | 47 |
| 2 | 0.917749 | 67 |
| 3 | 0.925620 | 61 |
| 4 | 0.912000 | 62 |
| 5 | 0.934694 | 68 |
| 6 | 0.916318 | 52 |
| 7 | 0.929961 | 62 |
| 8 | 0.929577 | 65 |
| 9 | 0.940092 | 48 |
| 10 | 0.928571 | 66 |
# 多維分組用unstack可以解成二維表(不堆疊)
a=pinfen_num["參與度"].groupby([pinfen_num["付款年月"],pinfen_num["評分"]]).min().unstack()
a
# a.shape
| 評分 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 付款年月 | |||||||||||
| 2019/01 | 0.954357 | 0.950000 | 0.971074 | 0.966805 | 0.967871 | 0.976378 | 0.937500 | 0.961538 | 0.935743 | 0.950617 | 0.957806 |
| 2019/02 | 0.967136 | 0.938967 | 0.972477 | 0.952000 | 0.946341 | 0.943925 | 0.931373 | 0.944444 | 0.934579 | 0.954545 | 0.944954 |
| 2019/03 | 0.926357 | 0.945148 | 0.928000 | 0.958716 | 0.939759 | 0.940928 | 0.966805 | 0.930612 | 0.956349 | 0.965368 | 0.937799 |
| 2019/04 | 0.967593 | 0.966825 | 0.970443 | 0.946903 | 0.925581 | 0.978632 | 0.990741 | 0.949580 | 0.977679 | 0.969957 | 0.944700 |
| 2019/05 | 0.955157 | 0.953975 | 0.946903 | 0.947154 | 0.974576 | 0.952174 | 0.957627 | 0.949153 | 0.962617 | 0.944206 | 0.970588 |
| 2019/06 | 0.943478 | 0.923695 | 0.948598 | 0.936937 | 0.948819 | 0.958159 | 0.957031 | 0.965217 | 0.953390 | 0.951327 | 0.945378 |
| 2019/07 | 0.958159 | 0.950413 | 0.968889 | 0.942222 | 0.950000 | 0.954167 | 0.957806 | 0.935849 | 0.948339 | 0.957983 | 0.977876 |
| 2019/08 | 0.972973 | 0.937500 | 0.945148 | 0.956897 | 0.973684 | 0.968037 | 0.921569 | 0.970711 | 0.961702 | 0.948936 | 0.958506 |
| 2019/09 | 0.934498 | 0.977876 | 0.948837 | 0.959276 | 0.972851 | 0.973913 | 0.970732 | 0.950893 | 0.954751 | 0.946565 | 0.970588 |
| 2019/10 | 0.950820 | 0.969957 | 0.982456 | 0.951965 | 0.927419 | 0.944882 | 0.954128 | 0.960526 | 0.946429 | 0.950207 | 0.967611 |
| 2019/11 | 0.957447 | 0.949580 | 0.982759 | 0.960870 | 0.942308 | 0.965517 | 0.965517 | 0.960159 | 0.934694 | 0.964467 | 0.965217 |
| 2019/12 | 0.967611 | 0.940639 | 0.948936 | 0.937238 | 0.967078 | 0.940594 | 0.958678 | 0.929961 | 0.946667 | 0.961702 | 0.930736 |
| 2020/01 | 0.944251 | 0.945607 | 0.961864 | 0.977876 | 0.943478 | 0.976959 | 0.941909 | 0.965517 | 0.951020 | 0.948207 | 0.928571 |
| 2020/02 | 0.935000 | 0.967890 | 0.963134 | 0.963115 | 0.973333 | 0.944444 | 0.972973 | 0.934783 | 0.969432 | 0.948498 | 0.967136 |
| 2020/03 | 0.960870 | 0.985915 | 0.941423 | 0.943478 | 0.941441 | 0.977578 | 0.973913 | 0.963636 | 0.946860 | 0.964844 | 0.955157 |
| 2020/04 | 0.939130 | 0.927203 | 0.938224 | 0.931452 | 0.952174 | 0.945946 | 0.961702 | 0.959184 | 0.945525 | 0.940092 | 0.942387 |
| 2020/05 | 0.964758 | 0.935484 | 0.917749 | 0.944664 | 0.978070 | 0.959276 | 0.933333 | 0.963855 | 0.954717 | 0.963563 | 0.974684 |
| 2020/06 | 0.949541 | 0.959091 | 0.952790 | 0.925620 | 0.951542 | 0.963855 | 0.953052 | 0.964126 | 0.958159 | 0.962617 | 0.941909 |
| 2020/07 | 0.965517 | 0.966245 | 0.949219 | 0.951754 | 0.912000 | 0.945701 | 0.924528 | 0.955645 | 0.967442 | 0.942748 | 0.942652 |
| 2020/08 | 0.956000 | 0.939759 | 0.926087 | 0.990610 | 0.931034 | 0.944206 | 0.916318 | 0.954733 | 0.976378 | 0.961373 | 0.966038 |
| 2020/09 | 0.961373 | 0.960000 | 0.958904 | 0.956332 | 0.944915 | 0.980952 | 0.935345 | 0.937238 | 0.960000 | 0.955357 | 0.945607 |
| 2020/10 | 0.960000 | 0.935345 | 0.943231 | 0.952381 | 0.926740 | 0.941704 | 0.964912 | 0.936000 | 0.931452 | 0.954751 | 0.956140 |
| 2020/11 | 0.969163 | 0.968872 | 0.952381 | 0.946667 | 0.918919 | 0.960159 | 0.942222 | 0.964758 | 0.929577 | 0.955157 | 0.947581 |
| 2020/12 | 0.947826 | 0.951542 | 0.957983 | 0.946667 | 0.967593 | 0.951754 | 0.948113 | 0.958175 | 0.951754 | 0.948718 | 0.959016 |
| 2021/01 | 0.948207 | 0.936364 | 0.961207 | 0.931727 | 0.950192 | 0.934694 | 0.967078 | 0.956693 | 0.961089 | 0.957447 | 0.944444 |
| 2021/02 | 0.982456 | 0.978723 | 0.970149 | 0.983607 | 1.000000 | 0.970588 | 1.000000 | 1.000000 | 0.969231 | 1.000000 | 0.984848 |
#多維分組堆疊成多索引
b=pinfen_num["參與度"].groupby([pinfen_num["付款年月"],pinfen_num["評分"]]).min()
b
# b.shape
付款年月 評分
2019/01 0 0.954357
1 0.950000
2 0.971074
3 0.966805
4 0.967871
...
2021/02 6 1.000000
7 1.000000
8 0.969231
9 1.000000
10 0.984848
Name: 參與度, Length: 286, dtype: float64
pinfen_num["參與度"].groupby(pinfen_num["評分"]).describe().unstack()
評分
count 0 26.000000
1 26.000000
2 26.000000
3 26.000000
4 26.000000
...
max 6 1.000000
7 1.000000
8 0.977679
9 1.000000
10 0.984848
Length: 88, dtype: float64
pinfen_num["參與度"].groupby(pinfen_num["評分"]).describe()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| 評分 | ||||||||
| 0 | 26.0 | 0.955372 | 0.013188 | 0.926357 | 0.947921 | 0.956723 | 0.965327 | 0.982456 |
| 1 | 26.0 | 0.952408 | 0.016614 | 0.923695 | 0.939165 | 0.950207 | 0.966680 | 0.985915 |
| 2 | 26.0 | 0.954187 | 0.016478 | 0.917749 | 0.945586 | 0.952585 | 0.967450 | 0.982759 |
| 3 | 26.0 | 0.952420 | 0.015517 | 0.925620 | 0.943775 | 0.951860 | 0.959136 | 0.990610 |
| 4 | 26.0 | 0.951066 | 0.021123 | 0.912000 | 0.940180 | 0.949409 | 0.967802 | 1.000000 |
| 5 | 26.0 | 0.957505 | 0.014377 | 0.934694 | 0.944554 | 0.956163 | 0.969950 | 0.980952 |
| 6 | 26.0 | 0.954035 | 0.020423 | 0.916318 | 0.938602 | 0.957329 | 0.966483 | 1.000000 |
| 7 | 26.0 | 0.954730 | 0.015248 | 0.929961 | 0.945621 | 0.957434 | 0.963801 | 1.000000 |
| 8 | 26.0 | 0.953291 | 0.013283 | 0.929577 | 0.946488 | 0.954053 | 0.961549 | 0.977679 |
| 9 | 26.0 | 0.956510 | 0.011825 | 0.940092 | 0.948773 | 0.954954 | 0.962388 | 1.000000 |
| 10 | 26.0 | 0.954920 | 0.014803 | 0.928571 | 0.944508 | 0.955649 | 0.966862 | 0.984848 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/266679.html
標籤:python
