电商平台零售数据分析
python匿名函数 lambda
matplotlib和pyecharts基础绘图 及美化
pandas模块的数据透视表pivot_table 函数、分类groupby 函数、分段cut 函数
1. 明确目标
1.1 每月退货率
2.1 利用RFM模型对用户群进行细分
用户分类(RFM模型),是网点衡量当前用户价值和客户潜在价值的重要工具和手段。对比分析不同用户群体在时间、地区等维度下交易量,交易金额指标,并根据分析结果提出优化建议
R(recent): 最近一次消费时间(最近一次消费到参考时间的长度)
F(frequency): 消费频次(单位时间消费了多少次)
M(money): 消费金额(单位时间内总消费金额)
2. 读取和理解数据
import pandas as pd import numpy as np import matplotlib. pyplot as plt from pyecharts. charts import Pie import pyecharts. options as opts plt. rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] plt. rcParams[ 'axes.unicode_minus' ] = False
df = pd. read_csv( 'data.csv' , encoding= 'utf-8' )
df. head( 3 )
InvoiceNo
StockCode
Description
Quantity
InvoiceDate
UnitPrice
CustomerID
Country
0
85123A
WHITE HANGING HEART T-LIGHT HOLDER
6
12/1/2010 8:26
2.55
17850.0
United Kingdom
1
71053
WHITE METAL LANTERN
6
12/1/2010 8:26
3.39
17850.0
United Kingdom
2
84406B
CREAM CUPID HEARTS COAT HANGER
8
12/1/2010 8:26
2.75
17850.0
United Kingdom
df. shape
(, 8)
df. info( )
<class 'pandas.core.frame.DataFrame'> RangeIndex: entries, 0 to Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 InvoiceNo non-null object 1 StockCode non-null object 2 Description non-null object 3 Quantity non-null int64 4 InvoiceDate non-null object 5 UnitPrice non-null float64 6 CustomerID non-null float64 7 Country non-null object dtypes: float64(2), int64(1), object(5) memory usage: 33.1+ MB
3. 数据清洗
3.1 缺失处理
df. apply ( lambda x: sum ( x. isnull( ) ) / len ( x) , axis= 0 )
InvoiceNo 0.000000 StockCode 0.000000 Description 0.002683 Quantity 0.000000 InvoiceDate 0.000000 UnitPrice 0.000000 CustomerID 0. Country 0.000000 dtype: float64
df. drop( [ 'Description' ] , axis= 1 , inplace= True )
df[ 'CustomerID' ] = df[ 'CustomerID' ] . fillna( 'U' )
df[ 'amount' ] = df[ 'Quantity' ] * df[ 'UnitPrice' ]
'''plan A:''' '''plan B:''' '''plan C''' df[ 'Date' ] = pd. to_datetime( df[ 'InvoiceDate' ] ) . dt. date df[ 'Time' ] = pd. to_datetime( df[ 'InvoiceDate' ] ) . dt. time df[ 'Date' ] = pd. to_datetime( df[ 'Date' ] ) df. drop( [ 'InvoiceDate' ] , axis= 1 , inplace= True )
df. head( )
InvoiceNo
StockCode
Quantity
UnitPrice
CustomerID
Country
amount
Date
Time
0
85123A
6
2.55
17850
United Kingdom
15.30
2010-12-01
08:26:00
1
71053
6
3.39
17850
United Kingdom
20.34
2010-12-01
08:26:00
2
84406B
8
2.75
17850
United Kingdom
22.00
2010-12-01
08:26:00
3
84029G
6
3.39
17850
United Kingdom
20.34
2010-12-01
08:26:00
4
84029E
6
3.39
17850
United Kingdom
20.34
2010-12-01
08:26:00
df = df. drop_duplicates( )
3.2 重复值
df. describe( )
Quantity
UnitPrice
amount
count
.000000
.000000
.000000
mean
9.
4.
18.
std
219.
97.
380.
min
-80995.000000
-11062.060000
-.
25%
1.000000
1.
3.
50%
3.000000
2.080000
9.
75%
10.000000
4.
17.
max
80995.000000
38970.000000
.
3.3 异常分析
df2 = df. loc[ df[ 'UnitPrice' ] <= 0 ]
df2. shape[ 0 ] / df. shape[ 0 ]
0.00
df2[ 'UnitPrice' ] . value_counts( )
0.00 2510 -11062.06 2 Name: UnitPrice, dtype: int64
4. 数据分析与可视化
4.1 退货率
df1 = df. loc[ df[ 'Quantity' ] <= 0 ]
returns = pd. pivot_table( df1, index= df1[ 'Date' ] . dt. year, columns= df1[ 'Date' ] . dt. month, values= 'amount' , aggfunc= {
'amount' : np. sum } ) returns
Date
1
2
3
4
5
6
7
8
9
10
11
12
Date
2010
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
-74729.12
2011
-.05
-25519.15
-34201.28
-44600.65
-47202.51
-70569.78
-37919.13
-54330.8
-38838.51
-81895.5
-47720.98
-.27
df3 = df[ ( df[ 'Quantity' ] > 0 ) & ( df[ 'UnitPrice' ] > 0 ) ]
sales = pd. pivot_table( df3, index= df3[ 'Date' ] . dt. year, columns= df3[ 'Date' ] . dt. month, values= 'amount' , aggfunc= {
'amount' : np. sum } ) sales
Date
1
2
3
4
5
6
7
8
9
10
11
12
Date
2010
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
.73
2011
.61
.56
.26
.491
.76
.01
.121
.38
.192
.73
.78
.33
4.1.1 2011年每月退货率
return_rate = np. abs ( returns) / sales return_rate
Date
1
2
3
4
5
6
7
8
9
10
11
12
Date
2010
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
0.090972
2011
0.
0.048836
0.047753
0.08306
0.061359
0.092788
0.052807
0.071692
0.036764
0.071135
0.031744
0.
4.1.2 2011年月平均退货率
avg_return = return_rate[ 1 : 2 ] . mean( axis= 1 ) . values[ 0 ] avg_return
0.055831
4.1.3 可视化
return_rate_11 = [ round ( i, 2 ) for i in return_rate. values. tolist( ) [ 1 ] ] month = return_rate. columns. tolist( )
from matplotlib. ticker import FuncFormatter plt. figure( figsize= ( 10 , 5 ) , dpi= 70 ) plt. style. use( 'fivethirtyeight' ) plt. plot( month, return_rate_11, 'bo-' , lw= 2 , label= '月退货率' ) plt. title( '每月退货率' , fontdict= {
'color' : 'black' , 'fontsize' : 16 } , pad= 12 ) plt. xlabel( '月份' , fontdict= {
'color' : 'black' , 'fontsize' : 14 } ) plt. ylabel( '退货率' , fontdict= {
'color' : 'black' , 'fontsize' : 14 } ) plt. yticks( [ ] ) plt. xticks( np. arange( 1 , 13 ) ) plt. gca( ) . yaxis. set_major_formatter( FuncFormatter( lambda x, position: '{:.1f}%' . format ( x* 100 ) ) ) for i, j in zip ( month, return_rate_11) : plt. text( i- 0.02 , j- 0.02 , '{:.1f}%' . format ( j* 100 ) , bbox= dict ( facecolor= 'red' , alpha= 0.1 ) ) plt. axhline( y= avg_return, ls= '--' , color= 'r' , lw= 2 , label= '平均退货率' ) plt. annotate( '{:.1f}%' . format ( round ( avg_return, 3 ) * 100 ) , xy= ( 8 , avg_return) , xytext= ( 8.5 , avg_return+ 0.05 ) , arrowprops= dict ( facecolor= 'red' , shrink= 0.05 ) ) plt. grid( b= False ) plt. legend( loc= 'best' ) plt. show( )
4.2 用户分级(RFM模型)
4.1.1 计算R,F,M
customer_newest_consume = df3. groupby( 'CustomerID' ) [ 'Date' ] . max ( ) customer_newest_consume. head( )
CustomerID 12346.0 2011-01-18 12347.0 2011-12-07 12348.0 2011-09-25 12349.0 2011-11-21 12350.0 2011-02-02 Name: Date, dtype: datetime64[ns]
newest_time_consume = df3[ 'Date' ] . max ( )
value_R = ( newest_time_consume - customer_newest_consume) . dt. days value_R
CustomerID 12346.0 325 12347.0 2 12348.0 75 12349.0 18 12350.0 310 ... 18281.0 180 18282.0 7 18283.0 3 18287.0 42 U 0 Name: Date, Length: 4339, dtype: int64
value_F = df3. groupby( 'CustomerID' ) [ 'InvoiceNo' ] . nunique( ) value_F
CustomerID 12346.0 1 12347.0 7 12348.0 4 12349.0 1 12350.0 1 ... 18281.0 1 18282.0 2 18283.0 16 18287.0 3 U 1428 Name: InvoiceNo, Length: 4339, dtype: int64
value_M = df3. groupby( 'CustomerID' ) [ 'amount' ] . sum ( ) value_M
CustomerID 12346.0 7.e+04 12347.0 4.e+03 12348.0 1.e+03 12349.0 1.e+03 12350.0 3.e+02 ... 18281.0 8.082000e+01 18282.0 1.e+02 18283.0 2.045530e+03 18287.0 1.e+03 U 1.e+06 Name: amount, Length: 4339, dtype: float64
4.2.2 观察R,F,M的数据分布
value_R. describe( )
count 4339.000000 mean 92.038258 std 100.010502 min 0.000000 25% 17.000000 50% 50.000000 75% 141. max 373.000000 Name: Date, dtype: float64
plt. hist( value_R, bins= 30 ) plt. show( )
value_M. describe( )
count 4.e+03 mean 2.e+03 std 2.e+04 min 3.e+00 25% 3.065050e+02 50% 6.e+02 75% 1.e+03 max 1.e+06 Name: amount, dtype: float64
plt. hist( value_M, bins= 30 ) plt. show( )
plt. hist( value_M[ value_M < 2000 ] , bins= 30 ) plt. show( )
value_F. describe( )
count 4339.000000 mean 4. std 22. min 1.000000 25% 1.000000 50% 2.000000 75% 5.000000 max 1428.000000 Name: InvoiceNo, dtype: float64
plt. hist( value_F[ value_F < 20 ] , bins= 30 ) plt. show( )
4.2.3 对R,F,M分段(给予权重)
print ( ' value_R:\t' , value_R. quantile( [ 0 , 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.6 , 0.7 , 0.8 , 0.9 , 1 ] ) . tolist( ) ) print ( ' value_F:\t' , value_F. quantile( [ 0 , 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.6 , 0.7 , 0.8 , 0.9 , 1 ] ) . tolist( ) ) print ( ' value_M:\t' , [ round ( i) for i in value_M. quantile( [ 0 , 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.6 , 0.7 , 0.8 , 0.9 , 1 ] ) . tolist( ) ] )
value_R: [0.0, 5.0, 12.0, 22.0, 32.0, 50.0, 71.0, 108.0, 179.0, 262.03, 373.0] value_F: [1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 3.0, 4.0, 6.0, 9.0, 1428.0] value_M: [4, 155, 250, 350, 488, 669, 934, 1348, 2056, 3641, ]
bins_R = [ 0 , 30 , 90 , 180 , 360 , 720 ] bins_F = [ 1 , 2 , 5 , 10 , 20 , 5000 ] bins_M = [ 0 , 500 , 2000 , 5000 , 10000 , ]
score_R = pd. cut( value_R, bins_R, labels= [ 5 , 4 , 3 , 2 , 1 ] , right= False ) score_F = pd. cut( value_F, bins_F, labels= [ 1 , 2 , 3 , 4 , 5 ] , right= False ) score_M = pd. cut( value_M, bins_M, labels= [ 1 , 2 , 3 , 4 , 5 ] , right= False )
score_R
CustomerID 12346.0 2 12347.0 5 12348.0 4 12349.0 5 12350.0 2 .. 18281.0 2 18282.0 5 18283.0 5 18287.0 4 U 5 Name: Date, Length: 4339, dtype: category Categories (5, int64): [5 < 4 < 3 < 2 < 1]
rfm = pd. concat( [ score_R, score_F, score_M] , axis= 1 )
rfm. rename( columns= {
'Date' : 'R_value' , 'InvoiceNo' : 'F_value' , 'amount' : 'M_value' } , inplace= True )
rfm
R_value
F_value
M_value
CustomerID
12346.0
2
1
5
12347.0
5
3
3
12348.0
4
2
2
12349.0
5
1
2
12350.0
2
1
1
...
...
...
...
18281.0
2
1
1
18282.0
5
2
1
18283.0
5
4
3
18287.0
4
2
2
U
5
5
NaN
4339 rows × 3 columns
rfm. info( )
<class 'pandas.core.frame.DataFrame'> Index: 4339 entries, 12346.0 to U Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 R_value 4339 non-null category 1 F_value 4339 non-null category 2 M_value 4336 non-null category dtypes: category(3) memory usage: 47.2+ KB
for i in [ 'R_value' , 'F_value' , 'M_value' ] : rfm[ i] = rfm[ i] . astype( float )
rfm. describe( )
R_value
F_value
M_value
count
4339.000000
4339.000000
4336.000000
mean
3.
2.028117
1.
std
1.
0.
0.
min
1.000000
1.000000
1.000000
25%
3.000000
1.000000
1.000000
50%
4.000000
2.000000
2.000000
75%
5.000000
3.000000
2.000000
max
5.000000
5.000000
5.000000
4.2.4 分级标准
rfm[ 'R' ] = np. where( rfm[ 'R_value' ] > 3.82 , '高' , '低' ) rfm[ 'F' ] = np. where( rfm[ 'F_value' ] > 2.03 , '高' , '低' ) rfm[ 'M' ] = np. where( rfm[ 'M_value' ] > 1.89 , '高' , '低' )
rfm
R_value
F_value
M_value
R
F
M
CustomerID
12346.0
2.0
1.0
5.0
低
低
高
12347.0
5.0
3.0
3.0
高
高
高
12348.0
4.0
2.0
2.0
高
低
高
12349.0
5.0
1.0
2.0
高
低
高
12350.0
2.0
1.0
1.0
低
低
低
...
...
...
...
...
...
...
18281.0
2.0
1.0
1.0
低
低
低
18282.0
5.0
2.0
1.0
高
低
低
18283.0
5.0
4.0
3.0
高
高
高
18287.0
4.0
2.0
2.0
高
低
高
U
5.0
5.0
NaN
高
高
低
4339 rows × 6 columns
rfm[ 'value' ] = rfm[ 'R' ] . str [ : ] + rfm[ 'F' ] . str [ : ] + rfm[ 'M' ] . str [ : ]
rfm
R_value
F_value
M_value
R
F
M
value
CustomerID
12346.0
2.0
1.0
5.0
低
低
高
低低高
12347.0
5.0
3.0
3.0
高
高
高
高高高
12348.0
4.0
2.0
2.0
高
低
高
高低高
12349.0
5.0
1.0
2.0
高
低
高
高低高
12350.0
2.0
1.0
1.0
低
低
低
低低低
...
...
...
...
...
...
...
...
18281.0
2.0
1.0
1.0
低
低
低
低低低
18282.0
5.0
2.0
1.0
高
低
低
高低低
18283.0
5.0
4.0
3.0
高
高
高
高高高
18287.0
4.0
2.0
2.0
高
低
高
高低高
U
5.0
5.0
NaN
高
高
低
高高低
4339 rows × 7 columns
4.2.5 确认分级
rfm[ 'value' ] = rfm[ 'value' ] . str . strip( )
def trans_value ( x) : if x == '高高高' : return '重要价值客户' elif x == '高低高' : return '重要发展客户' elif x == '高高低' : return '一般价值客户' elif x == '低高高' : return '重要保持客户' elif x == '低低高' : return '重要挽留客户' elif x == '高低低' : return '一般发展客户' elif x == '低高低' : return '一般保持客户' else : return '一般挽留客户'
rfm[ '用户等级' ] = rfm[ 'value' ] . apply ( trans_value)
rfm
R_value
F_value
M_value
R
F
M
value
用户等级
CustomerID
12346.0
2.0
1.0
5.0
低
低
高
低低高
重要挽留客户
12347.0
5.0
3.0
3.0
高
高
高
高高高
重要价值客户
12348.0
4.0
2.0
2.0
高
低
高
高低高
重要发展客户
12349.0
5.0
1.0
2.0
高
低
高
高低高
重要发展客户
12350.0
2.0
1.0
1.0
低
低
低
低低低
一般挽留客户
...
...
...
...
...
...
...
...
...
18281.0
2.0
1.0
1.0
低
低
低
低低低
一般挽留客户
18282.0
5.0
2.0
1.0
高
低
低
高低低
一般发展客户
18283.0
5.0
4.0
3.0
高
高
高
高高高
重要价值客户
18287.0
4.0
2.0
2.0
高
低
高
高低高
重要发展客户
U
5.0
5.0
NaN
高
高
低
高高低
一般价值客户
4339 rows × 8 columns
4.2.6 用户分级结果及可视化
re = rfm[ '用户等级' ] . value_counts( ) re
重要价值客户 1034 重要发展客户 1017 一般挽留客户 923 一般发展客户 829 重要挽留客户 455 重要保持客户 66 一般价值客户 10 一般保持客户 5 Name: 用户等级, dtype: int64
plt. figure( figsize= ( 9 , 5 ) ) plt. bar( re. index. tolist( ) , re. values. tolist( ) , width= 0.5 ) plt. grid( b= False ) plt. xticks( rotation= 45 ) plt. title( '用户等级' ) plt. show( )
pie = ( Pie( init_opts= opts. InitOpts( width= '900px' , height= '500px' , bg_color= '#E6E6FA' ) ) . add( ' ' , data_pair = [ [ x, y] for x, y in zip ( re. index. tolist( ) , re. values. tolist( ) ) ] , rosetype = 'radius' , radius = '55%' , center = [ '50%' , '50%' ] , label_opts= opts. LabelOpts( is_show= False , position= 'center' ) ) . set_global_opts( title_opts= opts. TitleOpts( title= '用户等级比例' , pos_left= 'center' , pos_top= '20' , title_textstyle_opts= opts. TextStyleOpts( color= '#000' ) , ) , legend_opts= opts. LegendOpts( is_show= False ) ) . set_series_opts( tooltip_opts= opts. TooltipOpts( trigger= 'item' , formatter= '{a} <br>{b}: {c} ({d}%)' ) , label_opts= opts. LabelOpts( color= 'rgba(25, 25, 112, 0.8)' ) , ) ) pie. render_notebook( )
5. 结论与建议
1月和12月退货率高于月平均退货率的原因: 根据第一篇实战数据分析知道影响总销量最大的是英国,同时其他也大都是西方国家,1月1日是他们的新年 以及12月25日是圣诞节,购物量大幅增加的同时也导致了退货量的增加。
对于不同等级用户采取针对且有效的营销措施: 非营销专业不知从何说起
等到业务知识充实了再回来补上。。。。。。
今天的文章
实战2-电商平台零售数据分析 分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/87275.html