主頁 >  其他 > 最經典的一門資料分析案例【CDNow】入門推薦

最經典的一門資料分析案例【CDNow】入門推薦

2021-11-15 13:28:15 其他

今天跟大家分享的是我之前跟著做過的一門專案,非常的經典,也非常的詳細,適合作為資料分析入門的專案,以下是有關的介紹,

資料來源于CDNow網站的用戶購買明細,一共有用戶ID,購買日期,購買數量,購買金額四個欄位,

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
  1. 加載資料:首先需要的是加載資料,同時由于資料中缺乏表頭,所以需要賦予,且讀取時注意更改默認分隔符(資料由多個空格分隔)
columns = ['user_id', 'order_dt', 'order_products', 'order_amount']
df = pd.read_csv('CDNow_master.txt', names=columns, sep='\s+')
  1. 觀察資料:其中需要注意的是,order_dr為日期,格式為int64,并非我們需要的日期格式,所以后續要進行更改,同時一個用戶也可以在一天內進行多次購買,如user_id為2的用戶就在19970112那天買了兩次,
df.head()
user_idorder_dtorder_productsorder_amount
0119970101111.77
1219970112112.00
2219970112577.00
3319970102220.76
4319970330220.76
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 4 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   user_id         69659 non-null  int64  
 1   order_dt        69659 non-null  int64  
 2   order_products  69659 non-null  int64  
 3   order_amount    69659 non-null  float64
dtypes: float64(1), int64(3)
memory usage: 2.1 MB
  1. 資料處理:沒有空值,很干凈的資料,現在需要將時間的資料型別進行轉換,
df['order_date'] = pd.to_datetime(df.order_dt, format='%Y%m%d')
df['month'] = df.order_date.values.astype('datetime64[M]')

%h是小時,%M是分鐘,注意和月的大小寫不一致,秒是%s,

另外之所以還將資料轉換成月份格式,是因為我們將月份作為消費行為的主要事件視窗,選擇哪種時間視窗取決于消費頻率,
(也可以是以天或者年來劃分)

df.head()
user_idorder_dtorder_productsorder_amountorder_datemonth
0119970101111.771997-01-011997-01-01
1219970112112.001997-01-121997-01-01
2219970112577.001997-01-121997-01-01
3319970102220.761997-01-021997-01-01
4319970330220.761997-03-301997-03-01
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 6 columns):
 #   Column          Non-Null Count  Dtype         
---  ------          --------------  -----         
 0   user_id         69659 non-null  int64         
 1   order_dt        69659 non-null  int64         
 2   order_products  69659 non-null  int64         
 3   order_amount    69659 non-null  float64       
 4   order_date      69659 non-null  datetime64[ns]
 5   month           69659 non-null  datetime64[ns]
dtypes: datetime64[ns](2), float64(1), int64(3)
memory usage: 3.2 MB

pandas中有專門的時間序列方法tseries,它可以用來進行時間偏移,也是處理時間型別的好方法,時間格式也能作為索引,在金融、財務等領域使用較多,

# df.date - pd.tseries.offsets.MonthBegin(1)
  1. 按每筆訂單來統計分布由上述可得,用戶平均每單購買2.4個商品,標準差為2.3,略有波動,中位數在2.0,75分位數為3,說明絕大多部分訂單的購買力 都不多,最大值為99個,數字較高,而購買金額則同購買數量差不多情況,大部分訂單都集中在小額, 一般而言,消費類的資料分布,都是長尾形態,大部分用戶都是小額,然而小部分用戶貢獻了收入的大頭,俗稱二八法則,
df.describe()
user_idorder_dtorder_productsorder_amount
count69659.0000006.965900e+0469659.00000069659.000000
mean11470.8545921.997228e+072.41004035.893648
std6819.9048483.837735e+032.33392436.281942
min1.0000001.997010e+071.0000000.000000
25%5506.0000001.997022e+071.00000014.490000
50%11410.0000001.997042e+072.00000025.980000
75%17273.0000001.997111e+073.00000043.700000
max23570.0000001.998063e+0799.0000001286.010000
  1. 上面的消費行為資料粒度是每筆訂單,我們轉換成每位用戶看一下,
user_grouped = df.groupby('user_id').sum()
user_grouped.head()
order_dtorder_productsorder_amount
user_id
119970101111.77
239940224689.00
311983360216156.46
4798822337100.50
521968613729385.61

用group_by創建一個新物件,

user_grouped.describe()
order_dtorder_productsorder_amount
count2.357000e+0423570.00000023570.000000
mean5.902627e+077.122656106.080426
std9.460684e+0716.983531240.925195
min1.997010e+071.0000000.000000
25%1.997021e+071.00000019.970000
50%1.997032e+073.00000043.395000
75%5.992125e+077.000000106.475000
max4.334408e+091033.00000013990.930000

從用戶角度來看,每位用戶平均購買7張CD,最多的用戶購買了1033張CD(太瘋狂了),用戶平均的消費金額(客單價)為100,標準差為240,結合分位數和最大值看,平均值才和75分位接近,說明存在小部分的高額消費用戶,

  1. 接下來按月的維度來分析
df.groupby('month').order_products.sum().plot()

在這里插入圖片描述

按月統計每個月的CD銷量,從圖中可以看出,前三個月銷量非常的高,但后期下降較大,并趨于平穩(平穩中也略有下降)

df.groupby('month').order_amount.sum().plot()

在這里插入圖片描述

情況同銷量相同,也是前期非常多,后期平穩下降,

  1. 至于為什么會出現這種情況?我們假設是用戶身上出了問題,早期時間段的用戶中有例外值,第二假設是早期有各類促銷營銷,但這里只有消費資料,所以無法判斷,

  2. 分析是否存在例外值

8.1 繪制每筆訂單的散點圖,從圖中觀察,訂單消費金額和訂單商品量呈規律性,每個商品十元左右,訂單的極值極少,超過1000的較少,顯然不是例外波動的罪魁禍首,

df.plot.scatter(x = 'order_amount', y = 'order_products')

在這里插入圖片描述

8.2 繪制用戶的散點圖,用戶也比較健康,而且規律性比訂單更強,因為這是CD網站的銷售資料,商品比較單一,金額和商品量的關系也因此呈線性,沒幾個離群點,

df.groupby('user_id').sum().plot.scatter(x = 'order_amount', y = 'order_products')

在這里插入圖片描述

8.3 消費能力特別強的用戶有,但是數量不多,為了更好的觀察,用直方圖,

plt.figure(figsize=(12, 4))
plt.subplot(121)
df.order_amount.hist(bins = 30)

plt.subplot(122)
df.groupby('user_id').order_amount.sum().hist(bins = 30)

在這里插入圖片描述

plt.subplot用于繪制子圖,子圖用數字引數表示,121表示分成1*2個圖片區域,占用第一個,即第一行第一列,122表示占用第二個,figure是尺寸函式,為了容納兩張子圖,寬設定的大一點即可,

從直方圖看,大部分用戶的消費能力確實不高,高消費用戶在圖上幾乎看不到,這也確實符合消費行為的行業規律,

8.4 觀察完用戶消費的金額和購買量,接下來看消費的時間節點

df.groupby('user_id').month.min().value_counts()
1997-02-01    8476
1997-01-01    7846
1997-03-01    7248
Name: month, dtype: int64

從中不難發現,所有用戶的第一次消費都集中在前三個月,我們可以這樣認為,案例中的訂單資料,只是選擇了某個時間段消費的用戶在18個月內的消費行為,

df.groupby('user_id').month.max().value_counts()
1997-02-01    4912
1997-03-01    4478
1997-01-01    4192
1998-06-01    1506
1998-05-01    1042
1998-03-01     993
1998-04-01     769
1997-04-01     677
1997-12-01     620
1997-11-01     609
1998-02-01     550
1998-01-01     514
1997-06-01     499
1997-07-01     493
1997-05-01     480
1997-10-01     455
1997-09-01     397
1997-08-01     384
Name: month, dtype: int64

所有用戶的最后一次消費也主要是集中在前三個月,后續時間段內,依然有用戶在消費,但是緩慢減少,

例外趨勢的原因獲得了解釋,現在針對消費資料進一步細分,我們要明確,這只是部分用戶的訂單資料,所以有一定局限性,在這里,我們統一將資料上消費的用戶定義為新客,

  1. 接下來分析消費中的復購率和回購率,首先將用戶消費資料進行資料透視,
pivoted_counts = df.pivot_table(index='user_id', columns='month', values='order_dt', aggfunc='count').fillna(0)
columns_month = df.month.sort_values().astype('str').unique()

pivoted_counts.columns = columns_month
pivoted_counts.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
11.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
22.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
31.00.01.01.00.00.00.00.00.00.02.00.00.00.00.00.01.00.0
42.00.00.00.00.00.00.01.00.00.00.01.00.00.00.00.00.00.0
52.01.00.01.01.01.01.00.01.00.00.02.01.00.00.00.00.00.0

在pandas中,資料透視有專門的函式pivot_table,功能非常強大,

使用資料透視表,需要明確獲得什么結果,有些用戶在某月沒有進行過消費,會用NaN表示,這里用fillna填充,

生成的資料透視,月份是1997-01-01 00:00:00表示,比較丑,所以優化成標準格式,

首先求復購率,復購率的定義是在某時間視窗消費兩次及以上的用戶在總消費用戶中占比,這里的時間視窗是月,如果一個用戶在同一天內下了兩筆訂單,這里也將他算作復購用戶,

將資料轉換一下,消費兩次及以上記為1,消費一次記為0,沒有消費記為NaN,

pivoted_counts_trans = pivoted_counts.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
pivoted_counts_trans.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
10.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
21.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
30.0NaN0.00.0NaNNaNNaNNaNNaNNaN1.0NaNNaNNaNNaNNaN0.0NaN
41.0NaNNaNNaNNaNNaNNaN0.0NaNNaNNaN0.0NaNNaNNaNNaNNaNNaN
51.00.0NaN0.00.00.00.0NaN0.0NaNNaN1.00.0NaNNaNNaNNaNNaN

applymap針對DataFrame里的所有資料,用lambda進行判斷,因為這里涉及了多個結果,所以要兩個if else,記住,lambda沒有elif的用法,

(pivoted_counts_trans.sum() / pivoted_counts_trans.count()).plot(figsize = (10, 4))

在這里插入圖片描述

用sum和count相除即可計算出復購率,因為這兩個函式都會忽略NaN,而NaN是沒有消費的用戶,count不論是0還是1都會統計,所以是總的消費用戶數,而sum求和計算了兩次以上的消費用戶這里用了比較巧妙的替代法計算復購率,SQL中也可以用,

圖中可以看出復購率在早期,因為大量新用戶加入的關系,新客的復購率并不高,譬如1月新客們的復購率只有6%左右,而在后期,這時的用戶都是大浪淘沙剩下的老客,復購率比較穩定,在20%左右,

單看新客和老客,復購率有三倍左右的差距,

  1. 接下來計算回購率,回購率是某一個時間視窗內消費的用戶,在下一個時間視窗仍舊消費的占比,比方說我1月消費用戶為1000,他們中有300個人在2月份依然消費,回購率是30%,

回購率的計算比較難,因為它設計了時間視窗的對比,

pivoted_amount = df.pivot_table(index='user_id', columns='month', values='order_amount', aggfunc='mean').fillna(0)
columns_month = df.month.sort_values().astype('str').unique()

pivoted_amount.columns = columns_month
pivoted_amount.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
111.770.00.000.000.000.000.000.000.000.00.0000.0000.000.00.00.00.000.0
244.500.00.000.000.000.000.000.000.000.00.0000.0000.000.00.00.00.000.0
320.760.020.7619.540.000.000.000.000.000.039.2050.0000.000.00.00.016.990.0
429.530.00.000.000.000.000.0014.960.000.00.00026.4800.000.00.00.00.000.0
521.6538.90.0045.5538.7126.1428.140.0040.470.00.00043.46537.470.00.00.00.000.0

將消費金額進行資料透視,這里作為練習,使用了平均值,

pivoted_purchase = pivoted_amount.applymap(lambda x:1 if x>0 else 0)
pivoted_purchase.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
1100000000000000000
2100000000000000000
3101100000010000010
4100000010001000000
5110111101001100000

再次用applymap+lambda轉換資料,只要有購買過,記為1,否則為0,

def purchase_return(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)
    status.append(np.NaN)
    return status
pivoted_purchase_return = pivoted_purchase.apply(purchase_return, axis=1, result_type='expand')
pivoted_purchase_return.columns = columns_month
pivoted_purchase_return.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
10.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
20.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
30.0NaN1.00.0NaNNaNNaNNaNNaNNaN0.0NaNNaNNaNNaNNaN0.0NaN
40.0NaNNaNNaNNaNNaNNaN0.0NaNNaNNaN0.0NaNNaNNaNNaNNaNNaN
51.00.0NaN1.01.01.00.0NaN0.0NaNNaN1.00.0NaNNaNNaNNaNNaN

新建一個判斷函式,data是輸入的資料,即用戶在18個月內是否消費的記錄,status是空串列,后續用來保存用戶是否回購的欄位,

因為有18個月,所以每個月都要濟寧一次判斷,需要用到回圈,判斷的主要邏輯是:如果用戶本月進行過消費,且下月也有消費,記為1,沒有記為0,如果本月沒有進行過消費,為NaN,后續的統計中進行消除,

用apply函式應用在所有行上,獲得想要的結果,

(pivoted_purchase_return.sum()/pivoted_purchase_return.count()).plot(figsize = (10, 4))

在這里插入圖片描述

最后的計算和復購率大同小異,用count和sum求出,從圖中可以看出,用戶的回購率高于復購率,約在30%左右,波動性也比較強,新用戶的回購率在15%左右,和老客差異不大,

將回購率和復購率綜合分析,可以得出,新客整體質量低于老客,老客的忠誠度(回購率)表現較好,消費頻次稍次,這是CDNow網站的用戶消費特征,

  1. 接下來進行用戶分層,我們按照用戶的消費行為,簡單劃分幾個維度:新用戶、活躍用戶 、不活躍用戶和回流用戶,

新用戶的定義是第一次消費,活躍用戶即為老客,在某一個時間視窗內有過消費,不活躍用戶則是時間視窗內沒有消費過的老客,回流用戶是在上一個視窗沒有消費,而在當前時間視窗有過消費,以上時間視窗都是按月統計,

比如某用戶在1月第一次消費,那么他在1月份的分層就是新用戶;他在2月份消費過,則是活躍用戶;3月份沒有消費,此時是不活躍用戶;4月份再次消費,此時是回流用戶,5月份還是消費,是活躍用戶,

分層會涉及到比較多的邏輯判斷,

def state_return(data):
    status = []
    for i in range(18):
        if data[i] == 0:
            if len(status) == 0:
                status.append('unreg')
            else:
                if status[i-1] == 'unreg':
                    status.append('unreg')
                else:
                    status.append('unact')
        else:
            if len(status) == 0:
                status.append('new')
            else:
                if status[i-1] == 'unreg':
                    status.append('new')
                elif status[i-1] == 'unact':
                    status.append('return')
                else:
                    status.append('act')
    return status


函式寫得比較復雜,主要分為兩部分來判斷,如果本月沒有消費,那么先判斷這個月是不是第一個月,如果是的話必然還沒有購買過(用unreg來表示),如果不是第一個月,那么判斷上個月是不是也還沒購買過,如果是就還是unreg,如果不是這個月就是不活躍用戶unact;如果本月有消費,也是先判斷這個月是不是第一個月,如果是那么說明他是新用戶new,如果不是判斷上個月注冊過沒,沒有就是new,如果上個月是unact說明購買過那么就是回流用戶,否則就是act,

pivoted_purchase_status = pivoted_purchase.apply(state_return, axis = 1, result_type='expand')
pivoted_purchase_status.columns = columns_month
pivoted_purchase_status.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
user_id
1newunactunactunactunactunactunactunactunactunactunactunactunactunactunactunactunactunact
2newunactunactunactunactunactunactunactunactunactunactunactunactunactunactunactunactunact
3newunactreturnactunactunactunactunactunactunactreturnunactunactunactunactunactreturnunact
4newunactunactunactunactunactunactreturnunactunactunactreturnunactunactunactunactunactunact
5newactunactreturnactactactunactreturnunactunactreturnactunactunactunactunactunact

從結果看,用戶每個月的分層狀態以及變化已經被我們計算出來,這個方法是根據透視過的寬表計算,其實還有另一種寫法,只提取時間視窗內的資料和上個視窗對比判斷,封裝成函式做回圈,封裝成函式做回圈,這種方法更適合ETL的增量更新,

pivoted_status_counts = pivoted_purchase_status.replace('unreg', np.NaN).apply(lambda x:pd.value_counts(x))
pivoted_status_counts.head()
1997-01-011997-02-011997-03-011997-04-011997-05-011997-06-011997-07-011997-08-011997-09-011997-10-011997-11-011997-12-011998-01-011998-02-011998-03-011998-04-011998-05-011998-06-01
actNaN1155.016801773.0852.0747.0746.0604.0528.0532.0624632.0512.0472.0569.0517.0458.0446.0
new7814.08455.07231NaNNaNNaNNaNNaNNaNNaN2NaNNaNNaNNaNNaNNaNNaN
returnNaNNaN5951049.01362.01592.01434.01168.01211.01307.014021232.01025.01079.01489.0919.01030.01060.0
unactNaN6659.01399420678.021286.021161.021320.021728.021761.021661.02147421638.021965.021951.021444.022066.022014.021996.0

將unreg更改為NaN是為了避免被計入總數,此時已換成按月的統計量,

pivoted_status_counts.fillna(0).T.plot.area(figsize = (12, 6))

在這里插入圖片描述

生成面積圖,因為它還是某時間段消費過的用戶的后續行為,所以藍色和灰色的區域都可以不看,只看紫色回流和紅色活躍 這兩個分層,用戶數比較穩定,這兩個分層相加,就是消費用戶占比(后期沒新客),

return_rata = pivoted_status_counts.apply(lambda x:x / x.sum(), axis = 1)
return_rata.loc['return'].plot(figsize = (12, 6))

在這里插入圖片描述

用戶回流占比在5%-8%,有下降趨勢,所謂回流占比,就是回流用戶在總用戶中的占比,另外一種指標叫回流率,指上個月多少不消費(活躍)用戶在本月消費(活躍)用戶的占比,

return_rata.loc['act'].plot(figsize = (12, 6))

在這里插入圖片描述

活躍用戶的下降趨勢更明顯,占比在3%~5%之間,這里的活躍用戶可以看作是連續消費用戶,質量上在一定程度上高于回流用戶,

結合回流用戶和活躍用戶來看,在后期的消費用戶中,60%是回流用戶,40%是活躍用戶(連續消費用戶),整體質量還不錯,但是針對這兩個分層依舊有改進的空間,可以繼續細化資料,

  1. 接下來分析用戶質量,因為消費行為有明顯的二八傾向,我們需要知道高質量用戶為消費貢獻了多少份額,
user_amount = df.groupby('user_id').order_amount.sum().sort_values().reset_index()
user_amount['amount_cumsum'] = user_amount.order_amount.cumsum()
user_amount.tail()
user_idorder_amountamount_cumsum
2356579316497.182463822.60
23566193396552.702470375.30
2356779836973.072477348.37
23568140488976.332486324.70
23569759213990.932500315.63

新建一個物件,按用戶的消費金額升序,使用cumsum函式,即累加函式,用于逐行計算累計的金額,最后的2500315便是總金額,

total_amount = user_amount.amount_cumsum.max()
user_amount['prop'] = user_amount.amount_cumsum.apply(lambda x:x / total_amount)
user_amount.tail()
user_idorder_amountamount_cumsumprop
2356579316497.182463822.600.985405
23566193396552.702470375.300.988025
2356779836973.072477348.370.990814
23568140488976.332486324.700.994404
23569759213990.932500315.631.000000

轉換成百分比,

user_amount.prop.plot()

在這里插入圖片描述

如上圖所示,橫坐標是按貢獻金額大小排序而成,縱坐標則是用戶累計貢獻,可以很清楚的看到,前20000個用戶貢獻了40%的消費,后面3000多位用戶則貢獻了60%,確實呈現二八傾向,

user_counts = df.groupby('user_id').order_dt.count().sort_values().reset_index()
user_counts['counts_cumsum'] = user_counts.order_dt.cumsum()

total_counts = user_counts.counts_cumsum.max()
user_counts['prop'] = user_counts.counts_cumsum.apply(lambda x:x / total_counts)
user_counts.prop.plot()

在這里插入圖片描述

此時統計銷量,前20000位用戶貢獻了45%的銷量,剩下的高消費用戶則貢獻了55%的銷量,在消費領域中,狼抓高質量用戶是萬古不變的道理,

  1. 接下來計算用戶的生命周期,這里定義第一次消費至最后一次消費為整個用戶生命,
user_purchase = df[['user_id', 'order_products', 'order_amount', 'order_date']]
order_date_min = user_purchase.groupby('user_id').order_date.min()
order_date_max = user_purchase.groupby('user_id').order_date.max()
(order_date_max - order_date_min).head(10)
user_id
1      0 days
2      0 days
3    511 days
4    345 days
5    367 days
6      0 days
7    445 days
8    452 days
9    523 days
10     0 days
Name: order_date, dtype: timedelta64[ns]

先統計出了用戶的第一次消費和最后一次消費,然后相減即可,因為資料中的用戶都是前三月第一次消費,所以這里的生命周期代表的是1月~3月用戶的生命周期,因為用戶會持續消費,所以理論上,隨著后續的消費,用戶的平均生命周期會增大,

(order_date_max - order_date_min).describe()
count                          23570
mean     134 days 20:55:36.987696224
std      180 days 13:46:43.039788104
min                  0 days 00:00:00
25%                  0 days 00:00:00
50%                  0 days 00:00:00
75%                294 days 00:00:00
max                544 days 00:00:00
Name: order_date, dtype: object

求一下平均,所有用戶的平均生命周期是134天,比預想的高,但是中位數是0,說明了由很多用戶第一次購買和最后一次購買都是在同一天,

為了更清楚地查看用戶的生命周期,我們需要看一下分布,

((order_date_max - order_date_min) / np.timedelta64(1, 'D')).hist(bins = 15)

在這里插入圖片描述

因為這里的資料型別是timedelta時間,它無法直接作出直方圖,所以先換算成數值,劃算的方式直接除timedelta函式即可,這里的np.timedelta64(1, ‘D’)即可,

從圖中可以看出,大部分用戶只消費了一次,所有生命周期的大頭都集中在了0天,但這不是我們想要的答案,不妨將只消費了一次的新客排除,來計算所有消費過兩次以上的老客的生命周期,

life_time = (order_date_max - order_date_min).reset_index() # 轉為dataframe
life_time.head()
user_idorder_date
010 days
120 days
23511 days
34345 days
45367 days
life_time['life_time'] = life_time.order_date / np.timedelta64(1, 'D')
life_time[life_time.life_time > 0].life_time.hist(bins=100, figsize=(12, 6))

在這里插入圖片描述

篩選出lifetime>0,即排除了僅消費了一次的那些人,做直方圖,

這個圖的價值明顯高于上圖,雖然仍舊有不少用戶生命周期靠攏0天,這是雙峰趨勢圖,部分質量差的用戶,雖然消費了兩次,但是仍舊無法持續,在用戶首次消費30天內應該盡量引導,少部分用戶集中在50天~300天,屬于普通型的生命周期,高質量用戶的生命周期,集中在400天以后,這已經屬于忠誠用戶了,

life_time[life_time.life_time > 400].life_time.count() / life_time[life_time.life_time > 0].life_time.count()
0.31703716568252865

生命周期在400天以上的用戶占老客的比例為31.7%,挺高的數值了,

life_time[life_time.life_time > 0].life_time.mean()
276.0448072247308

消費兩次以上的用戶生命周期為276天,遠高于總體,從策略上看,用戶首次消費后應該花費更多時間精力去引導其進行多次消費,延長生命周期,這會帶來2.5倍的增量,

  1. 再來計算留存率,留存率也是消費分析領域的經典應用,它指用戶在第一次消費后,有多少比率進行第二次消費,和回流率的區別是留存率傾向于計算第一次消費,并且有多個時間視窗,
user_purchase_retention = pd.merge(left=user_purchase, right=order_date_min.reset_index(), how='inner', on='user_id',suffixes=('', '_min'))
user_purchase_retention.head()
user_idorder_productsorder_amountorder_dateorder_date_min
01111.771997-01-011997-01-01
12112.001997-01-121997-01-12
22577.001997-01-121997-01-12
33220.761997-01-021997-01-02
43220.761997-03-301997-01-02

這里用到merge函式,它和SQL中的join差不多,用來將兩個dataframe進行合并,我們選擇了inner的方式,對標SQL中的inner join,

這里merge的目的是將用戶消費行為和第一次消費時間對應上,形成一個新的dataframe,

user_purchase_retention['order_date_diff'] = user_purchase_retention.order_date - user_purchase_retention.order_date_min
user_purchase_retention.head()
user_idorder_productsorder_amountorder_dateorder_date_minorder_date_diff
01111.771997-01-011997-01-010 days
12112.001997-01-121997-01-120 days
22577.001997-01-121997-01-120 days
33220.761997-01-021997-01-020 days
43220.761997-03-301997-01-0287 days

這里將order_date和order_date_min想減,獲得一個新的列,為用戶每一次消費距第一次消費的時間差值,

date_trans = lambda x: x/np.timedelta64(1, 'D')

user_purchase_retention['date_diff'] = user_purchase_retention.order_date_diff.apply(date_trans)
user_purchase_retention.head(10)
user_idorder_productsorder_amountorder_dateorder_date_minorder_date_diffdate_diff
01111.771997-01-011997-01-010 days0.0
12112.001997-01-121997-01-120 days0.0
22577.001997-01-121997-01-120 days0.0
33220.761997-01-021997-01-020 days0.0
43220.761997-03-301997-01-0287 days87.0
53219.541997-04-021997-01-0290 days90.0
63557.451997-11-151997-01-02317 days317.0
73420.961997-11-251997-01-02327 days327.0
83116.991998-05-281997-01-02511 days511.0
94229.331997-01-011997-01-010 days0.0
bin = [0,3,7,15,30,60,90,180,365]
user_purchase_retention['date_diff_bin'] = pd.cut(user_purchase_retention.date_diff, bins = bin)
user_purchase_retention.head(15)
user_idorder_productsorder_amountorder_dateorder_date_minorder_date_diffdate_diffdate_diff_bin
01111.771997-01-011997-01-010 days0.0NaN
12112.001997-01-121997-01-120 days0.0NaN
22577.001997-01-121997-01-120 days0.0NaN
33220.761997-01-021997-01-020 days0.0NaN
43220.761997-03-301997-01-0287 days87.0(60.0, 90.0]
53219.541997-04-021997-01-0290 days90.0(60.0, 90.0]
63557.451997-11-151997-01-02317 days317.0(180.0, 365.0]
73420.961997-11-251997-01-02327 days327.0(180.0, 365.0]
83116.991998-05-281997-01-02511 days511.0NaN
94229.331997-01-011997-01-010 days0.0NaN
104229.731997-01-181997-01-0117 days17.0(15.0, 30.0]
114114.961997-08-021997-01-01213 days213.0(180.0, 365.0]
124226.481997-12-121997-01-01345 days345.0(180.0, 365.0]
135229.331997-01-011997-01-010 days0.0NaN
145113.971997-01-141997-01-0113 days13.0(7.0, 15.0]
pivoted_retention = user_purchase_retention.pivot_table(index='user_id', columns='date_diff_bin', values='order_amount', aggfunc=sum)
pivoted_retention.head(10)
date_diff_bin(0, 3](3, 7](7, 15](15, 30](30, 60](60, 90](90, 180](180, 365]
user_id
10.00.00.000.000.000.00.000.00
20.00.00.000.000.000.00.000.00
30.00.00.000.000.0040.30.0078.41
40.00.00.0029.730.000.00.0041.44
50.00.013.970.0038.900.0110.40155.54
60.00.00.000.000.000.00.000.00
70.00.00.000.000.000.00.0097.43
80.00.00.000.0013.970.045.29104.17
90.00.00.000.000.000.030.330.00
100.00.00.000.000.000.00.000.00

用pivot_table資料透視,獲得的結果是用戶在第一次消費之后,在后續各時間段內的消費總額,

這里不難發現如果沒有消費的話就是0,但為方便后續我們只統計有消費的用戶,將0更改為nan,

pivoted_retention.replace(0,np.nan).mean()
date_diff_bin
(0, 3]        35.905798
(3, 7]        36.385121
(7, 15]       42.669895
(15, 30]      45.986198
(30, 60]      50.215070
(60, 90]      48.975277
(90, 180]     67.223297
(180, 365]    91.960059
dtype: float64

此時計算用戶在后續時間段的平均消費額,這里只統計有消費的平均值,雖然后面時間段的金額高,但是它的時間范圍也寬廣,從平均效果來看,用戶第一次消費后的0-3天內,更可能消費更多,

但消費更多是一個相對的概念,我們還要看整體中有多少用戶在0-3天消費,

pivoted_retention_trans = pivoted_retention.applymap(lambda x: 1 if x>0 else 0)
pivoted_retention_trans.head()
date_diff_bin(0, 3](3, 7](7, 15](15, 30](30, 60](60, 90](90, 180](180, 365]
user_id
100000000
200000000
300000101
400010001
500101011

如果有消費就記為1,沒有就是0.

(pivoted_retention_trans.sum() / pivoted_retention_trans.count()).plot.bar()

在這里插入圖片描述

只有2.5%的用戶在第一次消費后的三天內有過消費,3%的用戶在3-7天內有過消費,數字并不好看,不過CD購買確實不是高頻消費行為,時間范圍放寬后數字好看了不少,有20%的用戶在第一次消費后的三個月到半年之間有過購買,27%的用戶在半年后到一年內有過購買,從運營角度看,CD機營銷在教育新用戶的同時,應該注重用戶忠誠度的培養,放長線釣大魚,在一定時間內召回用戶購買,

  1. 怎么算放長線釣大魚呢?我們計算出用戶的平均購買周期,
def diff(group):
    d = group.date_diff.shift(-1) - group.date_diff
    return d

last_diff = user_purchase_retention.groupby('user_id').apply(diff)
last_diff.head(10)
user_id   
1        0      NaN
2        1      0.0
         2      NaN
3        3     87.0
         4      3.0
         5    227.0
         6     10.0
         7    184.0
         8      NaN
4        9     17.0
Name: date_diff, dtype: float64

此時已經求出了用戶的每次購買距離上次購買的時間差,

last_diff.mean()
68.97376814424265

此時求出了用戶的平均消費間隔是68天,所以想要召回用戶,在60天左右的消費間隔是比較好的,

last_diff.hist(bins = 20)

在這里插入圖片描述

看一下直方圖,典型的長尾分布,大部分的用戶的消費間隔確實比較短,不妨將時間召回點設為消費后立即贈送優惠券,消費10天詢問用戶CD怎么樣,消費30天后提醒優惠券到期,消費60天后短信推送,這便是資料的應用了,


推薦關注的專欄

👨?👩?👦?👦 機器學習:分享機器學習實戰專案和常用模型講解
👨?👩?👦?👦 資料分析:分享資料分析實戰專案和常用技能整理

往期內容回顧

💚 學習Python全套代碼【超詳細】Python入門、核心語法、資料結構、Python進階【致那個想學好Python的你】
?? 學習pandas全套代碼【超詳細】資料查看、輸入輸出、選取、集成、清洗、轉換、重塑、數學和統計方法、排序
💙 學習pandas全套代碼【超詳細】分箱操作、分組聚合、時間序列、資料可視化
💜 學習NumPy全套代碼【超詳細】基本操作、資料型別、陣列運算、復制和試圖、索引、切片和迭代、形狀操作、通用函式、線性代數


關注我,了解更多相關知識!

CSDN@報告,今天也有好好學習

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/357244.html

標籤:AI

上一篇:資料挖掘—Weka 的資料庫挖掘及資料預處理

下一篇:一文速覽-江西開放資料大賽VET風險預測診斷單特征思路分享

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more