实战2-电商平台零售数据分析

电商平台零售数据分析

本案例主要用于学习RFM模型,同时加深对数据分析流程的理解和python数据分析相关的coding能力。
其中涉及到:

  • 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)
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER612/1/2010 8:262.5517850.0United Kingdom
153636571053WHITE METAL LANTERN612/1/2010 8:263.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER812/1/2010 8:262.7517850.0United Kingdom
# 数据维度
df.shape
(541909, 8)
# 数据信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 541909 entries, 0 to 541908
Data columns (total 8 columns):
 #   Column       Non-Null Count   Dtype  
---  ------       --------------   -----  
 0   InvoiceNo    541909 non-null  object 
 1   StockCode    541909 non-null  object 
 2   Description  540455 non-null  object 
 3   Quantity     541909 non-null  int64  
 4   InvoiceDate  541909 non-null  object 
 5   UnitPrice    541909 non-null  float64
 6   CustomerID   406829 non-null  float64
 7   Country      541909 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.249267
Country        0.000000
dtype: float64
# Description字段对该数据分析目标无意义,删除
df.drop(['Description'], axis=1, inplace=True)
# 缺失的用户ID填充为'U'
df['CustomerID'] = df['CustomerID'].fillna('U')
# 每个订单的发生额
df['amount'] = df['Quantity'] * df['UnitPrice']
# 将 InvoiceDate字段拆分成两列,之后删除InvoiceDate字段
'''plan A:'''
# df['Date'] = [i.split(' ')[0] for i in df['InvoiceDate']]
# df['Time'] = [i.split(' ')[1] for i in df['InvoiceDate']]

'''plan B:'''
# df = df.join(df['InvoiceDate'].str.split(' ', expand=True))
# df.columns = df.columns.tolist()[:-2] + ['Date', 'Time']

'''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()
InvoiceNoStockCodeQuantityUnitPriceCustomerIDCountryamountDateTime
053636585123A62.5517850United Kingdom15.302010-12-0108:26:00
15363657105363.3917850United Kingdom20.342010-12-0108:26:00
253636584406B82.7517850United Kingdom22.002010-12-0108:26:00
353636584029G63.3917850United Kingdom20.342010-12-0108:26:00
453636584029E63.3917850United Kingdom20.342010-12-0108:26:00
# 删除重复值
df = df.drop_duplicates()

3.2 重复值

df.describe()
QuantityUnitPriceamount
count536639.000000536639.000000536639.000000
mean9.6195004.63266018.122900
std219.13020697.233299380.656313
min-80995.000000-11062.060000-168469.600000
25%1.0000001.2500003.750000
50%3.0000002.0800009.870000
75%10.0000004.13000017.400000
max80995.00000038970.000000168469.600000

3.3 异常分析

# 对单价进行异常分析
df2 = df.loc[df['UnitPrice'] <= 0]
df2.shape[0]/df.shape[0]
0.0046809866595607106
# 异常值中单价的分类
# df2['UnitPrice'].groupby(df2['UnitPrice']).count()
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
Date123456789101112
Date
2010NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN-74729.12
2011-131363.05-25519.15-34201.28-44600.65-47202.51-70569.78-37919.13-54330.8-38838.51-81895.5-47720.98-205089.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
Date123456789101112
Date
2010NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN821452.73
2011689811.61522545.56716215.26536968.491769281.76760547.01718076.121757841.381056435.1921151263.731503329.78637790.33
4.1.1 2011年每月退货率
# 退货率
return_rate = np.abs(returns) / sales
return_rate
Date123456789101112
Date
2010NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN0.090972
20110.1904330.0488360.0477530.083060.0613590.0927880.0528070.0716920.0367640.0711350.0317440.321562
4.1.2 2011年月平均退货率
# 平均退货率
avg_return = return_rate[1:2].mean(axis=1).values[0]
avg_return
0.09249438130255831
4.1.3 可视化
# 取出2011年退货率数据,用于画图
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([0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35])
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
# nunique()去重
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.718360e+04
12347.0    4.310000e+03
12348.0    1.797240e+03
12349.0    1.757550e+03
12350.0    3.344000e+02
               ...     
18281.0    8.082000e+01
18282.0    1.780500e+02
18283.0    2.045530e+03
18287.0    1.837280e+03
U          1.754902e+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.500000
max       373.000000
Name: Date, dtype: float64
plt.hist(value_R, bins=30)
plt.show()

在这里插入图片描述

value_M.describe()
count    4.339000e+03
mean     2.452537e+03
std      2.808589e+04
min      3.750000e+00
25%      3.065050e+02
50%      6.685800e+02
75%      1.660890e+03
max      1.754902e+06
Name: amount, dtype: float64
# 异常值严重影响了数据的分布
plt.hist(value_M, bins=30)
plt.show()

在这里插入图片描述

# 绘制金额小于2000的
plt.hist(value_M[value_M < 2000], bins=30)
plt.show()

在这里插入图片描述

# 中位数是2,而最大值是1428,异常值很严重
value_F.describe()
count    4339.000000
mean        4.600138
std        22.943499
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.2000000000003, 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, 1754902]
# 构建分段指标
bins_R = [0, 30, 90, 180, 360, 720]
bins_F = [1, 2, 5, 10, 20, 5000]
bins_M = [0, 500, 2000, 5000, 10000, 200000]
# 分段,label可以理解为权重,对R而言,值越小表示里目标时间近,所占权重也就更大,F,M同理
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]
# 横向合并,axis=1;纵向合并,axis=0(默认)
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)
# (U,M_value)为NaN原因是在对M分段指标构建时,最大值到20万,而U累计超过175万
rfm
R_valueF_valueM_value
CustomerID
12346.0215
12347.0533
12348.0422
12349.0512
12350.0211
............
18281.0211
18282.0521
18283.0543
18287.0422
U55NaN

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_valueF_valueM_value
count4339.0000004339.0000004336.000000
mean3.8216182.0281171.885609
std1.1748800.9979890.951810
min1.0000001.0000001.000000
25%3.0000001.0000001.000000
50%4.0000002.0000002.000000
75%5.0000003.0000002.000000
max5.0000005.0000005.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_valueF_valueM_valueRFM
CustomerID
12346.02.01.05.0
12347.05.03.03.0
12348.04.02.02.0
12349.05.01.02.0
12350.02.01.01.0
.....................
18281.02.01.01.0
18282.05.02.01.0
18283.05.04.03.0
18287.04.02.02.0
U5.05.0NaN

4339 rows × 6 columns

# 汇总
rfm['value'] = rfm['R'].str[:] + rfm['F'].str[:] + rfm['M'].str[:]
rfm
R_valueF_valueM_valueRFMvalue
CustomerID
12346.02.01.05.0低低高
12347.05.03.03.0高高高
12348.04.02.02.0高低高
12349.05.01.02.0高低高
12350.02.01.01.0低低低
........................
18281.02.01.01.0低低低
18282.05.02.01.0高低低
18283.05.04.03.0高高高
18287.04.02.02.0高低高
U5.05.0NaN高高低

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_valueF_valueM_valueRFMvalue用户等级
CustomerID
12346.02.01.05.0低低高重要挽留客户
12347.05.03.03.0高高高重要价值客户
12348.04.02.02.0高低高重要发展客户
12349.05.01.02.0高低高重要发展客户
12350.02.01.01.0低低低一般挽留客户
...........................
18281.02.01.01.0低低低一般挽留客户
18282.05.02.01.0高低低一般发展客户
18283.05.04.03.0高高高重要价值客户
18287.04.02.02.0高低高重要发展客户
U5.05.0NaN高高低一般价值客户

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. 1月和12月退货率高于月平均退货率的原因:
    根据第一篇实战数据分析知道影响总销量最大的是英国,同时其他也大都是西方国家,1月1日是他们的新年
    以及12月25日是圣诞节,购物量大幅增加的同时也导致了退货量的增加。

  2. 对于不同等级用户采取针对且有效的营销措施:

    非营销专业不知从何说起

    等到业务知识充实了再回来补上。。。。。。

### 回答1: 这是一篇关于大型电商用户行为分析大数据平台的实战文章。文章介绍了如何使用Spark技术构建一个可扩展的、高性能的大数据平台,用于分析电商用户的行为数据。该平台可以处理海量的数据,提供实时的数据分析和可视化展示,帮助电商企业更好地了解用户行为,优化产品和服务,提升用户体验和销售业绩。文章详细介绍了平台的架构设计、数据处理流程、数据分析方法和可视化展示方式,对于从事大数据分析电商业务的人员具有很高的参考价值。 ### 回答2: 大数据平台在如今的电商领域中扮演着越来越重要的角色,帮助电商企业更好地了解用户需求、优化营销策略、提高销售效率和用户体验。而在这个领域中,Spark大数据分析引擎的应用也愈发广泛,帮助企业更好地处理和分析海量的数据。 电商用户行为分析大数据平台的构建需要考虑多个因素,包括数据采集、数据存储和数据处理等方面。其中,数据采集是关键的一环,需要收集用户在电商平台中的各种行为数据,如浏览商品、下单、付款和退款等。这些数据需要经过初步处理和清洗后才能被存储到大数据平台中。 在数据存储方面,Hadoop和HBase是两个常用的大数据存储技术。Hadoop可以将各种不同类型的数据按照文件的形式存储,而HBase则是分布式的、面向列的数据库,可以更好地支持结构化数据的存储和查询。 在数据处理方面,Spark作为一种快速而通用的大数据处理引擎,具有良好的扩展性、高效性和易用性。Spark可以处理非常大的数据集,并且可以在内存中缓存数据以加速处理速度。此外,Spark还提供了一些高级API,如Spark SQL、MLlib和GraphX等,可以帮助企业更高效地进行数据分析和挖掘。 在电商用户行为分析大数据平台的具体使用场景中,Spark可以用于用户行为分析、推荐算法优化、用户画像构建和活动效果评估等方面。例如,可以使用Spark对用户浏览、下单和购买等行为数据进行分析,探索用户行为模式,挖掘用户需求,优化商品推荐和定价策略;同时,可以使用Spark对不同用户群体的行为数据进行整合和分析,为企业提供更准确的用户画像信息,帮助企业更好地了解不同用户群体的特点和需求。通过这些分析,企业可以精准地掌握用户需求,提高产品服务质量和营销效果,加速企业的发展和壮大。 ### 回答3: 随着电商市场的不断发展,对于用户的行为分析越来越重要,为了更好地提升用户体验和销售业绩,企业需要构建一个可靠的电商用户行为分析大数据平台。而Spark大型项目实战电商用户行为分析大数据平台(一)则是这一领域的先锋。 Spark大型项目实战电商用户行为分析大数据平台(一)所涉及到的核心技术主要有三个方面:数据采集、数据处理及数据分析。 首先是数据采集。在电商平台上,用户的行为主要包括页面访问、商品浏览、购物车添加、下单付款等。为了获取这些数据,需要在网站内添加代码或者使用第三方网站统计工具进行数据采集。 其次是数据处理。这一步骤主要利用Spark进行离线数据处理和流式数据处理,包括数据清洗、数据整合、数据融合、数据统计等。对于数据清洗来说,首先需要对数据进行去重、过滤,然后再进行数据整合、数据融合。数据统计则是非常重要的一步,可以统计用户的浏览、下单、付款等行为,以此来评估用户的消费行为和进行推荐。在流式数据处理方面,可以使用Kafka和Spark Streaming对实时数据进行处理,以提升数据处理效率和精确度。 最后是数据分析。通过对采集和处理的数据进行分析,可以对用户消费行为、支付渠道、商品销售情况等进行全面评估和推广分析。可以通过编写Spark程序,使用SQL和Python进行数据分析,从而挖掘出数据中隐藏的价值。例如可以通过用户行为数据来推荐商品、针对用户定制促销策略等。 以上就是Spark大型项目实战电商用户行为分析大数据平台(一)的主要内容。通过使用Spark等技术,企业可以深入了解用户的消费行为,优化促销策略和提高销售业绩,提升用户满意度。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值