本文是對單車案例的一個(gè)總結(jié)毁习,主要是根據(jù)用戶的消費(fèi)記錄,深入分析用戶消費(fèi)行為寒随,建立RFM模型糠悯,進(jìn)行用戶分層,發(fā)掘高價(jià)值用戶并進(jìn)行針對性管理和維護(hù)妻往,實(shí)現(xiàn)用戶運(yùn)營精細(xì)化互艾。
目錄
- 分析背景
- 分析思路
- 分析過程
- 總結(jié)
一、分析背景
數(shù)據(jù)來源于網(wǎng)上蒲讯,是用戶在一家單車網(wǎng)上18個(gè)月的消費(fèi)記錄
二忘朝、分析思路
本文為可視化查看方便,使用Matplotlib來畫圖呈現(xiàn)判帮,也可以導(dǎo)出數(shù)據(jù)為csv局嘁,然后使用powerBI等BI工具畫圖查看
三溉箕、分析過程
1. 數(shù)據(jù)的導(dǎo)入觀察與清洗
- 數(shù)據(jù)導(dǎo)入和觀察
import numpy as np
import pandas as pd
import datetime
df = pd.read_csv('bicycle_master.txt')
df.head()
df.info()
從上面可以看出數(shù)據(jù)
- 數(shù)據(jù)以table鍵作為分隔符,需要進(jìn)行分隔符的解析
可以使用pd.read_csv中的參數(shù)進(jìn)行解析悦昵,sep='\s+'肴茄,可以將table鍵和多個(gè)空格當(dāng)成一樣的分隔符 - 數(shù)據(jù)是沒有列名的,需要提前指定列名稱
使用 pd.read_csv()中names可以在讀取時(shí)指定列名稱 - 日期列需要解析為日期格式
可以使用pd.read_csv()中的parse_dates 解析指定的列為日期格式 - 需要提取月的信息但指,后續(xù)需要By月進(jìn)行統(tǒng)計(jì)
月信息提取有兩種方法:df.astype() 強(qiáng)制類型轉(zhuǎn)換和pd.to_datetime 指定類型日期轉(zhuǎn)換兩種方式寡痰,這里使用的df.astype()進(jìn)行強(qiáng)制轉(zhuǎn)換,datetime64[M],表示轉(zhuǎn)換為每月的第一天了棋凳,同理設(shè)置為[Y]就是每年的1月1日
重新讀取如下
cols = ['user_id','date','quantity','money']
df = pd.read_table('bicycle_master.txt', sep='\s+', names=cols, parse_dates=[1])
df['month'] = df.date.values.astype('datetime64[M]')
df['year'] = df.date.values.astype('datetime64[Y]')
df.head()
df.info()
pd.to_datetime()應(yīng)用如下
- 查看數(shù)據(jù)的缺失值和統(tǒng)計(jì)性描述情況
df.isnull().sum()
df.describe()
- 數(shù)據(jù)中沒有缺失值
- 可以看出大部分訂單只購買了少量商品(均值2.4, 3/4分位數(shù)是3)拦坠,有個(gè)別極值的干擾
- 用戶的消費(fèi)金額較穩(wěn)定,平均消費(fèi)約35剩岳,75%用戶消費(fèi)43.7元贞滨,中位數(shù)25
2. 用戶消費(fèi)趨勢的分析(按月)
1) 每月的消費(fèi)總金額,對消費(fèi)金額求和:np.sum()
2)每月的產(chǎn)品購買數(shù)量拍棕,對消費(fèi)數(shù)量求和:np.sum()
3)每月的訂單量晓铆,對訂單進(jìn)行計(jì)數(shù):pd.count()
4) 每月的消費(fèi)人數(shù),對user_id進(jìn)行去重后計(jì)數(shù)绰播,pd.nunique()
5) 每月的用戶的平均消費(fèi)金額骄噪,對消費(fèi)金額的取平均,np.mean()
df_month = df.groupby('month').agg({'user_id':'nunique', 'date':'count', 'quantity':np.sum, 'money':[np.sum, np.mean]})
df_month
可以看出蠢箩,列名稱需要重新命名链蕊,且要重置索引
df_month.columns = ['user_count','order_count','quantity_sum','monetary_sum','monetary_mean']
df_month.reset_index(inplace=True)
df_month
6) 每月用戶平均消費(fèi)次數(shù)的趨勢,每月的訂單數(shù)/每月用戶人數(shù)
df_month['order_avguser'] = df_month['order_count'] / df_month['user_count']
df_month
7)畫圖忙芒,可視化查看
- 月消費(fèi)總金額和月購買產(chǎn)品數(shù)量趨勢
# 畫圖展示
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = df_month['month']
y = df_month['monetary_sum']
y2 = df_month['quantity_sum']
plt.plot(x, y, 'm')
plt.ylabel('消費(fèi)金額')
plt.title('月消費(fèi)總金額趨勢')
plt.legend()
plt.plot(x, y2)
plt.ylabel('產(chǎn)品購買數(shù)量')
plt.title('月產(chǎn)品購買數(shù)量')
plt.legend()
從月消費(fèi)總金額和購買數(shù)量查看示弓,在前三個(gè)月呈上升趨勢讳侨,第三個(gè)月達(dá)到頂峰呵萨,后續(xù)每月消費(fèi)逐漸穩(wěn)定,趨于輕微下降趨勢
- 月消費(fèi)人數(shù)和訂單數(shù)查看
# 月消費(fèi)人數(shù)和訂單數(shù)
x = df_month['month']
y3= df_month['user_count']
y4 = df_month['order_count']
plt.plot(x, y3, 'y', label='消費(fèi)人數(shù)')
plt.plot(x, y4, 'c', label='訂單數(shù)')
plt.legend()
- 前三個(gè)月的消費(fèi)人數(shù)和訂單數(shù)呈上升趨勢跨跨,前三個(gè)月的訂單數(shù)在1000左右潮峦,第三個(gè)月達(dá)到頂峰,后續(xù)趨于平穩(wěn)勇婴,呈輕微下降趨勢忱嘹,在2500左右
- 每月消費(fèi)人數(shù)低于訂單數(shù),但差異不大
- 前三個(gè)月每月的消費(fèi)人數(shù)在8000-10000之間耕渴,后續(xù)每月平均消費(fèi)人數(shù)2000不到
- 平均消費(fèi)金額和消費(fèi)次數(shù)查看
x = df_month['month']
y5 = df_month['monetary_mean']
y6 = df_month['order_avguser']
plt.subplot(2,1,1)
plt.plot(x, y5, 'r', label='平均消費(fèi)金額')
plt.legend()
plt.subplot(2,1,2)
plt.plot(x, y6, 'b', label='平均訂單數(shù)')
plt.legend()
- 用戶的平均消費(fèi)金額呈波動(dòng)上升在11月達(dá)到頂峰后拘悦,趨于波動(dòng)下降,整體差異不大橱脸,在33-41之間
- 平均訂單數(shù)在前三月從1.1上升到1.3础米,后續(xù)趨于穩(wěn)定在1.3-1.4區(qū)間內(nèi)
3. 用戶個(gè)體分析
1) 用戶消費(fèi)金額和消費(fèi)數(shù)量的分布
2) 用戶消費(fèi)金額的分布分苇,按消費(fèi)金額區(qū)間統(tǒng)計(jì)用戶人數(shù)
3) 用戶消費(fèi)次數(shù)的分布,按消費(fèi)次數(shù)分布區(qū)間統(tǒng)計(jì)用戶人數(shù)
4) 用戶累計(jì)消費(fèi)金額占比屁桑,百分之多少的用戶貢獻(xiàn)了百分之多少的消費(fèi)金額
1) 用戶消費(fèi)金額和消費(fèi)數(shù)量的分布
df_user = df.groupby('user_id').agg({'quantity':np.sum,'money':np.sum, 'date':'count'}).reset_index()
df_user.rename(columns={'date':'order_count'}, inplace=True)
df_user.describe()
- 可以看出消費(fèi)數(shù)量quantity消費(fèi)數(shù)量和消費(fèi)金額Money 的平均值是16,240医寿,中位數(shù)是3,43;說明有個(gè)別用戶購買了數(shù)量較多的產(chǎn)品
- 散點(diǎn)圖查看
df_user.plot.scatter('money', 'quantity', alpha=0.5, title='用戶的消費(fèi)金額和消費(fèi)數(shù)量')
從上圖中看出受極值的影響蘑斧,大部分的用戶購買金額在4000以下靖秩,有12位用戶累計(jì)購金額大于4000
df_user1 = df_user[df_user['money'] < 4000]
plt.scatter(df_user1['money'],df_user1['quantity'], c='c', alpha=0.5)
plt.title('用戶的消費(fèi)金額和消費(fèi)數(shù)量散點(diǎn)圖')
plt.xlabel('消費(fèi)金額')
plt.ylabel('消費(fèi)數(shù)量')
plt.legend()
2) 用戶消費(fèi)金額的分布,按消費(fèi)金額區(qū)間統(tǒng)計(jì)用戶人數(shù)
將用戶消費(fèi)金額分組統(tǒng)計(jì)竖瘾,查看在哪個(gè)區(qū)間內(nèi)用戶人數(shù)最多沟突;使用pd.cut可以自動(dòng)將數(shù)據(jù)劃分區(qū)間或者按指定的數(shù)據(jù)列表進(jìn)行劃分,pd.cut劃分時(shí)每組包含右邊的數(shù)值捕传,不包含左邊的數(shù)值
bi = [x for x in range(0, int(df_user.money.max())+50, 50)]
df_user['money_b'] = pd.cut(df_user['money'], bins=bi, labels=bi[1:])
df_user.head()
可以看出在對用戶消費(fèi)金額分組后事扭,金額標(biāo)簽一欄存在缺失值,因?yàn)槲覀兊膭澐质菑?開始的乐横,pd.cut劃分含右不含左求橄,所以0的數(shù)值會(huì)顯示為缺失
金額為0 的數(shù)據(jù)是一些異常數(shù)據(jù),可以刪除這些數(shù)據(jù)后再進(jìn)行統(tǒng)計(jì)
df_user_2 = df_user.dropna(axis=0)
df_user_22 = df_user_2.groupby('money_b').agg({'user_id':'count'}).reset_index()
df_user_22['money_label'] = df_user_22.money_b.astype('int')
df_user_22.sort_values('money_label',inplace=True)
df_user_22.plot.line('money_label','user_id')
- 直接使用分組后的數(shù)據(jù)畫圖時(shí)葡公,bar圖無法直觀呈現(xiàn)罐农,暫時(shí)使用折線圖,可以出當(dāng)消費(fèi)金額大于2000時(shí)催什,用戶人數(shù)趨向0涵亏,有極值干擾。
- 使用切比雪夫定理過濾掉極值的影響
切比雪夫定理
- 所有數(shù)據(jù)中蒲凶,至少有3/4(或75%)的數(shù)據(jù)位于[平均數(shù)]2個(gè)標(biāo)準(zhǔn)差范圍內(nèi)气筋。
- 所有數(shù)據(jù)中,至少有8/9(或88.9%)的數(shù)據(jù)位于平均數(shù)3個(gè)標(biāo)準(zhǔn)差范圍內(nèi)旋圆。
-
所有數(shù)據(jù)中宠默,至少有24/25(或96%)的數(shù)據(jù)位于平均數(shù)5個(gè)標(biāo)準(zhǔn)差范圍內(nèi) [2] 。
金額平均值的5個(gè)標(biāo)準(zhǔn)差:(mean = 106 ,std = 241)106+5*241=1311
21.PNG
過濾前:280組數(shù)據(jù)灵巧,過濾后只有27組數(shù)據(jù)
#畫圖
x = df_user_23['money_label']
y = df_user_23['user_id']
x1 = range(len(df_user_23['money_label']))
plt.bar(x1, y, width=0.8)
plt.xlabel('消費(fèi)金額')
plt.ylabel('用戶量')
plt.xticks(x1,df_user_23['money_label'], fontsize=9, rotation=90)
plt.title('用戶消費(fèi)金額頻次')
從直方圖可知搀矫,用戶消費(fèi)金額,絕大部分呈現(xiàn)集中趨勢
3) 用戶消費(fèi)次數(shù)的分布刻肄,按消費(fèi)次數(shù)分布區(qū)間統(tǒng)計(jì)用戶人數(shù)
按照如上同樣的方法進(jìn)行劃分小組瓤球,再分組統(tǒng)計(jì)
order_count_label = [x for x in range(0, int((df_user['order_count']).max()+1))]
df_user['order_count_label'] = pd.cut(df_user['order_count'],bins=order_count_label, labels=order_count_label[1:])
#分組后的標(biāo)簽為目錄格式,需要轉(zhuǎn)換為整型數(shù)據(jù)敏弃,以便后面畫圖使用
df_user['order_count_label'] = df_user.order_count_label.astype('int')
#按消費(fèi)次數(shù)分組求和
df_user_order = df_user.groupby('order_count_label').agg({'user_id':'count'}).reset_index().sort_values('order_count_label')
df_user_order.rename(columns={'user_id':'user_count'}, inplace=True)
df_user_order.plot.line('order_count_label','user_count')
從上圖可以看出卦羡,消費(fèi)次數(shù)也受極值的影響,同樣使用切比雪夫定理進(jìn)行過濾。
# 3+5*5
df_user_order_28 = df_user_order[df_user_order['order_count_label']<28]
x = range(len(df_user_order_28['order_count_label']))
x1 = df_user_order_28['order_count_label']
y = df_user_order_28['user_count']
# 畫直方圖時(shí)注意绿饵,可以先使用區(qū)間的長度按順序來畫圖逝薪,再使用區(qū)間的標(biāo)簽映射x軸原先使用的順序
plt.bar(x, y, width=0.8, color='c')
plt.xlabel('消費(fèi)次數(shù)')
plt.ylabel('用戶量')
plt.xticks(x, x1)
plt.title('用戶消費(fèi)次數(shù)頻次')
plt.legend()
可以看出用戶消費(fèi)次數(shù)集中在一次
4) 用戶累計(jì)消費(fèi)金額曲線
累計(jì)消費(fèi)金額可以使用pd.consum()來計(jì)算
# 累計(jì)消費(fèi)金額曲線
df_user_cumsum_money = df_user.sort_values('money').money.cumsum() / df_user.sort_values('money').money.sum()
# 重置索引,和重命名列
df_user_cumsum_money = df_user_cumsum_money.reset_index().rename(columns={'index':'user_id','money':'money'})
# 畫圖準(zhǔn)備
y3 = df_user_cumsum_money['money']
x3 = range(df_user_cumsum_money.shape[0])
# 求中位數(shù)
y4 = [np.median(y3) for x in x3]
plt.plot(x3, y3, 'm')
plt.plot(x3, y4, 'r--', label='人數(shù)中位數(shù)')
plt.xlabel('消費(fèi)人次')
plt.ylabel('消費(fèi)金額占比')
plt.title('累積消費(fèi)金額曲線')
plt.legend()
由上可以看出蝴罪,按照用戶消費(fèi)金額進(jìn)行升序排序董济,50%的用戶僅貢獻(xiàn)10%的消費(fèi)金額,15%的用戶貢獻(xiàn)了60%的消費(fèi)金額
4. 用戶消費(fèi)行為
- 首購時(shí)間分布
- 最后一次購買時(shí)間分布
- 每月新客占比
- 用戶RFM模型分層
- 新要门、老虏肾、活躍、流失欢搜、回流用戶分布情況
- 用戶購買周期分布封豪,按兩次購買時(shí)間的間隔
- 用戶生命周期分布,首購和最后一次購買消費(fèi)時(shí)間
1)用戶首購分布
即每個(gè)用戶購買時(shí)間的最小值,再根據(jù)時(shí)間統(tǒng)計(jì)用戶人數(shù)炒瘟,除了使用分組統(tǒng)計(jì)groupby外吹埠,series的df.date.value_counts()也可以實(shí)現(xiàn)統(tǒng)計(jì)每個(gè)日期的人數(shù)
# 用戶首購
df_user_a = df.groupby('user_id').agg({'date':np.min}).date.value_counts().reset_index()
df_user_a = df_user_a.rename(columns={'index':'first_date', 'date':'user_count'})
print(df_user_a.head())
df_user_a.plot.line('first_date','user_count', title='用戶首購分布')
可以看到用戶的首購人數(shù)在1月到2月初是上升的,從2月中旬后就開始逐漸下降
2) 用戶最后一次消費(fèi)疮装,方法同上
# 用戶最后一次消費(fèi)
df_user_b = df.groupby('user_id').agg({'date':np.max}).date.value_counts().reset_index()
df_user_b.rename(columns={'index':'last_date','date':'user_count'}, inplace=True)
print(df_user_b.head())
df_user_b.plot.line('last_date', 'user_count', title='用戶最后一次購買分布')
- 在3月份后缘琅,用戶最后一次購買分布斷崖式下跌,可以理解用戶流失也在3月份斷崖式下跌廓推;
- 一開始用戶迅猛增長數(shù)量比較多流失的也比較多刷袍,后面沒有用戶 用戶最后一次購買的分布比第一次分布廣
- 大部分最后一次購買,集中在前三個(gè)月樊展,說明很多用戶購買了一次后就不再進(jìn)行購買
- 隨著時(shí)間的遞增呻纹,最后一次購買數(shù)量也在遞增,消費(fèi)呈現(xiàn)流失上升的狀況(這也是正常专缠,隨著時(shí)間的增長,可能運(yùn)營沒跟上哥力,或者用戶忠誠度下降了)
3)統(tǒng)計(jì)多少用戶僅消費(fèi)一次嘁圈,新老客戶的占比
# 查看多少用戶只購買了一次
df_user_d = df.groupby('user_id').date.agg([np.min, np.max])
df_user_d2 = (df_user_d['amax'] == df_user_d['amin']).value_counts()
df_user_d2
- 將近一半的用戶僅購買了一次
df_user_d2 = df_user_d2.reset_index()
df_user_d2.columns = ['客戶', 'count']
df_user_d2.replace({'客戶':{0:'老客戶',1:'新客戶'}}, inplace=True)
df_user_d2.set_index('客戶',inplace=True)
df_user_d2.plot.pie('count',autopct='%.1f%%', radius = 1, startangle = 90, title='新老客戶消費(fèi)比', label='客戶')
- 每月的新客比
按月分組下的userid分組,求每月的最早購買日期和最晚消費(fèi)日期
# 每月新客比,只在每個(gè)月內(nèi)看用戶是否復(fù)購,如果沒有復(fù)購怠惶,算作新客
df_user_e = df.groupby(['month','user_id']).date.agg([np.min, np.max])
# 當(dāng)首購時(shí)間和末購時(shí)間相同則為新客戶
df_user_e['new'] = (df_user_e['amin'] == df_user_e['amax'])
df_user_e = df_user_e.reset_index().groupby('month').new.value_counts()
df_user_e2 = pd.DataFrame(df_user_e)
# 多重索引的處理,先提取原來的索引數(shù)據(jù)涨缚,再刪除掉多重索引
df_user_e2['flag'] = df_user_e2.index.droplevel('month')
df_user_e2['month1'] = df_user_e2.index.droplevel('new')
df_user_e2.index = df_user_e2.index.droplevel()
df_user_e3 = df_user_e2.reset_index(drop=True)
#標(biāo)簽的映射
df_user_e3['客戶'] = df_user_e3.flag.map({True:'新客戶', False:'老客戶'})
# 數(shù)據(jù)合并,將新客戶和老客戶分列展示
df_user_e4 = pd.merge(df_user_e3[df_user_e3['客戶'] == '新客戶'], df_user_e3[df_user_e3['客戶'] == '老客戶'],how='outer',on='month1')
# 計(jì)算新客比
df_user_e4['新客比'] = df_user_e4.apply(lambda x: x.new_x/(x.new_x+x.new_y), axis=1)
# 畫圖
df_user_e4.plot.line(x='month1', y='新客比', title='每月新客比')
柱狀圖呈現(xiàn)每月新客比
import matplotlib.pyplot as plt
%matplotlib inline
x = df_user_e4['month1'].astype('str')
x1 = np.arange(len(x))
y1 = df_user_e4['new_x']
y2 = df_user_e4['new_y']
plt.bar(x1, y1, color='m', width=0.25, label='新客戶')
plt.bar(x1+0.25, y2, color='b', width=0.25, label='老客戶')
plt.xticks(x1, x, rotation=-45)
plt.legend()
可以看到前三個(gè)月新客占比大于0.85脓魏,后面每個(gè)月新客占比趨于穩(wěn)定茂翔,說明大部分用戶每個(gè)月只購買一次
4)RFM模型
# 用戶分層:RFM珊燎,R最后一次消費(fèi)悔政,F(xiàn):消費(fèi)次數(shù)谋国,購買數(shù)量芦瘾,M:消費(fèi)金額
rfm = df.groupby('user_id').agg({'date':np.max, 'quantity':np.sum, 'money':np.sum}).reset_index()
#最近一次消費(fèi)時(shí)間距最近的天數(shù)近弟,因案例中的時(shí)間過于遠(yuǎn),所以采用案例中的最大時(shí)間做參考
import datetime
# -(rfm['date'] -np.datetime64('today','D')) / np.timedelta64(1, 'D')
rfm['R'] = (rfm['date'] -rfm.date.max()) / np.timedelta64(1, 'D')
# 列名重命名
rfm = rfm.rename(columns={'quantity':'F','money':'M'})
rfm.head()
# 歸一化,將RFM三個(gè)參數(shù)處理為中心化迫摔,零均值化句占,方便后續(xù)數(shù)據(jù)分層
rfm1 = rfm[['R','F','M']].apply(lambda x:x - x.mean())
rfm1.head()
# 標(biāo)簽映射
level = rfm1.applymap(lambda x :'1' if x >=0 else '0')
label = level.R + level.F + level.M
d = {
'111':'重要價(jià)值客戶',
'011':'重要喚回客戶',
'101':'重要深耕客戶',
'001':'重要挽留客戶',
'110':'潛力客戶',
'100':'新客戶',
'010':'一般維持客戶',
'000':'流失客戶'
}
rfm['level'] = label.map(d)
rfm['color'] = rfm.level.apply(lambda x :'重要價(jià)值客戶' if x=='重要價(jià)值客戶' else '非重要價(jià)值客戶')
rfm.head()
畫圖查看纱烘,主要查看重要價(jià)值客戶和非重要價(jià)值客戶的分布
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(rfm[rfm['color']=='重要價(jià)值客戶'].F, rfm[rfm['color']=='重要價(jià)值客戶'].R, c = 'c',label='重要', alpha=0.5)
plt.scatter(rfm[rfm['color']=='非重要價(jià)值客戶'].F, rfm[rfm['color']=='非重要價(jià)值客戶'].R, c = 'm',alpha=0.5,label='非重要')
plt.xlabel('F')
plt.ylabel('R')
plt.title('客戶標(biāo)簽分布')
plt.legend()
- 從RFM 分層可知,大部分用戶是重要保持客戶帆阳,但是這是由于極值的影響
- 所以 RFM 的劃分標(biāo)準(zhǔn)應(yīng)該以業(yè)務(wù)為準(zhǔn),也可以通過切比雪夫去除極值后求均值至扰,并且 RFM 的各個(gè)劃分標(biāo)準(zhǔn)可以都不一樣
5)新敢课、老直秆、活躍切厘、流失疫稿、回流用戶分布情況
按每個(gè)用戶每個(gè)月是否有消費(fèi)進(jìn)行判斷
# 用戶生命周期遗座, userid為索引途蒋,月為列号坡,求每月的消費(fèi)次數(shù)宽堆,這里缺失值使用0填充
pivot_count = pd.pivot_table(df, index='user_id', columns='month', values='date',
aggfunc='count').fillna(0)
# 有消費(fèi)為1畜隶,無消費(fèi)為0
df_purchase = pivot_count.applymap(lambda x:1 if x>0 else 0)
df_purchase.head()
判斷用戶分布狀況
- 新用戶:之前從沒購買籽慢,也就是之前月的購買次數(shù)為0
- 活躍用戶:這個(gè)周期和上個(gè)周期都購買了箱亿,且不是新客戶
- 回流用戶:上個(gè)周期沒買极景,這個(gè)周期買了盼樟,且不是新客戶
- 流失用戶:本周期沒有購買晨缴,且不是新客戶
- 未注冊:從未購買過
- return:回流
- new:新客
- unreg:未注冊
- active:活躍
- unactive:流失
def status_m(data):
status = []
for i in range(18):
if len(status) ==0:
if data[i] == 1:
status.append('new')
else:
status.append('unreg')
else:
if data[i] ==1:
if status[i-1] == 'unreg':
status.append('new')
elif status[i-1] == 'unactive':
status.append('return')
else:
status.append('active')
else:
if status[i-1] == 'unreg':
status.append('unreg')
else:
status.append('unactive')
return status
p1 = df_purchase.apply(status_m, axis=1)
#將未注冊的用戶使用np.nan替換,從未購買稍途,這樣count計(jì)算人數(shù)時(shí)不會(huì)計(jì)算在內(nèi)
p2 = p1.replace('unreg',np.nan).apply(lambda x :pd.value_counts(x))
#填充np.nan為0械拍,方便后續(xù)畫圖
p_p= p2.fillna(0).T
p_p.head()
畫圖查看用戶分布情況
from matplotlib import pyplot as plt
label = ['active','new','return','unactive']
y1 = p_p.active
y2 = p_p.new
y3 = p_p['return']
y4 = p_p.unactive
plt.stackplot(p_p.index, y1, y2,y3, y4, labels=['active','new','return','unactive'],alpha=0.4)
plt.title('用戶分層')
plt.legend()
可以看出前三個(gè)月新增用戶和回流用戶達(dá)到最大,3月之后幾乎無新客戶迄损,回流用戶和活躍用處于基本穩(wěn)定的狀態(tài)芹敌,不活躍用戶3月份后占了大部分
- 計(jì)算用戶分布的占比
# 求出所有用戶的占比
p2.fillna(0).apply(lambda x : x/x.sum(), axis=0).T.head()
- 前三個(gè)月有大量新用戶涌入氏捞,新用戶占比很高幌衣,而后面幾個(gè)月不活躍用戶占比非常高豁护,普遍在90%以上楚里。
- 活躍用戶班缎,持續(xù)消費(fèi)的用戶达址,對應(yīng)的是消費(fèi)運(yùn)營的質(zhì)量沉唠。
- 回流用戶满葛,之前不消費(fèi)嘀韧,本月才消費(fèi)锄贷,對應(yīng)的是喚回運(yùn)營肃叶。
- 不活躍用戶因惭,對應(yīng)的是流失
6)用戶購買周期的分布
# 用戶購買周期
order_diff = df.groupby('user_id').apply(lambda x :x.date - x.date.shift())
# 將間隔日期轉(zhuǎn)換為數(shù)值
order_diff_i = order_diff / np.timedelta64(1,'D')
order_diff_i.describe()
# count 46089.000000
# mean 68.973768
# std 91.033032
# min 0.000000
# 25% 10.000000
# 50% 31.000000
# 75% 89.000000
# max 533.000000
# Name: date, dtype: float64
# 將間隔日期分組切割
ls = [i for i in range(0, int(order_diff_i.max())+10,10)]
order_diff_i1 = pd.cut(order_diff_i, bins=ls, labels=ls[1:]).fillna(10)
# 按切割后的每組統(tǒng)計(jì)人數(shù)
order_diff_i2 = order_diff_i1.reset_index().groupby('date').agg({'date':'count'})
order_diff_i2.columns =['count']
# 重置索引和強(qiáng)制轉(zhuǎn)換日期天數(shù)目錄為整型
data = order_diff_i2.reset_index()
# 畫圖激率,因?yàn)閎ar畫圖在matplot呈現(xiàn)不直觀乒躺,使用折線圖
y = data['count']
x1 = list(data.date.astype(int))
plt.plot(x1, y)
plt.title('用戶購買周期')
plt.xlabel('天數(shù)')
plt.ylabel('人數(shù)')
plt.legend()
- 訂單周期呈指數(shù)分布
- 用戶的平均購買周期是68天
- 絕大部分用戶的購買周期都低于100天
7)用戶生命周期分布,第一次購買和最后一次購買的差值
# 用戶生命周期分布
user_l1 = df.groupby('user_id').agg({'date':[np.max, np.min]}).reset_index()
user_l1.columns = ['user_id', 'max_date', 'min_date']
# 時(shí)間格式轉(zhuǎn)為數(shù)字類型
user_l1['life'] = (user_l1['max_date'] - user_l1['min_date']) / np.timedelta64(1,'D')
user_l1['life'].describe()
# count 23570.000000
# mean 134.871956
# std 180.574109
# min 0.000000
# 25% 0.000000
# 50% 0.000000
# 75% 294.000000
# max 544.000000
# Name: life, dtype: float64
# 生命周期的均值為134,中位數(shù)為0
user_l1[user_l1['life'] >0 ].describe()
# user_id life
# count 11516.000000 11516.000000
# mean 11718.596301 276.044807
# std 6822.464439 166.633990
# min 3.000000 1.000000
# 25% 5747.750000 117.000000
# 50% 11778.000000 302.000000
# 75% 17632.500000 429.000000
# max 23570.000000 544.000000
# 過濾生命周期為0 的數(shù)據(jù)后讳推,平均購買周期為276天银觅,中位數(shù)302
# 列表生成周期的分布標(biāo)簽究驴,切割周期數(shù)據(jù)后再分組統(tǒng)計(jì)
ls = [i for i in range(0, int(user_l1.life.max()+10),10)]
# 填充缺失值為最小值
user_l1['lf_label'] = pd.cut(user_l1['life'], bins=ls, labels=ls[1:]).fillna(10)
# 分組統(tǒng)計(jì)
user_l2 = user_l1.groupby('lf_label').agg({'user_id':'count'}).reset_index()
# 周期的目錄格式轉(zhuǎn)為數(shù)字格式洒忧,方便畫圖統(tǒng)計(jì)
user_l2['lf_label'] = user_l2.lf_label.astype(int)
# 畫圖統(tǒng)計(jì)
plt.plot(user_l2['lf_label'], user_l2['user_id'])
plt.xlabel('天數(shù)')
plt.ylabel('人數(shù)')
plt.title('用戶生命周期')
plt.legend()
- 受只購買一次的用戶影響較大万皿,過濾生命周期為0 的數(shù)據(jù)后,平均購買周期為276天芝雪,中位數(shù)302
- 用戶人均生命周期134天惩系,中位數(shù)僅為0天
# 篩選用戶生命周期大于1次的數(shù)據(jù)
user_l3 = user_l2[user_l2['lf_label']>10]
plt.plot(user_l3['lf_label'], user_l3['user_id'])
plt.xlabel('天數(shù)')
plt.ylabel('人數(shù)')
plt.title('用戶生命周期')
plt.legend()
5. 復(fù)購率和回購率
復(fù)購率:自然月內(nèi)堡牡,購買多次的用戶占比(即晤柄,購買了兩次以上)
回購率:在本周期內(nèi)購買了且下個(gè)周期也購買了的用戶占本周期總用戶的比例芥颈,曾經(jīng)購買過的用戶在某一時(shí)期的再次購買的占比(可能是在三個(gè)月內(nèi))
1)回購率計(jì)算
# 回購率計(jì)算
df_um = pd.pivot_table(df, values='money', index='user_id', columns='month', aggfunc='count')
df_um.head()
回購率函數(shù)的定義:
本月有購買且下個(gè)月也有購買時(shí),則為回購:1
本月有購買下個(gè)月沒有購買盾计,未回購:0
本月沒有購買應(yīng)為空值:np.nan署辉,這樣計(jì)算時(shí)count不會(huì)計(jì)算在內(nèi)
最后一個(gè)月的回購應(yīng)為np.nan
# 是否回購的判斷
def huigou(data):
status = []
for i in range(len(data)-1):
if data[i] >= 1:
if data[i+1] >= 1:
status.append(1)
else:
status.append(0)
else:
status.append(np.nan)
status.append(np.nan)
return status
df_um1 = df_um.apply(lambda x :huigou(x), axis=1)
# 每月的回購率
# df_um1.sum() 每月回購人數(shù)統(tǒng)計(jì)
# df_um1.count() 每月購買總?cè)藬?shù)統(tǒng)計(jì)
hg = df_um1.sum()/df_um1.count()
# 重置索引&重命名列
hg1 = hg.reset_index()
hg1.columns = ['month','hg_rate']
hg1
# 回購率曲線繪制
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] =False
plt.plot(hg1['month'], hg1['hg_rate'])
plt.ylabel('回購率')
plt.title('回購曲線')
plt.legend()
2)復(fù)購率計(jì)算
# 每月購買的人數(shù):df_um.count()
# 每月復(fù)購的人數(shù):df_um.applymap(lambda x : 1 if x>1 else 0).sum()
df_fg = df_um.applymap(lambda x : 1 if x>1 else 0).sum() / df_um.count()
# 索引重置&列重命名
df_fg1 = df_fg.reset_index()
df_fg1.columns = ['month', 'fg_rate']
plt.plot(df_fg1['month'], df_fg1['fg_rate'])
plt.ylabel('復(fù)購率')
plt.title('復(fù)購率曲線')
plt.legend()
小結(jié):
- 回購率穩(wěn)定在30%左右炫乓,前三個(gè)月有大量新用戶涌入,導(dǎo)致回購率較低
- 復(fù)購率穩(wěn)定在20%左右
四创橄、總結(jié)
- 用戶消費(fèi)趨勢分析
- 前三個(gè)月有大量的新用戶涌入妥畏,消費(fèi)金額醉蚁、產(chǎn)品消費(fèi)數(shù)量
网棍、消費(fèi)人數(shù)滥玷、訂單量均達(dá)到最高峰惑畴,后續(xù)每個(gè)月較為穩(wěn)定且呈輕微下降趨勢 - 平均消費(fèi)金額呈波動(dòng)上升在11月達(dá)到頂峰后如贷,趨于波動(dòng)下降倒得,整體差異不大霞掺,在33-41之間
- 平均訂單數(shù)在前三月從1.1上升到1.3菩彬,后續(xù)趨于穩(wěn)定在1.3-1.4區(qū)間內(nèi)
- 用戶個(gè)體消費(fèi)
- 個(gè)別用戶購買了大量的產(chǎn)品骗灶,拉高了平均消費(fèi)金額耙旦,絕大部分的用戶購買金額在200以下免都,有12位用戶累計(jì)購金額大于4000绕娘;
- 絕大部分的用戶只消費(fèi)了一次
- 用戶累計(jì)消費(fèi)金額曲線看出险领,50%的用戶僅貢獻(xiàn)10%的消費(fèi)金額挨下,15%的用戶貢獻(xiàn)了60%的消費(fèi)金額复颈,符合二八法則
- 用戶消費(fèi)行為
- 首購和最后一次購買時(shí)間集中在前三個(gè)月,說明很多用戶購買一次后就不買了机杜,而且用戶最后一次的購買時(shí)間在隨時(shí)間上升椒拗,消費(fèi)呈現(xiàn)流失狀態(tài)
- 新老客戶看蚀苛,有一半的用戶僅消費(fèi)一次堵未;每月新客查看渗蟹,1月的新客占比達(dá)到90%以上雌芽,后續(xù)有所下降,3月后開始維持在81%左右
- RFM模型看屉佳,在8種客戶中忘古,重要價(jià)值客戶的消費(fèi)頻次和消費(fèi)金額最高髓堪,人數(shù)排在第二位
- 用戶分布情況來看干旁,新用戶從第4月份以后沒有新增争群;活躍用戶有所下降换薄;回流用戶數(shù)量趨于穩(wěn)定轻要,每月1000多冲泥。流失/不活躍用戶志秃,數(shù)量非常多浮还,基本上每月都在20000以上碑定。
- 用戶購買周期方面延刘,用戶的平均購買周期是68天碘赖,絕大部分用戶的購買周期都低于100天
- 用戶生命周期方面播掷,受只購買一次的用戶影響較大歧匈,排除這部分后件炉,平均購買周期為276天斟冕,中位數(shù)302
- 復(fù)購率和回購率方面,復(fù)購率穩(wěn)定在20%左右秀撇,回購率穩(wěn)定在30%左右捌袜,前3個(gè)月因?yàn)橛写罅啃掠脩粲咳耄@批用戶只購買了一次适肠,所以導(dǎo)致復(fù)購率和回購率都比較低。