(出行)单车项目用户消费行为——数据分析( pandas & power BI)

数据来源于网上,是用户在一家单车网站上的消费记录

【目录】

1.用户消费趋势的月度分析

2.用户个体消费金额与次数分析

3.用户首购、末购及用户分层

4.用户周期

5.复购率与回购率

总结

import pandas as pd  
import numpy as np 
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table('bicycle_master.txt',names=columns,sep='\s+')
  • user_id:用户ID
  • order_dt:购买日期
  • order_products:购买产品数
  • order_amount:购买金额
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
df.head()
user_idorder_dtorder_productsorder_amount
0119970101111.77
1219970112112.00
2219970112577.00
3319970102220.76
4319970330220.76
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
  • 大部分订单只消费了少量商品(平均2.4),有一定值干扰
  • 用户的消费金额比较稳定,平均消费35元,中位数在25元,有一定极值的干扰
df['order_dt']=pd.to_datetime(df.order_dt,format='%Y%m%d')
df['month']=df.order_dt.astype('datetime64[M]')
df.month
0       1997-01-01
1       1997-01-01
2       1997-01-01
3       1997-01-01
4       1997-03-01
           ...    
69654   1997-04-01
69655   1997-04-01
69656   1997-03-01
69657   1997-03-01
69658   1997-03-01
Name: month, Length: 69659, dtype: datetime64[ns]
  • 对时间进行解析,一开始默认的格式是 int64的,可以用 df.info() 来查看,默认会是 datetime64[ns] 类型,后面中括号表示 时间间隔是 ns
  • 下面是对 order_dt 列(新版本可以不用取values直接用astype强制转换),转换类型为datetime64[M],默认就会是每月的第一天了,同理设置为[Y]就是每年的1月1日,然后生成新的一列 month

1.进行用户消费趋势的分析(按月)

- 每月的消费总金额 - 每月的消费次数 - 每月的产品购买量 - 每月的消费人数
每月消费总金额
month_order_amount=df.groupby('month').order_amount.sum()
month_order_amount
month
1997-01-01    299060.17
1997-02-01    379590.03
1997-03-01    393155.27
1997-04-01    142824.49
1997-05-01    107933.30
1997-06-01    108395.87
1997-07-01    122078.88
1997-08-01     88367.69
1997-09-01     81948.80
1997-10-01     89780.77
1997-11-01    115448.64
1997-12-01     95577.35
1998-01-01     76756.78
1998-02-01     77096.96
1998-03-01    108970.15
1998-04-01     66231.52
1998-05-01     70989.66
1998-06-01     76109.30
Name: order_amount, dtype: float64
每月消费次数
month_order_fq=df.groupby('month').order_amount.count()
month_order_fq
month
1997-01-01     8928
1997-02-01    11272
1997-03-01    11598
1997-04-01     3781
1997-05-01     2895
1997-06-01     3054
1997-07-01     2942
1997-08-01     2320
1997-09-01     2296
1997-10-01     2562
1997-11-01     2750
1997-12-01     2504
1998-01-01     2032
1998-02-01     2026
1998-03-01     2793
1998-04-01     1878
1998-05-01     1985
1998-06-01     2043
Name: order_amount, dtype: int64
每月产品购买量
month_order_products=df.groupby('month').order_products.sum()
month_order_products
month
1997-01-01    19416
1997-02-01    24921
1997-03-01    26159
1997-04-01     9729
1997-05-01     7275
1997-06-01     7301
1997-07-01     8131
1997-08-01     5851
1997-09-01     5729
1997-10-01     6203
1997-11-01     7812
1997-12-01     6418
1998-01-01     5278
1998-02-01     5340
1998-03-01     7431
1998-04-01     4697
1998-05-01     4903
1998-06-01     5287
Name: order_products, dtype: int64
一次性取出每月消费总金额、每月消费次数、每月产品购买量多组数据
month_info=df.groupby('month')[['order_amount','user_id','order_products']].agg({'order_amount':sum,'user_id':'count','order_products':sum})
month_info
order_amountuser_idorder_products
month
1997-01-01299060.17892819416
1997-02-01379590.031127224921
1997-03-01393155.271159826159
1997-04-01142824.4937819729
1997-05-01107933.3028957275
1997-06-01108395.8730547301
1997-07-01122078.8829428131
1997-08-0188367.6923205851
1997-09-0181948.8022965729
1997-10-0189780.7725626203
1997-11-01115448.6427507812
1997-12-0195577.3525046418
1998-01-0176756.7820325278
1998-02-0177096.9620265340
1998-03-01108970.1527937431
1998-04-0166231.5218784697
1998-05-0170989.6619854903
1998-06-0176109.3020435287
month_info.rename(columns={'order_amount':'月度消费总金额','user_id':'月度消费次数','order_products':'月度购买量'},inplace=True)
month_info.head()
月度消费总金额月度消费次数月度购买量
month
1997-01-01299060.17892819416
1997-02-01379590.031127224921
1997-03-01393155.271159826159
1997-04-01142824.4937819729
1997-05-01107933.3028957275
每月消费人数
month_info['月度消费人数']=df.groupby('month').user_id.nunique()
month_info.head()
月度消费总金额月度消费次数月度购买量月度消费人数
month
1997-01-01299060.178928194167846
1997-02-01379590.0311272249219633
1997-03-01393155.2711598261599524
1997-04-01142824.49378197292822
1997-05-01107933.30289572752214
month_info=month_info.reset_index()
month_info['month']=month_info.astype(str)
month_info.head()
month月度消费总金额月度消费次数月度购买量月度消费人数
01997-01-01299060.178928194167846
11997-02-01379590.0311272249219633
21997-03-01393155.2711598261599524
31997-04-01142824.49378197292822
41997-05-01107933.30289572752214
month_info.to_excel(r'.\月度销售趋势分析.xlsx')

去重的方法有多种,这里也可以使用数据库思想,df.groupby([‘month’,‘user_id’]).count().reset_index()
每月消费人数低于每月消费次数,但差异不大
前三个月每月的消费人数在8000-10000之间,后续月份,平均消费人数在2000不到

# 上面进行的汇总分析,其实可以用数据透视的方法更快实现,一次性求出结果
df.pivot_table(index='month',
               values=['order_amount','user_id','order_products'],
               aggfunc={'order_amount':sum,'user_id':'count','order_products':sum}).head()
order_amountorder_productsuser_id
month
1997-01-01299060.17194168928
1997-02-01379590.032492111272
1997-03-01393155.272615911598
1997-04-01142824.4997293781
1997-05-01107933.3072752895
  • 每月用户平均消费金额的趋势
  • 每月用户平均消费次数的趋势
user_month_trends=df.groupby('month')[['order_amount','order_products']].agg(np.mean)
user_month_trends.rename(columns={'order_amount':'用户每月平均消费金额的趋势','order_products':'用户每月平均消费次数的趋势'},inplace=True)
user_month_trends.head()
用户每月平均消费金额的趋势用户每月平均消费次数的趋势
month
1997-01-0133.4968832.174731
1997-02-0133.6754822.210877
1997-03-0133.8985402.255475
1997-04-0137.7742632.573129
1997-05-0137.2826602.512953

2.用户个体消费分析

  • 用户消费金额,消费次数的描述统计(可绘制散点图)
  • 用户消费金额的分布
  • 用户消费次数的分布
  • 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
df.columns
Index(['user_id', 'order_dt', 'order_products', 'order_amount', 'month'], dtype='object')
用户消费金额,消费次数的描述统计
group_user=df.groupby('user_id')
group_user.sum().describe()
order_productsorder_amount
count23570.00000023570.000000
mean7.122656106.080426
std16.983531240.925195
min1.0000000.000000
25%1.00000019.970000
50%3.00000043.395000
75%7.000000106.475000
max1033.00000013990.930000
user_info=group_user.sum()
user_info.to_excel(r'.\用户个体消费行为分析.xlsx')
用户消费金额的分布
group_user.order_amount.sum()
user_id
1         11.77
2         89.00
3        156.46
4        100.50
5        385.61
          ...  
23566     36.00
23567     20.97
23568    121.70
23569     25.74
23570     94.08
Name: order_amount, Length: 23570, dtype: float64
group_user_order_amount_sum_lst=[i for i in range(0,int(group_user.order_amount.sum().max()+50),50)]
group_user_order_amount_sum=pd.cut(group_user.order_amount.sum(),
                                   bins=group_user_order_amount_sum_lst,
                                   labels=group_user_order_amount_sum_lst[1:])
group_user_order_amount_sum
user_id
1         50
2        100
3        200
4        150
5        400
        ... 
23566     50
23567     50
23568    150
23569     50
23570    100
Name: order_amount, Length: 23570, dtype: category
Categories (280, int64): [50 < 100 < 150 < 200 ... 13850 < 13900 < 13950 < 14000]
group_user_order_amount_sum.to_excel(r'.\用户消费金额的分布.xlsx')
用户消费次数的分布
group_user.order_amount.count()
user_id
1         1
2         2
3         6
4         4
5        11
         ..
23566     1
23567     1
23568     3
23569     1
23570     2
Name: order_amount, Length: 23570, dtype: int64
group_user_order_amount_count_lst=[i for i in range(0,int(group_user.order_amount.count().max()+2),2)]
group_user_order_amount_count=pd.cut(group_user.order_amount.count(),
                                   bins=group_user_order_amount_count_lst,
                                   labels=group_user_order_amount_count_lst[1:])
group_user_order_amount_count
user_id
1         2
2         2
3         6
4         4
5        12
         ..
23566     2
23567     2
23568     4
23569     2
23570     2
Name: order_amount, Length: 23570, dtype: category
Categories (109, int64): [2 < 4 < 6 < 8 ... 212 < 214 < 216 < 218]
group_user_order_amount_count.to_excel(r'.\用户消费次数的分布.xlsx')
用户累计消费金额占比
# cumsum 是求累加值
user_cumsum=group_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
# 这里 reset_index() 是为了得到一个自然数的行标签,表示的就是人数,下面的图就可以看出来多少个少占多少百分比
user_cumsum.reset_index().order_amount
0        0.000000
1        0.000000
2        0.000000
3        0.000000
4        0.000000
           ...   
23565    0.985405
23566    0.988025
23567    0.990814
23568    0.994404
23569    1.000000
Name: order_amount, Length: 23570, dtype: float64
user_cumsum.reset_index().order_amount.to_excel(r'.\用户累计消费金额占比.xlsx')

3.用户消费行为

  • 用户第一次消费(首购)
  • 用户最后一次消费
  • 新老客户消费比
  • 多少用户仅消费一次
  • 每月新客占比
  • 用户分层
  • RFM模型
  • 新、老、活跃、回流、流失
  • 用户购买周期(按订单)
  • 用户消费周期描述
  • 用户消费周期分布
  • 用户生命周期(按第一次和最后一次消费)
    -用户生命周期描述
  • 用户生命周期分布
用户第一次消费(首购)
group_user_dtmin=group_user.order_dt.min().value_counts()
group_user_dtmin=group_user_dtmin.reset_index().rename(columns={'index':'first_date','order_dt':'quantities'})
group_user_dtmin.first_date=group_user_dtmin.first_date.astype(str)
group_user_dtmin
first_datequantities
01997-02-08363
11997-02-24347
21997-02-04346
31997-02-06346
41997-03-04340
.........
791997-01-08213
801997-03-21213
811997-01-07211
821997-01-01209
831997-01-04174

84 rows × 2 columns

group_user_dtmin.to_excel(r'.\用户首购.xlsx')
用户最后一次消费
group_user_maxdt=group_user.order_dt.max().value_counts()
group_user_maxdt=group_user_maxdt.reset_index().rename(columns={'index':'last_date','order_dt':'quantities'})
group_user_maxdt.last_date=group_user_maxdt.last_date.astype(str)
group_user_maxdt
last_datequantities
01997-02-08221
11997-03-12213
21997-02-04210
31997-03-06204
41997-02-27202
.........
5411997-07-044
5421997-10-194
5431997-07-184
5441997-06-184
5451997-07-134

546 rows × 2 columns

group_user_maxdt.to_excel(r'.\用户最后一次购买.xlsx')
#新老客消费比
# 得到第一次和最后yc次消费情况,如果 min、max 日期相同,说明只消费了一次
user_life=df.groupby('user_id').order_dt.agg([min,max])
user_life.head()
minmax
user_id
11997-01-011997-01-01
21997-01-121997-01-12
31997-01-021998-05-28
41997-01-011997-12-12
51997-01-011998-01-03
(user_life['min']==user_life['max'])
user_id
1         True
2         True
3        False
4        False
5        False
         ...  
23566     True
23567     True
23568    False
23569     True
23570    False
Length: 23570, dtype: bool
# 统计只消费了一次的用户
(user_life['min']==user_life['max']).value_counts()
True     12054
False    11516
dtype: int64

有一半以上的用户只消费了一次

每月新客占比
#按月分组下的userid分组,求每月的最早购买日期和最晚消费日期
group_um=df.groupby(['month','user_id']).order_dt.agg([min,max])
group_um
minmax
monthuser_id
1997-01-0111997-01-011997-01-01
21997-01-121997-01-12
31997-01-021997-01-02
41997-01-011997-01-18
51997-01-011997-01-14
............
1998-06-01234441998-06-281998-06-28
234891998-06-211998-06-21
235131998-06-141998-06-14
235551998-06-101998-06-10
235561998-06-071998-06-07

55379 rows × 2 columns

# 新增列 True 为 新用户
group_um['new']=(group_um['min']==group_um['max'])

group_um.reset_index().groupby('month').new.value_counts()
month       new  
1997-01-01  True     7093
            False     753
1997-02-01  True     8571
            False    1062
1997-03-01  True     8154
            False    1370
1997-04-01  True     2228
            False     594
1997-05-01  True     1801
            False     413
1997-06-01  True     1912
            False     427
1997-07-01  True     1743
            False     437
1997-08-01  True     1450
            False     322
1997-09-01  True     1410
            False     329
1997-10-01  True     1489
            False     350
1997-11-01  True     1654
            False     374
1997-12-01  True     1493
            False     371
1998-01-01  True     1242
            False     295
1998-02-01  True     1264
            False     287
1998-03-01  True     1627
            False     433
1998-04-01  True     1175
            False     262
1998-05-01  True     1209
            False     279
1998-06-01  True     1212
            False     294
Name: new, dtype: int64
用户分层
# 画 RFM,先对原始数据进行透视
rfm=df.pivot_table(index='user_id',
                  values=['order_products','order_amount','order_dt'],
                  aggfunc={'order_dt':max,'order_amount':sum,'order_products':sum})
rfm.head()

order_amountorder_dtorder_products
user_id
111.771997-01-011
289.001997-01-126
3156.461998-05-2816
4100.501997-12-127
5385.611998-01-0329
rfm.order_dt.max()
Timestamp('1998-06-30 00:00:00')
rfm.order_dt-rfm.order_dt.max()
user_id
1       -545 days
2       -534 days
3        -33 days
4       -200 days
5       -178 days
           ...   
23566   -462 days
23567   -462 days
23568   -434 days
23569   -462 days
23570   -461 days
Name: order_dt, Length: 23570, dtype: timedelta64[ns]
# 得到最近一次消费,一般是计算距离today最近一次消费,这里因为时间太久远,就随便用的max值
# 数值越大就越久远,分子得到的是一些天数类似 545 days(因为是时间格式相减),处以一个单位,就不会有单位了只留下数值
rfm['R']=(rfm.order_dt-rfm.order_dt.max())/np.timedelta64(1,'D')
# 重命名,也就是 R:最后一次消费距今天数,F:消费频次 ,M:消费总金额
# R :消费时间  F:消费频次  M:消费总金额 
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm.head()
Morder_dtFR
user_id
111.771997-01-011-545.0
289.001997-01-126-534.0
3156.461998-05-2816-33.0
4100.501997-12-127-200.0
5385.611998-01-0329-178.0
rfm[['R','F','M']].apply(lambda x:x-x.mean())
RFM
user_id
1-177.778362-6.122656-94.310426
2-166.778362-1.122656-17.080426
3334.2216388.87734450.379574
4167.221638-0.122656-5.580426
5189.22163821.877344279.529574
............
23566-94.778362-5.122656-70.080426
23567-94.778362-6.122656-85.110426
23568-66.778362-1.12265615.619574
23569-94.778362-5.122656-80.340426
23570-93.778362-2.122656-12.000426

23570 rows × 3 columns

def rfm_func(x):
    level=x.apply(lambda x:'1' if x> 0 else '0')
    # level 的类型是 series,index 是 R、F、M
#     print(type(level))
#     print(level.index)
    label=level.R+level.F+level.M
    d={'111':'重要价值用户',
       '011':'重要保持用户',
       '101':'重要发展用户',
       '001':'重要挽留用户',
       '110':'一般价值用户',
       '010':'一般保持用户',
       '100':'一般发展用户',
       '000':'一般挽留用户',
    }
        # R 为1 表示比均值大,离最早时间近,F为1 表示 消费金额比较多,M 为1 表示消费频次比较多,所以是重要价值客户
    result=d[label]
    return result
# 注意这里是要一行行的传递进来,所以 axis=1,传递一行得到一个 111,然后匹配返回一个值
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
Morder_dtFRlabelcolor
user_id
111.771997-01-011-545.0一般挽留用户非重要价值客户
289.001997-01-126-534.0一般挽留用户非重要价值客户
3156.461998-05-2816-33.0重要价值用户非重要价值客户
4100.501997-12-127-200.0一般发展用户非重要价值客户
5385.611998-01-0329-178.0重要价值用户非重要价值客户
.....................
2356636.001997-03-252-462.0一般挽留用户非重要价值客户
2356720.971997-03-251-462.0一般挽留用户非重要价值客户
23568121.701997-04-226-434.0重要挽留用户非重要价值客户
2356925.741997-03-252-462.0一般挽留用户非重要价值客户
2357094.081997-03-265-461.0一般挽留用户非重要价值客户

23570 rows × 6 columns

rfm.loc[rfm.label=='重要价值用户','color']='重要价值用户'
rfm.loc[~(rfm.label=='重要价值用户'),'color']='非重要价值用户'
rfm
Morder_dtFRlabelcolor
user_id
111.771997-01-011-545.0一般挽留用户非重要价值用户
289.001997-01-126-534.0一般挽留用户非重要价值用户
3156.461998-05-2816-33.0重要价值用户重要价值用户
4100.501997-12-127-200.0一般发展用户非重要价值用户
5385.611998-01-0329-178.0重要价值用户重要价值用户
.....................
2356636.001997-03-252-462.0一般挽留用户非重要价值用户
2356720.971997-03-251-462.0一般挽留用户非重要价值用户
23568121.701997-04-226-434.0重要挽留用户非重要价值用户
2356925.741997-03-252-462.0一般挽留用户非重要价值用户
2357094.081997-03-265-461.0一般挽留用户非重要价值用户

23570 rows × 6 columns

rfm.to_excel(r'.\RFM模型.xlsx')
rfm.groupby('label').sum()
MFR
label
一般价值用户19937.451712-29448.0
一般保持用户7181.28650-36295.0
一般发展用户196971.2313977-591108.0
一般挽留用户438291.8129346-6951815.0
重要价值用户1592039.62107789-517267.0
重要保持用户167080.8311121-358363.0
重要发展用户45785.012023-56636.0
重要挽留用户33028.401263-114482.0

从RFM 分层可知,大部分用户是重要保持客户,但是这是由于极值的影响,所以 RFM 的划分标准应该以业务为准,也可以通过切比雪夫去除极值后求均值,并且 RFM 的各个划分标准可以都不一样

  • 尽量用小部分的用户覆盖大部分的额度

  • 不要为了数据好看划分等级

  • 用户生命周期
    新客,活跃,回流,流失(一段时间不消费,或者不活跃)

# 数据透视, userid为索引,月为列,求每月的消费次数
pivot_count=df.pivot_table(index='user_id',
                           columns='month',
                           values='order_dt',
                           aggfunc='count').fillna(0)
pivot_count.head()
month1997-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
# 转变一下消费,有消费为1,没有消费为0
df_purchase=pivot_count.applymap(lambda x:1 if x>0 else 0)
df_purchase.head()
month1997-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
len(df_purchase.columns)
18
# 这里由于进行数据透视,填充了一些 null 值为0,而实际可能用户在当月根本就没有注册,
#这样会误导第一次消费数据的统计,所以写一个函数来处理
def active_status(data):
    status=[]
    # 数据一共有18个月份,每次输入一行数据,这样进行逐月判断
    for i in range(len(df_purchase.columns)):
         # 若本月没有消费,上面处理过的结果
        if data[i]==0:
            if len(status)>0:
                if status[i-1]=='unreg':
                    status.append('unreg')
                else:
                    status.append('unactive')
            # 之前一个数据都没有,就认为是未注册
            else:
                status.append('unreg')
        # 若本月消费
        else:
            if len(status)==0:
                status.append('new')
            else:
                if status[i-1]=='unactive':
                    status.append('return')
                elif status[i-1]=='unreg':
                    status.append('new')
                else:
                    status.append('acitve')
              
    return status

若本月没有消费,这里只是和上个月判断是否注册,有缺陷,可以判断是否存在就可以了

  • 若之前是未注册,则依旧为未注册
  • 若之前有消费,则为流失/不活跃
  • 其他情况,为未注册

若本月有消费

  • 若是第一次消费,则为新用户
  • 如果之前有过消费,则上个月为不活跃,则为回流
  • 如果上个月为未注册,则为新用户
  • 初次之外,为活跃

return:回流

new:新客

unreg:未注册

active:活跃

purchase_status=df_purchase.apply(lambda x: pd.Series(active_status(x),index=df_purchase.columns),axis=1)
purchase_status.head()
month1997-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
1newunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactive
2newunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactiveunactive
3newunactivereturnacitveunactiveunactiveunactiveunactiveunactiveunactivereturnunactiveunactiveunactiveunactiveunactivereturnunactive
4newunactiveunactiveunactiveunactiveunactiveunactivereturnunactiveunactiveunactivereturnunactiveunactiveunactiveunactiveunactiveunactive
5newacitveunactivereturnacitveacitveacitveunactivereturnunactiveunactivereturnacitveunactiveunactiveunactiveunactiveunactive
purchase_status.shape
(23570, 18)
# 这里把未注册的替换为空值,这样 count 计算时不会计算到
# 得到每个月的用户分布
purchase_status_count=purchase_status.replace('unreg',np.NAN).apply(lambda x:pd.value_counts(x))
purchase_status_count
month1997-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
acitveNaN1157.016811773.0852.0747.0746.0604.0528.0532.0624.0632.0512.0472.0571.0518.0459.0446.0
new7846.08476.07248NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
returnNaNNaN5951049.01362.01592.01434.01168.01211.01307.01404.01232.01025.01079.01489.0919.01029.01060.0
unactiveNaN6689.01404620748.021356.021231.021390.021798.021831.021731.021542.021706.022033.022019.021510.022133.022082.022064.0
return_rate=purchase_status_count.apply(lambda x:x/x.sum(),axis=0)
return_rate
month1997-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
acitveNaN0.0708860.0713190.0752230.0361480.0316930.031650.0256260.0224010.0225710.0264740.0268140.0217230.0200250.0242260.0219770.0194740.018922
new1.00.5192990.307510NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
returnNaNNaN0.0252440.0445060.0577850.0675430.060840.0495550.0513790.0554520.0595670.0522700.0434870.0457790.0631740.0389900.0436570.044972
unactiveNaN0.4098150.5959270.8802720.9060670.9007640.907510.9248200.9262200.9219770.9139580.9209160.9347900.9341960.9126010.9390330.9368690.936105
purchase_status_count_info=purchase_status_count.fillna(0).T
purchase_status_count_info.reset_index('month').astype(str)
purchase_status_count_info
acitvenewreturnunactive
month
1997-01-010.07846.00.00.0
1997-02-011157.08476.00.06689.0
1997-03-011681.07248.0595.014046.0
1997-04-011773.00.01049.020748.0
1997-05-01852.00.01362.021356.0
1997-06-01747.00.01592.021231.0
1997-07-01746.00.01434.021390.0
1997-08-01604.00.01168.021798.0
1997-09-01528.00.01211.021831.0
1997-10-01532.00.01307.021731.0
1997-11-01624.00.01404.021542.0
1997-12-01632.00.01232.021706.0
1998-01-01512.00.01025.022033.0
1998-02-01472.00.01079.022019.0
1998-03-01571.00.01489.021510.0
1998-04-01518.00.0919.022133.0
1998-05-01459.00.01029.022082.0
1998-06-01446.00.01060.022064.0
purchase_status_count_info.to_excel(r'.\用户分层-新、活跃、流失、回流.xlsx')
# 求出所有用户的占比
purchase_status_count_T=purchase_status_count.fillna(0).T.apply(lambda x: x/x.sum(),axis=1) 
purchase_status_count_T
acitvenewreturnunactive
month
order_dt1997-01-010.0000001.0000000.0000000.000000
1997-02-010.0708860.5192990.0000000.409815
1997-03-010.0713190.3075100.0252440.595927
1997-04-010.0752230.0000000.0445060.880272
1997-05-010.0361480.0000000.0577850.906067
1997-06-010.0316930.0000000.0675430.900764
1997-07-010.0316500.0000000.0608400.907510
1997-08-010.0256260.0000000.0495550.924820
1997-09-010.0224010.0000000.0513790.926220
1997-10-010.0225710.0000000.0554520.921977
1997-11-010.0264740.0000000.0595670.913958
1997-12-010.0268140.0000000.0522700.920916
1998-01-010.0217230.0000000.0434870.934790
1998-02-010.0200250.0000000.0457790.934196
1998-03-010.0242260.0000000.0631740.912601
1998-04-010.0219770.0000000.0389900.939033
1998-05-010.0194740.0000000.0436570.936869
1998-06-010.0189220.0000000.0449720.936105
purchase_status_count_T/purchase_status_count_T.shift()
acitvenewreturnunactive
month
order_dt1997-01-01NaNNaNNaNNaN
1997-02-01inf0.519299NaNinf
1997-03-011.0061160.592163inf1.454137
1997-04-011.0547290.0000001.7630251.477147
1997-05-010.480541NaN1.2983791.029304
1997-06-010.876761NaN1.1688690.994147
1997-07-010.998661NaN0.9007541.007489
1997-08-010.809651NaN0.8145051.019074
1997-09-010.874172NaN1.0368151.001514
1997-10-011.007576NaN1.0792730.995419
1997-11-011.172932NaN1.0742160.991303
1997-12-011.012821NaN0.8774931.007613
1998-01-010.810127NaN0.8319811.015065
1998-02-010.921875NaN1.0526830.999365
1998-03-011.209746NaN1.3799810.976884
1998-04-010.907180NaN0.6171931.028963
1998-05-010.886100NaN1.1196950.997696
1998-06-010.971678NaN1.0301260.999185

4.用户周期

# 计算相邻两个订单的时间间隔,shift 函数是对数据进行错位,所有数据会往下平移一下
order_diff=df.groupby('user_id').apply(lambda x:x.order_dt-x.order_dt.shift())
order_diff.head(20)
user_id    
1        0         NaT
2        1         NaT
         2      0 days
3        3         NaT
         4     87 days
         5      3 days
         6    227 days
         7     10 days
         8    184 days
4        9         NaT
         10    17 days
         11   196 days
         12   132 days
5        13        NaT
         14    13 days
         15    21 days
         16    66 days
         17    50 days
         18    16 days
         19    36 days
Name: order_dt, dtype: timedelta64[ns]
order_diff.describe()
count                         46089
mean     68 days 23:22:13.567662566
std      91 days 00:47:33.924168893
min                 0 days 00:00:00
25%                10 days 00:00:00
50%                31 days 00:00:00
75%                89 days 00:00:00
max               533 days 00:00:00
Name: order_dt, dtype: object
order_diff.reset_index()
user_idlevel_1order_dt
010NaT
121NaT
2220 days
333NaT
43487 days
............
69654235686965411 days
69655235686965517 days
696562356969656NaT
696572357069657NaT
6965823570696581 days

69659 rows × 3 columns

order_diff
user_id       
1        0           NaT
2        1           NaT
         2        0 days
3        3           NaT
         4       87 days
                   ...  
23568    69654   11 days
         69655   17 days
23569    69656       NaT
23570    69657       NaT
         69658    1 days
Name: order_dt, Length: 69659, dtype: timedelta64[ns]
(order_diff/np.timedelta64(1,'D'))
user_id       
1        0         NaN
2        1         NaN
         2         0.0
3        3         NaN
         4        87.0
                  ... 
23568    69654    11.0
         69655    17.0
23569    69656     NaN
23570    69657     NaN
         69658     1.0
Name: order_dt, Length: 69659, dtype: float64
order_diff_info = (order_diff/np.timedelta64(1,'D'))
order_diff_info_lst=[i for i in range(0,int(order_diff_info.max()+1),10)]
order_diff_info_hist=pd.cut(order_diff_info,bins=order_diff_info_lst,labels=order_diff_info_lst[1:])
order_diff_info_hist
user_id       
1        0        NaN
2        1        NaN
         2        NaN
3        3        NaN
         4         90
                 ... 
23568    69654     20
         69655     20
23569    69656    NaN
23570    69657    NaN
         69658     10
Name: order_dt, Length: 69659, dtype: category
Categories (53, int64): [10 < 20 < 30 < 40 ... 500 < 510 < 520 < 530]
order_diff_info_hist=order_diff_info_hist.fillna(10)
order_diff_info_hist.to_excel(r'.\用户购买周期时间差频率直方图.xlsx')
  • 订单周期呈指数分布
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期都低于100天
user_life
minmax
user_id
11997-01-011997-01-01
21997-01-121997-01-12
31997-01-021998-05-28
41997-01-011997-12-12
51997-01-011998-01-03
.........
235661997-03-251997-03-25
235671997-03-251997-03-25
235681997-03-251997-04-22
235691997-03-251997-03-25
235701997-03-251997-03-26

23570 rows × 2 columns

# 用户生命周期(按第一次和最后一次消费)
(user_life['max']-user_life['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
dtype: object
(user_life['max']-user_life['min'])
user_id
1         0 days
2         0 days
3       511 days
4       345 days
5       367 days
          ...   
23566     0 days
23567     0 days
23568    28 days
23569     0 days
23570     1 days
Length: 23570, dtype: timedelta64[ns]
user_life_info = ((user_life['max']-user_life['min'])/np.timedelta64(1,"D"))
user_life_lst = [i for i in range(0,int(user_life_info.max())+1,10)]
user_life_info_hist = pd.cut(user_life_info,bins=user_life_lst,labels=user_life_lst[1:])
user_life_info_hist
user_id
1        NaN
2        NaN
3        520
4        350
5        370
        ... 
23566    NaN
23567    NaN
23568     30
23569    NaN
23570     10
Length: 23570, dtype: category
Categories (54, int64): [10 < 20 < 30 < 40 ... 510 < 520 < 530 < 540]
user_life_info_hist_2 = user_life_info_hist.fillna(10)
user_life_info_hist_2.to_excel(r'.\用户生命周期频率直方图.xlsx')
  • 用户的生命周期受只购买一次的用户影响比较大
  • 用户均消费134天,中位数仅0天
user_life["差值"]=(user_life["max"] - user_life["min"])
user_life.head()
minmax差值
user_id
11997-01-011997-01-010 days
21997-01-121997-01-120 days
31997-01-021998-05-28511 days
41997-01-011997-12-12345 days
51997-01-011998-01-03367 days
user_life["差值"]=(user_life["max"] - user_life["min"])/np.timedelta64(1,"D")
user_life.head(5)
minmax差值
user_id
11997-01-011997-01-010.0
21997-01-121997-01-120.0
31997-01-021998-05-28511.0
41997-01-011997-12-12345.0
51997-01-011998-01-03367.0
user_life_info_hist
user_id
1        NaN
2        NaN
3        520
4        350
5        370
        ... 
23566    NaN
23567    NaN
23568     30
23569    NaN
23570     10
Length: 23570, dtype: category
Categories (54, int64): [10 < 20 < 30 < 40 ... 510 < 520 < 530 < 540]
user_life_info_hist.to_excel(r'.\用户生命周期频率直方图(忽略一次购买).xlsx')

5.复购率和回购率分析

复购率
  • 自然月内,购买多次的用户占比(即,购买了两次以上)
pivot_count.head()
month1997-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
# 区分一个,和一个以上的情况,以便于计算复购率,大于1为1,等于1为0,等于0为空
purchase_r=pivot_count.applymap(lambda x:1 if x>1 else (0 if x==1 else np.NaN))
purchase_r.head()
month1997-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
purchase_r_reshop=(purchase_r.sum()/purchase_r.count()).reset_index(name='reshop')
purchase_r_reshop
monthreshop
01997-01-010.107571
11997-02-010.122288
21997-03-010.155292
31997-04-010.223600
41997-05-010.196929
51997-06-010.195810
61997-07-010.215138
71997-08-010.200339
81997-09-010.202415
91997-10-010.206634
101997-11-010.202170
111997-12-010.219957
121998-01-010.210800
131998-02-010.203095
141998-03-010.229612
151998-04-010.199026
161998-05-010.200269
171998-06-010.214475
purchase_r_reshop['month']=purchase_r_reshop['month'].astype(str)
purchase_r_reshop.to_excel(r'.\复购人数与总消费人数比例.xlsx')
回购率
  • 曾经购买过的用户在某一时期的再次购买的占比(可能是在三个月内)
df_purchase.head()
month1997-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
# 需要使用函数来判断是否回购:当月消费过的用户下个月也消费了叫做回购,这个定义可以改变
def purchase_back(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)
            
    # 第18个月补充NaN
    status.append(np.NaN)
    return status
index=df['month'].sort_values().astype('str').unique()
purchase_b=df_purchase.apply(lambda x:pd.Series(purchase_back(x),index=index),axis=1)
purchase_b.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
purchase_b_backshop=(purchase_b.sum()/purchase_b.count())
purchase_b_backshop
1997-01-01    0.147464
1997-02-01    0.174504
1997-03-01    0.186161
1997-04-01    0.301914
1997-05-01    0.337398
1997-06-01    0.318940
1997-07-01    0.277064
1997-08-01    0.297968
1997-09-01    0.305923
1997-10-01    0.339315
1997-11-01    0.311637
1997-12-01    0.274678
1998-01-01    0.307092
1998-02-01    0.368150
1998-03-01    0.251456
1998-04-01    0.319415
1998-05-01    0.299731
1998-06-01         NaN
dtype: float64
purchase_b_backshop.to_excel(r'.\回购率.xlsx')

总结

1.月度消费统计情况

在这里插入图片描述

消费金额在前三个月达到最高峰,后续消费较为稳定,有轻微下降趋势;
产品购买量在前三个月达到最高峰,后续消费较为稳定,有轻微下降趋势;
前三个月消费订单人数在10000笔左右,后续月份的平均消费人数则在2500人

2.用户消费金额与频次情况

在这里插入图片描述

用户平均消费106元,中位值有43,说明小部分用户购买了大量货物
从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常
按照用户消费金额进行升序排序,由图可以知道50%的用户仅贡献了11%的消费额度,80%的用户仅贡献了32%的消费额度,排名前5000的用户就贡献了60%的消费额度

3.用户消费行为

在这里插入图片描述

1997年3月下旬出现用户数量的断崖式下跌,大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买
有一半的用户,只消费了一次
随着时间的递增,最后一次购买数量也在递增,消费呈现流失上升的状况(这也是正常,随着时间的增长,可能运营每跟上,或者用户忠诚度下降了)
从RFM 分层可知,大部分用户是重要保持客户

4.用户周期

在这里插入图片描述

用户的购买周期呈指数分布,用户的平均购买周期是68天,绝大部分用户的购买周期都低于100天
用户的生命周期受只购买一次的用户影响比较显著,排除只购买一次用户后,用户平均生命周期134天

5.复购率与和回购率

在这里插入图片描述

复购率稳定在20%左右,前一个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低
前三个月回购率呈上升趋势,随着用户数量大幅下跌,回购率后期波动较大
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
深圳是中国最早推行共享单车的城市之一,市区内随处可见各种品牌的共享单车。其中,CSDN作为共享单车的一家知名企业,也在深圳市场拥有相当大的市场份额。 CSDN共享单车作为一种绿色出行方式,通过手机APP实现用户注册、解锁、用车等功能。用户可以随时在任意位置扫描车辆二维码解锁使用,用完后可将车辆停放在合适的位置,通过APP结束使用。相比传统公交、出租车等交通方式,CSDN共享单车具有使用便捷、灵活性强等特点,深受广大市民的喜爱。 首先,CSDN共享单车服务覆盖范围广泛。无论是商业区、学校、居民小区还是公园等场所,都能看到CSDN共享单车的身影。这为市民提供了更多的绿色出行选择,减少了交通拥堵与环境污染。 其次,CSDN共享单车推行了一套科学的运营系统。通过智能锁、GPS定位等技术手段,管理者可以准确掌握每辆车的位置、使用情况等信息,从而实现车辆调度和维护。这不仅能够提高车辆利用率,减少了用户等待时间,还能保证车辆的良好状态,为用户提供良好的骑行体验。 最后,CSDN共享单车用户体验方面也下了很多功夫。用户可以通过APP查询附近的空闲车辆,预约车辆等待用车,还可以查看骑行轨迹、消费记录等信息。此外,CSDN还提供了多种支付方式,如支付宝、微信支付等,方便用户进行支付结算。 总的来说,深圳的共享单车市场竞争激烈,但CSDN作为其中一家知名企业,在市场份额和用户口碑方面都具备一定的优势。未来,随着科技的不断发展和人们对绿色出行的需求不断增加,CSDN的共享单车将会继续发展壮大,给人们的出行带来更多便利与选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值