分析過(guò)程--代碼集

該篇主要是對(duì)上篇分析報(bào)告的一個(gè)過(guò)程解讀。

總共有110段代碼:(以下操作均在IPython中進(jìn)行)

(部分重復(fù)代碼略、圖表的標(biāo)題、x軸、y軸、圖列等圖標(biāo)設(shè)置代碼略)

-------------------------------------

大綱:

引入及設(shè)置部分:1~6(6段)

Part 1 :7~28(22段)

Part 2 :29~45(17段)

Part 3 :46~61(16段)

Part 4 :62~106(45段)

? ? ????用戶生命周期?:62~73(12段)

????????用戶購(gòu)買周期?:74~79(6段)

????????回流率/流失率:80~99(20段)

????????復(fù)購(gòu)率/回購(gòu)率:100~106(7段)

Part 5 :補(bǔ)充4段,Part 1每月/日走勢(shì)圖


Let's go!



引入及設(shè)置部分


調(diào)用庫(kù)和一些設(shè)置

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: import matplotlib.pyplot as plt

In [4]: from datetime import datetime

In [5]: %matplotlib????????????????????? #使在交互軟件中可視化

Using matplotlib backend: Qt5Agg

In [6]:?plt.style.use('ggplot')????? ??#設(shè)置風(fēng)格


Part 1數(shù)據(jù)總覽


知識(shí)點(diǎn)及說(shuō)明:

①描述性分析、用戶的貢獻(xiàn)情況、消費(fèi)數(shù)量和金額關(guān)系

②groupby()函數(shù):變換維度統(tǒng)計(jì)用戶的情況,每天的銷售情況以及每月的銷售情況。

③info()函數(shù):查看數(shù)據(jù)的基本信息類型、是否有缺失值

④describe()函數(shù):查看數(shù)據(jù)的描述性分析(極值、均值、四分位等)

拿到一份csv文件格式的數(shù)據(jù),先打開看看有什么信息。該數(shù)據(jù)沒(méi)有表頭,字符串是空格分割,一共有四列數(shù)據(jù):用戶ID、購(gòu)買的日期、購(gòu)買的數(shù)量,購(gòu)買的金額。下面讀取數(shù)據(jù),并查看前五行以及統(tǒng)計(jì)描述、文件基本信息。

In [7]:columns=['user_id','order_dt','order_products','order_amount']

In [8]: df = pd.read_csv(r'C:\Users\55414\Desktop\CDNOW_master.txt',

names=columns,sep='\s+')

In [9]: df.head()

Out[9]:

??user_id? order_dt? order_products? order_amount

0???????1? 19970101?????????????? 1???????? 11.77

1???????2? 19970112?????????????? 1???????? 12.00

2???????2? 19970112?????????????? 5???????? 77.00

3???????3? 19970102?????????????? 2???????? 20.76

4???????3? 19970330?????????????? 2???????? 20.76

In [10]: df.describe()

Out[10]:

???????????user_id????? order_dt? order_products? order_amount

count?69659.000000? 6.965900e+04??? 69659.000000? 69659.000000

mean??11470.854592? 1.997228e+07??????? 2.410040???? 35.893648

std????6819.904848? 3.837735e+03??????? 2.333924???? 36.281942

min???????1.000000? 1.997010e+07??????? 1.000000????? 0.000000

25%????5506.000000? 1.997022e+07??????? 1.000000???? 14.490000

50%???11410.000000? 1.997042e+07??????? 2.000000???? 25.980000

75%???17273.000000? 1.997111e+07??????? 3.000000???? 43.700000

max???23570.000000? 1.998063e+07?????? 99.000000?? 1286.010000

In [11]: df.info()

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 69659 entries, 0 to 69658

Data columns (total 4 columns):

user_id?????????? 69659 non-null int64

order_dt????????? 69659 non-null int64

order_products??? 69659 non-null int64

order_amount????? 69659 non-null float64

dtypes: float64(1), int64(3)

memory usage: 2.1 MB

觀察以上數(shù)據(jù)發(fā)現(xiàn):

①總用戶數(shù)為23570人,總訂單數(shù)為69659個(gè),沒(méi)有缺失值。

②每個(gè)訂單平均有2.4個(gè)產(chǎn)品,35.89元。

③每個(gè)訂單的產(chǎn)品數(shù)標(biāo)準(zhǔn)差為2.33,說(shuō)明數(shù)據(jù)比較集中,用戶每次購(gòu)買的產(chǎn)品數(shù)量大多數(shù)在1~5個(gè)之間。

④訂單的產(chǎn)品數(shù)量的極差為98個(gè),上四分位為3個(gè),說(shuō)明訂單產(chǎn)品數(shù)量超過(guò)3個(gè)的最多占到總訂單數(shù)的25%。

⑤訂單的金額最小值為0,可能存在優(yōu)惠或減免的促銷活動(dòng);上四分位只有43.7元,說(shuō)明絕大部分的用戶為低消費(fèi)群體。符合二八原則期望。

⑥order_dt表示時(shí)間,但文件記錄它只是年月日組合的一串整型數(shù)字,沒(méi)有時(shí)間含義。


下面以用戶維度進(jìn)行轉(zhuǎn)換

In [12]: df_user =df.groupby('user_id').sum()

In [13]: df_user.describe()

Out[13]:

??????????order_dt? order_products? order_amount

count?2.357000e+04??? 23570.000000? 23570.000000

mean??5.902627e+07??????? 7.122656??? 106.080426

std???9.460684e+07?????? 16.983531??? 240.925195

min???1.997010e+07??????? 1.000000????? 0.000000

25%???1.997021e+07??????? 1.000000???? 19.970000

50%???1.997032e+07??????? 3.000000???? 43.395000

75%???5.992125e+07??????? 7.000000??? 106.475000

max???4.334408e+09???? 1033.000000? 13990.930000

In [14]: df_user.info()

<class 'pandas.core.frame.DataFrame'>

Int64Index: 23570 entries, 1 to 23570

Data columns (total 3 columns):

order_dt????????? 23570 non-null int64

order_products??? 23570 non-null int64

order_amount????? 23570 non-null float64

dtypes: float64(1), int64(2)

memory usage: 736.6 KB

從描述信息得知:

①總用戶數(shù)量為23570

②每個(gè)用戶平均消費(fèi)產(chǎn)品7個(gè),金額(客單價(jià))106.08元。

③每個(gè)用戶購(gòu)買產(chǎn)品數(shù)標(biāo)準(zhǔn)差為16.9個(gè),說(shuō)明數(shù)據(jù)的離散程度比較大。

④用戶所購(gòu)買產(chǎn)品數(shù)和金額的平均值和上四分位比較接近,而且極差相差懸殊,說(shuō)明存在小部分的高額消費(fèi)用戶。


消費(fèi)總金額可以對(duì)order_amount列進(jìn)行求和

In [15]: df.order_amount.sum()

Out[15]: 2500315.6300004106


其他不再贅述。不過(guò)再轉(zhuǎn)換為時(shí)間維度前,先把order_dt轉(zhuǎn)換為時(shí)間類型,如下:

In [16]: df['order_date'] =pd.to_datetime(df.order_dt,

format='%Y%m%d')? ? ?#新增一列,將日期整型轉(zhuǎn)日期格式

In[17]:df['month']=df.order_date.values.astype('datetime64[M]')?????????????? #新增一列,提取每個(gè)日期的月份

In [18]: df.head()

Out[18]:

??user_id? order_dt? order_products? order_amount order_date????? month

0???????1? 19970101?????????????? 1???????? 11.77 1997-01-01 1997-01-01

1???????2? 19970112?????????????? 1???????? 12.00 1997-01-12 1997-01-01

2???????2? 19970112?????????????? 5???????? 77.00 1997-01-12 1997-01-01

3???????3? 19970102?????????????? 2???????? 20.76 1997-01-02 1997-01-01

4???????3? 19970330?????????????? 2???????? 20.76 1997-03-30 1997-03-01


接下來(lái)看用戶的貢獻(xiàn)情況,分別描述用戶和消費(fèi)金額、訂單數(shù)量的關(guān)系。

In [19]: user_amount=df.groupby('user_id').order_amount.sum().sort_values().reset_index()

In [20]:user_amount['amount_cumsum']=user_amount.order_amount.cumsum()

In [21]: user_amount.head()

Out[21]:

??user_id? order_amount? amount_cumsum

0???10175?????? ????0.0??????????? 0.0

1????4559?????????? 0.0??????????? 0.0

2????1948?????????? 0.0??????????? 0.0

3?????925?????????? 0.0??????????? 0.0

4???10798?????????? 0.0??????????? 0.0

將消費(fèi)金額進(jìn)行求和并排序,再累加。查看前5行,發(fā)現(xiàn)都是0.0,說(shuō)明可能是存在大力的促銷減免活動(dòng)

(這可能是前三個(gè)月用戶數(shù)量暴漲的一個(gè)重要因素)。看看尾5行,如下:

In [22]: user_amount.tail()

Out[22]:

??????user_id? order_amount? amount_cumsum

23565????7931?????? 6497.18???? 2463822.60

23566???19339?????? 6552.70???? 2470375.30

23567????7983?????? 6973.07???? 2477348.37

23568???14048?????? 8976.33???? 2486324.70

23569????7592????? 13990.93???? 2500315.63


目的是查看用戶的貢獻(xiàn)情況,把數(shù)據(jù)轉(zhuǎn)換成百分比更直觀。接下來(lái)新增一行把累計(jì)情況轉(zhuǎn)換成百分比。

In [23]: user_amount['prop']=user_amount.apply(lambdax:x.amount_cumsum/user_amount.amount_cumsum.max(),axis=1)

In [24]: user_amount.tail()

Out[24]:

??????user_id? order_amount? amount_cumsum????? prop

23565????7931?????? 6497.18???? 2463822.60?0.985405

23566???19339?????? 6552.70???? 2470375.30?0.988025

23567????7983?????? 6973.07???? 2477348.37?0.990814

23568???14048?????? 8976.33???? 2486324.70?0.994404

23569????7592????? 13990.93???? 2500315.63?1.000000

由圖看出,前20000個(gè)用戶貢獻(xiàn)了40%的消費(fèi)。后面3000多位用戶貢獻(xiàn)了60%,呈現(xiàn)二八傾向。

將以上代碼中的order_amount改為order_dt,即可查看用戶和訂單數(shù)量的關(guān)系。


了解了用戶的貢獻(xiàn)之后,再看看訂單訂購(gòu)的數(shù)量和用戶消費(fèi)金額之間的關(guān)系。引入seaborn模塊做線性回歸分析。

In [26]: import seaborn as sns

In [27]: sns.regplot(x='order_amount',y='order_products', data=df)

In [28]: sns.regplot(x='order_amount',y='order_products',data = df.groupby('user_id').sum())


分別從訂單和用戶兩個(gè)維度進(jìn)行分析,

訂單維度:訂單消費(fèi)金額和訂單商品量呈規(guī)律性,每個(gè)商品十元左右。訂單的極值較少,超出1000的就幾個(gè)

用戶維度:用戶消費(fèi)金額和訂購(gòu)商品數(shù)量的規(guī)律性更強(qiáng)。說(shuō)明銷售的商品相對(duì)比較單一。


Part2用戶分層——RFM模型


知識(shí)點(diǎn)及說(shuō)明:

①R的參數(shù):根據(jù)用戶最近一次消費(fèi)的時(shí)間與截止數(shù)據(jù)采集當(dāng)天的間隔,計(jì)算出時(shí)間間隔均值,然后用每個(gè)用戶的時(shí)間間隔與該均值比較,確定R的參數(shù);

②F的參數(shù):根據(jù)用戶的總購(gòu)買數(shù)量與所有用戶的平均購(gòu)買數(shù)量作比,確定F的參數(shù);

③M的參數(shù):根據(jù)用戶的總購(gòu)買金額與所有用戶的平均購(gòu)買金額作比,確定M的參數(shù)。

④np.timedelta64(1, 'D')將日期類型(timedelta)轉(zhuǎn)換成數(shù)值類型

⑤這里使用均值做為判斷條件,最近一次消費(fèi)的時(shí)間小于均值時(shí),R返回1,否則返回0;購(gòu)買數(shù)量和金額大于均值時(shí)返回1,否則返回0。


In [29]: rfm = df.pivot_table(index='user_id',

???...: values=['order_products', 'order_amount', 'order_date'],

???...: aggfunc={'order_date': 'max','order_amount':'sum','order_products': 'sum'})

In [30]: rfm['R'] = (rfm.order_date -rfm.order_date.max()) / np.timedelta64(1, 'D')?

In [31]:rfm.rename(columns={'order_products': 'F', 'order_amount': 'M'}, inplace=True) #重命名列

In [32]: rfm.head()

Out[32]:

????????????? M order_date?? F?????R

user_id

1????????11.77 1997-01-01?? 1 -545.0

2????????89.00 1997-01-12?? 6 -534.0

3???????156.46 1998-05-28? 16? -33.0

4???????100.50 1997-12-12?? 7 -200.0

5???????385.61 1998-01-03? 29 -178.0

In [33]:?def rfm_func(x):

???...:????? level = x.apply(lambdax: '1' if x >= 0 else '0')

???...:????? label = level.R +level.F + level.M???????? # 字符串拼接

???...:????? level_class = {

???...:????? '111': '重要價(jià)值客戶',

???...:????? '011': '重要保持客戶',

???...:????? '101': '重要挽留客戶',

???...:????? '001': '重要發(fā)展客戶',

???...:????? '110': '一般價(jià)值客戶',

???...:????? '010': '一般保持客戶',

???...:????? '100': '一般挽留客戶',

???...:????? '000': '一般發(fā)展客戶'}

???...:????? result =level_class[label]

???...:????? return result

???...:

In [34]: rfm['label'] = rfm[['R', 'F','M']].apply(lambda x: x -?x.mean()).apply(rfm_func, axis=1)???? #用均值作比,調(diào)用函數(shù)

In [35]: rfm.head()

Out[35]:

????????????? M order_date?? F?????R?? label

user_id

1????????11.77 1997-01-01?? 1 -545.0? 一般發(fā)展客戶

2????????89.00 1997-01-12?? 6 -534.0? 一般發(fā)展客戶

3???????156.46 1998-05-28? 16? -33.0? 重要價(jià)值客戶

4???????100.50 1997-12-12?? 7 -200.0? 一般挽留客戶

5???????385.61 1998-01-03? 29 -178.0? 重要價(jià)值客戶


對(duì)用戶分層完,進(jìn)行繪制餅圖,查看各層用戶占比。

In [36]: use_c =rfm.groupby('label').count()

In [37]: plt.axis('equal')

In [38]: labels = ['一般價(jià)值客戶','一般保持客戶','一般發(fā)展客戶','一般挽留客戶

???...: ','重要價(jià)值客戶','重要保持客戶','重要發(fā)展客戶','重要挽留客戶']

In [39]:plt.pie(use_c['M'],autopct='%1.1f%%',labels = labels,pctdistance=0.9,

...:labeldistance=1.1,radius=2.2,startangle = 180)

此時(shí),顯示的圖表沒(méi)有顯示出漢字,需要對(duì)maplotlib進(jìn)行設(shè)置,如下:

In [40]:plt.rcParams['font.sans-serif']=['SimHei']????? #設(shè)置字體顯示類型

In [41]: plt.rcParams['font.family']='sans-serif'???????? #字體集

In [42]: plt.rcParams['axes.unicode_minus']= False? #設(shè)置顯示負(fù)號(hào)


接下來(lái)對(duì)RFM模型的各個(gè)指標(biāo)進(jìn)行查詢,并在excel中進(jìn)行繪制。

In [43]: -rfm.groupby('label').mean().R

Out[43]:

label

一般價(jià)值客戶???142.951456

一般保持客戶???471.363636

一般發(fā)展客戶???493.947350

一般挽留客戶???179.123636

重要價(jià)值客戶???113.585200

重要保持客戶???455.353240

重要發(fā)展客戶???475.029046

重要挽留客戶???171.105740

Name: R, dtype: float64

In [44]: rfm.groupby('label').sum().F

Out[44]:

label

一般價(jià)值客戶?????1712

一般保持客戶??????650

一般發(fā)展客戶????29346

一般挽留客戶????13977

重要價(jià)值客戶???107789

重要保持客戶????11121

重要發(fā)展客戶?????1263

重要挽留客戶?????2023

Name: F, dtype: int64

In [45]: rfm.groupby('label').sum().M

Out[45]:

label

一般價(jià)值客戶?????19937.45

一般保持客戶??????7181.28

一般發(fā)展客戶????438291.81

一般挽留客戶????196971.23

重要價(jià)值客戶???1592039.62

重要保持客戶????167080.83

重要發(fā)展客戶?????33028.40

重要挽留客戶?????45785.01

Name: M, dtype: float64


從RFM分層可知,六成左右的客戶屬于一般發(fā)展客戶(該分層中的底層用戶),最高層的重要價(jià)值客戶占比19.3%。這一批重要價(jià)值客戶(種子用戶)貢獻(xiàn)了63.6%的銷售額,

在消費(fèi)領(lǐng)域中,狠抓高質(zhì)量用戶是萬(wàn)古不變的道理。


Part 3 用戶活躍度

知識(shí)點(diǎn)及說(shuō)明:

①分類標(biāo)準(zhǔn):

新用戶new: 第一次消費(fèi)。

活躍用戶active: 在某一個(gè)時(shí)間窗口內(nèi)有過(guò)消費(fèi)的老客。

不活躍用戶unactive: 在時(shí)間窗口內(nèi)沒(méi)有消費(fèi)過(guò)的老客。

回流用戶return:在上一個(gè)窗口中沒(méi)有消費(fèi),而在當(dāng)前時(shí)間窗口內(nèi)有過(guò)消費(fèi)。

潛在用戶unreg: 截止當(dāng)前窗口還沒(méi)有消費(fèi)過(guò)

以上的時(shí)間窗口都是按月統(tǒng)計(jì)。


比如某用戶在

1月第一次消費(fèi),那么他在1月的分層就是新用戶;

2月消費(fèi)過(guò),則是活躍用戶;

3月沒(méi)有消費(fèi),此時(shí)是不活躍用戶;

4月再次消費(fèi),此時(shí)是回流用戶;

5月還是消費(fèi),是活躍用戶


②思路:對(duì)數(shù)據(jù)進(jìn)行透視,然后用applymap函數(shù)將透視表的值轉(zhuǎn)換成布爾值(0/1),最后定義一個(gè)函數(shù)對(duì)其進(jìn)行判斷、分類。

In [46]: pivoted_date =df.pivot_table(index='user_id', columns='month',

???...:????? values='order_dt',aggfunc='count').fillna(0)? #對(duì)df進(jìn)行透視,用于計(jì)算的值及計(jì)算方法

In [47]: columns_month = df.month.sort_values().astype('str').unique()

In [48]: pivoted_date.columns =columns_month

In [49]: pivoted_date_bool =pivoted_date.applymap(lambda x: 1 if x > 0 else 0)

In [50]: def active_status(df):

???...:???? status = []

???...:???? for i in range(18):

???...: ????????#若本月沒(méi)有消費(fèi)

???...:???????? if df[i] == 0:

???...:???????????? if len(status)> 0: #排除第一月

???...:???????????????? ifstatus[i-1] == 'unreg':

???...:????????????????????status.append('unreg')

???...:???????????????? else:

???...:??????????????? ?????status.append('unactive')

???...:???????????? else:

???...:????????????????status.append('unreg')???? #<=0,說(shuō)明是第一個(gè)月,未消費(fèi)過(guò)

???...:???????? #若本月消費(fèi)

???...:???????? else:

???...:???????????? if len(status) ==0:

???...:????????????????status.append('new')

???...:???????????? else:

???...:???????????????? ifstatus[i-1] == 'unactive':

???...:????????????????????status.append('return')

???...:???????????????? elifstatus[i-1] == 'unreg':

???...:????????????????????status.append('new')

???...:???????????????? else:

???...:????????????????????status.append('active')

???...:???? return status

???...:

In [51]: user_level =pivoted_date_bool.apply(lambda x: active_status(x), axis=1)

In [52]: user_level.head()

Out[52]:

???????1997-01-01 1997-02-01 1997-03-01 1997-04-01 1997-05-01 1997-06-01? \

user_id

1????????????? new?? unactive??unactive?? unactive?? unactive??unactive

2????????????? new?? unactive??unactive?? unactive?? unactive??unactive

3????????????? new?? unactive????return???? active?? unactive??unactive

4????????????? new?? unactive??unactive?? unactive?? unactive??unactive

5????????????? new???? active??unactive???? return???? active????active


???????1997-07-01 1997-08-01 1997-09-01 1997-10-01 1997-11-01 1997-12-01? \

user_id

1????????unactive?? unactive?? unactive??unactive?? unactive?? unactive

2????????unactive?? unactive?? unactive??unactive?? unactive?? unactive

3????????unactive?? unactive?? unactive??unactive???? return?? unactive

4????????unactive???? return?? unactive??unactive?? unactive???? return

5??????????active?? unactive???? return??unactive?? unactive???? return


???????1998-01-01 1998-02-01 1998-03-01 1998-04-01 1998-05-01 1998-06-01

user_id

1????????unactive?? unactive?? unactive??unactive?? unactive?? unactive

2????????unactive?? unactive?? unactive??unactive?? unactive?? unactive

3????????unactive?? unactive?? unactive??unactive???? return?? unactive

4????????unactive?? unactive?? unactive??unactive?? unactive?? unactive

5??????????active?? unactive?? unactive??unactive?? unactive?? unactive


潛在用戶unreg截止當(dāng)前窗口還沒(méi)有消費(fèi)過(guò),是未來(lái)作為新客,在未發(fā)生交易之前,統(tǒng)計(jì)時(shí)應(yīng)該去掉。

In [53]: user_level_del_unreg =user_level.replace('unreg', np.nan).apply(lambda x: pd.value_counts(x))

In [54]: user_level_del_unreg.T.fillna(0)

Out[54]:

???????????active???? new? return?unactive

1997-01-01???? 0.0?7846.0???? 0.0?????? 0.0

1997-02-01?1157.0? 8476.0???? 0.0???6689.0

1997-03-01?1681.0? 7248.0?? 595.0??14046.0

1997-04-01?1773.0???? 0.0? 1049.0??20748.0

1997-05-01??852.0???? 0.0? 1362.0??21356.0

1997-06-01??747.0???? 0.0? 1592.0??21231.0

1997-07-01??746.0???? 0.0? 1434.0??21390.0

1997-08-01??604.0???? 0.0? 1168.0??21798.0

1997-09-01??528.0???? 0.0? 1211.0??21831.0

1997-10-01??532.0???? 0.0? 1307.0??21731.0

1997-11-01??624.0???? 0.0? 1404.0??21542.0

1997-12-01??632.0???? 0.0? 1232.0??21706.0

1998-01-01??512.0???? 0.0? 1025.0??22033.0

1998-02-01??472.0???? 0.0? 1079.0??22019.0

1998-03-01??571.0???? 0.0? 1489.0??21510.0

1998-04-01??518.0???? 0.0?? 919.0??22133.0

1998-05-01??459.0???? 0.0? 1029.0??22082.0

1998-06-01??446.0???? 0.0? 1060.0??22064.0


新用戶前部都在前三個(gè)月,前面數(shù)據(jù)展示部分訂單的消費(fèi)金額為零,可能是網(wǎng)站有減免活動(dòng)。將數(shù)據(jù)以柱狀圖形式展示,查看各個(gè)時(shí)間窗口的值的差距。

In [55]: bar_values =user_level_del_unreg.T.div(user_level_del_unreg.T.sum(1),axis=0).fillna(0)#歸1化

In [56]: bar_values.plot.bar(figsize=(16,8))

活躍用戶和回流用戶的占比和不活躍用戶的占比相差十分懸殊。單獨(dú)查看活躍用戶和回流用戶的占比。

In [57]: user_level_del_unreg.apply(lambdax: x / x.sum(), axis=0).fillna(0).T[['active', 'return']].plot()

In [58]: values =user_level_del_unreg.fillna(0).T[['active', 'return']].sum()

In [59]: plt.axis('equal')

In [60]: labels = ['active', 'return']

In [61]:plt.pie(values,autopct='%2.2f%%',labels=labels)



Part 4 用戶畫像


用戶生命周期、用戶購(gòu)買周期、回流率、流失率、復(fù)購(gòu)率、回購(gòu)率、


查看用戶周期為0天的占比,超過(guò)一半!說(shuō)明大部分用戶的粘性和忠誠(chéng)度很低!

In [62]: user_life =df.groupby('user_id').order_date.agg(['min','max'])

In [63]: (user_life['min'] ==user_life['max']).value_counts()

Out[63]:

True????12054

False???11516

dtype: int64

In [64]: rate = (user_life['min'] ==user_life['max']).value_counts()

In [65]: labels = ['生命周期為0天','生命周期大于0天']

In [66]: plt.axis('equal')

In [67]: plt.pie(rate,labels=labels,

...:?????????autopct='%2.1f%%',

...:?????????startangle=90,

...:?????????radius=1.5)


查看整體用戶的生命周期

知識(shí)點(diǎn)及說(shuō)明:

①統(tǒng)計(jì)邏輯:第一次消費(fèi)時(shí)間至最后一次消費(fèi)時(shí)間為整個(gè)用戶生命周期

②思路:用用戶最后一次消費(fèi)的時(shí)間減去第一次消費(fèi)的時(shí)間。

In [68]: life_seri =(user_life['max'] -?user_life['min'])

...:?????????????????????/np.timedelta64(1,'D')

In[69]: life_seri.hist(bins=50)

In[70]: life_seri.mean()

Out[70]:134.8719558761137

圖表呈現(xiàn)出長(zhǎng)尾模型,說(shuō)明大部分用戶的生命周期比較短。接下來(lái)把非零的用戶篩選出來(lái)分析。

In [71]: life_seri = (user_life['max'] -user_life['min']).reset_index()[0]/np.timedelta64(1,'D')

In [72]:life_seri[life_seri>0].hist(bins=50)

Out[72]: <matplotlib.axes._subplots.AxesSubplotat 0x224561fdef0>

In [73]: life_seri[life_seri > 0].mean()

Out[73]: 276.0448072247308

排除極值0天的影響之后,雖然仍舊有不少用戶生命周期靠攏在0天,但數(shù)據(jù)呈現(xiàn)雙峰圖,有相當(dāng)部分用戶的生命周期在400天到500天之間,這已經(jīng)屬于忠誠(chéng)用戶。對(duì)于有過(guò)兩次以上消費(fèi)但生命周期在一個(gè)月內(nèi)的而用戶應(yīng)當(dāng)盡量引導(dǎo),逐步培養(yǎng)用戶的忠誠(chéng)度。

再看看用戶的購(gòu)買周期。

知識(shí)點(diǎn)及說(shuō)明:

①統(tǒng)計(jì)邏輯:求用戶相鄰兩次消費(fèi)的時(shí)間間隔;

②思路:先求出用戶后續(xù)的所有產(chǎn)生消費(fèi)的日期與其第一次消費(fèi)的日期進(jìn)行相減,求出差值,再使用一階差分進(jìn)行相減即可求得用戶購(gòu)買周期;

③merge()函數(shù):將用戶消費(fèi)行為和第一次消費(fèi)時(shí)間對(duì)應(yīng)上,形成一個(gè)新的DataFrame。它和SQL中的join差不多;

④shift()函數(shù):可以移動(dòng)行或列的值,將前后的值相減可以進(jìn)行錯(cuò)位求差值(一階差分)

In [74]: df2=df[['user_id','order_products', 'order_amount', 'order_date']]?? #切片

In [75]: buy_df = pd.merge(left=df2, right= user_life['min'].reset_index(),??? #使用merge聯(lián)結(jié)兩個(gè)DF

???...: how = 'inner', on = 'user_id', suffixes = ('', '_min'))

In [76]: buy_df['date_diff'] =(buy_df.order_date -buy_df['min'])/np.timedelta64(1,'D')

In [77]: buy_gap =buy_df.groupby('user_id').apply(lambda x :x.date_diff.shift(-1)-x.date_diff)

In [78]: buy_gap.hist(bins=100)

In [79]: buy_gap.mean()

Out[79]: 68.97376814424265


用戶得購(gòu)買平均周期為69天,想要召回用戶,在60天左右的消費(fèi)間隔是比較好的。直方圖也是典型得長(zhǎng)尾分布,大部分用戶得消費(fèi)間隔比較短。

接下來(lái)分析用戶的留存率:

知識(shí)點(diǎn)及說(shuō)明:

①統(tǒng)計(jì)邏輯:用戶在第一次消費(fèi)后,進(jìn)行第二次消費(fèi)的比率。(某個(gè)時(shí)間窗口,第二次消費(fèi)數(shù)/總的一次消費(fèi)數(shù)),

②思路:對(duì)用戶購(gòu)買周期的數(shù)據(jù)分區(qū)間進(jìn)行統(tǒng)計(jì);再透視相關(guān)數(shù)據(jù),然后用applymap函數(shù)將透視表的值轉(zhuǎn)換成布爾值(0/1),最后求DF.sum()/DF.count()即為所求。

③cut()將區(qū)間切為左開右閉區(qū)間(即date_diff=0并沒(méi)有被劃分入0~3天)

如果用戶僅消費(fèi)了一次或者在第一天內(nèi)消費(fèi)了多次但是往后沒(méi)有消費(fèi),留存率都是是0。統(tǒng)計(jì)時(shí)剔除。


In [80]: bins = [0, 3, 7, 15, 30, 60, 90,180, 365]

In [81]: buy_df['date_diff_bins'] =pd.cut(buy_df.date_diff, bins=bins)

In [82]: pivoted_buy_df =buy_df.pivot_table(index='user_id', columns='date_d

???...: iff_bins',values='order_amount', aggfunc=sum)

In [83]: pivoted_buy_df_bool =pivoted_buy_df.fillna(0).applymap(lambda x: 1 if x > 0 else 0)

In [84]: retention_rate =(pivoted_buy_df_bool.sum() / pivoted_buy_df_bool.count())

In [85]: retention_rate.plot()

In [86]: retention_rate.plot.bar()

In [87]: pivoted_buy_df.mean()

Out[87]:

date_diff_bins

(0, 3]???????35.905798

(3, 7]??????? 36.385121

(7, 15]?????? 42.669895

(15, 30]????? 45.964649

(30, 60]????? 50.215070

(60, 90]????? 48.975277

(90, 180]???? 67.223297

(180, 365]??? 91.960059

dtype: float64

從用戶在后續(xù)各階段的平均消費(fèi)金額效果看,用戶第一次消費(fèi)后的0~3天內(nèi),更可能消費(fèi)更多。雖然后面時(shí)間段的金額高,但是它的時(shí)間范圍也寬廣。


用戶流失率:

知識(shí)點(diǎn)及說(shuō)明:

①統(tǒng)計(jì)邏輯:用戶最后一次購(gòu)買時(shí)間距離最后統(tǒng)計(jì)時(shí)間窗口的間隔大于一個(gè)統(tǒng)計(jì)窗口(這里設(shè)置為1個(gè)月)即為流失。累計(jì)所有流失用戶數(shù)量除以當(dāng)期用戶的總數(shù)即為流失率

②思路:求每月發(fā)生購(gòu)買的用戶數(shù)/新用戶數(shù)每月累加值,得到每月留存的用戶比例,用1減去該比例即是流失率


In [88]: pivoted_data =df.pivot_table(index='user_id', columns='month', values='order_products',aggfunc=sum)

In [89]: pivoted_data_bool =pivoted_data.applymap(lambda x: 1 if x>0 else np.nan)

In [90]: month_buyers =pivoted_data_bool.count()?? #統(tǒng)計(jì)各列,用戶id為統(tǒng)計(jì)對(duì)象,結(jié)果得到每個(gè)月的消費(fèi)人數(shù)

In [91]: month_buyers.name = 'user_buy'

In [92]: month_new=df.groupby('user_id').month.min().value_counts()

#求首次消費(fèi)的用戶數(shù)(即新用戶數(shù)量)在每個(gè)月的分布情況

In [93]: month_new.name = 'new_users'

In [94]: month_new.index.name = 'month'

In [95]: month_buyers_new =pd.merge(left=month_buyers.reset_index(), right=m

???...: onth_new.reset_index(),how='left', on='month', suffixes=('','_min')).fillna(0)

In [96]: month_buyers_new['churn_rate'] =1-month_buyers_new.user_buy/month_buyers_new.new_users.cumsum()

In [97]: month_buyers_new.churn_rate.index= month_buyers_new.month.sort_values().astype('str')

In [98]: month_buyers_new.churn_rate.plot()

In [99]:month_buyers_new.churn_rate.plot.bar()

前三月,用戶的流失率急劇上升,后續(xù)穩(wěn)定在93%。

求用戶的回購(gòu)率

①統(tǒng)計(jì)邏輯:某一個(gè)時(shí)間窗口內(nèi)消費(fèi)的用戶,在下一個(gè)時(shí)間窗口仍舊消費(fèi)的占比。

②思路:將上期有消費(fèi)且本期都有消費(fèi)的用戶,記為1;上期和本期只消費(fèi)一次的記為0;都沒(méi)有消費(fèi)的記為NaN。然后統(tǒng)計(jì)DF.sum()和DF.count()的人數(shù)進(jìn)行相除。

In [100]: def back_buy(data):

????...:???? status=[]

????...:???? for i in range(17):

????...:???????? if data[i]==1:

????...:???????????? if data[i+1]==1:

????...:????????????????status.append(1)

????...:???????????? if data[i+1]==0:

????...:????????????????status.append(0)

????...:???????? else :

????...:????????????status.append(np.nan)

????...:???? else:

????...:???????? status.append(np.nan)

????...:???? return status

????...:

In [101]: pivoted_date_bool_2 =pivoted_date_bool.apply(back_buy,axis=1)

In [102]: back_buy_rate = (pivoted_date_bool_2.sum()/ pivoted_date_bool_2.count())

In [103]: back_buy_rate.plot()

復(fù)購(gòu)率

①統(tǒng)計(jì)邏輯:在某時(shí)間窗口內(nèi)消費(fèi)兩次及以上的用戶在總消費(fèi)用戶中占比。

②思路:將數(shù)據(jù)轉(zhuǎn)換未,消費(fèi)兩次及以上記為1,消費(fèi)一次記為0,沒(méi)有消費(fèi)記為NaN。然后統(tǒng)計(jì)DF.sum()和DF.count()的人數(shù)進(jìn)行相除。

In [104]: pivoted_date_bool_3 =pivoted_date.applymap(lambda x: 1 if x > 1 else np.NaN if x == 0 else 0)

In [105]: rebuy_rate =(pivoted_date_bool_3.sum() / pivoted_date_bool_3.count())

In [106]: rebuy_rate.plot()

Part 5? 補(bǔ)充


In [1]:df.groupby('month').order_products.sum().plot(figsize=(12,6))

Out[1]:<matplotlib.axes._subplots.AxesSubplot at

0x21d31575b00>

In [2]:df.groupby('month').order_amount.sum().plot(figsize=(12,6))

Out[2]:<matplotlib.axes._subplots.AxesSubplot at 0x21d31cdc780>

In [3]: df.groupby('month').order_products.count().plot(figsize=(12,6))

Out[3]:<matplotlib.axes._subplots.AxesSubplot at 0x21d30ae9198>

In [4]:df.groupby('month').user_id.count().plot(figsize=(12,6))

Out[4]:<matplotlib.axes._subplots.AxesSubplot at 0x21d31575b00>

每月銷售產(chǎn)品數(shù)量
每月銷售總金額
每月訂單數(shù)量
每月發(fā)生交易的用戶數(shù)


part 1每月/日銷量、銷售額、訂單量、用戶數(shù)的代碼(直接將month->order_date即可得到每日走勢(shì)圖)


Part 6 總結(jié)


來(lái)一波彩蛋吧!

圖表中顯示漢字及負(fù)號(hào):

#中文亂碼的處理(字體設(shè)置、負(fù)號(hào)問(wèn)題)

plt.rcParams['font.sans-serif']=['SimHei']

plt.rcParams['font.family']='sans-serif'

plt.rcParams['axes.unicode_minus'] =False

如果還出現(xiàn)亂碼,需要修改一些默認(rèn)的設(shè)置,參考:

https://blog.csdn.net/minixuezhen/article/details/81516949

設(shè)置圖表的標(biāo)題、坐標(biāo)軸標(biāo)簽及刻度值、字號(hào)的大小:

plt.xlabel(x,fontsize=20)plt.ylabel(y,fontsize=20)

plt.title(t,fontsize=20)

plt.xticks(fontsize=20)

plt.yticks(fontsize=20)

對(duì)坐標(biāo)軸進(jìn)行格式化,顯示百分比:

from matplotlib.tickerimport FuncFormatter

def to_percent(temp,position):

return '%2.1f' % (100 * temp) +'%'

plt.gca().xaxis.set_major_formatter(FuncFormatter(to_percent))

plt.gca().yaxis.set_major_formatter(FuncFormatter(to_percent))

圖片存儲(chǔ)及展示:

plt.savefig(path, bbox_inches='tight', dpi=200)#一定要在show()前設(shè)置

plt.show()????#主要是在其他編輯器,如pycharm等對(duì)圖表進(jìn)行可視化



來(lái)自小明傳書,一起嗑數(shù)據(jù)。


公眾號(hào)(小明傳書)后臺(tái)恢復(fù):CD,可獲取原始數(shù)據(jù)。

公眾號(hào)(小明傳書)后臺(tái)恢復(fù):CD分析,獲取本人所整理的代碼及分析過(guò)程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,480評(píng)論 0 13
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,392評(píng)論 0 10
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽(yáng)光小鎮(zhèn)少爺閱讀 3,842評(píng)論 0 8
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,713評(píng)論 0 23
  • 真的是很討厭他和他媽 人生不太完美的瑕疵點(diǎn) 唉,身體越來(lái)越發(fā)虛,得認(rèn)真運(yùn)動(dòng)認(rèn)真讀書認(rèn)真談?dòng)瓤死锢?莫名覺(jué)得被綠 媽...
    小寶尼閱讀 156評(píng)論 0 0