电商体系

电商数据分析报告

2018-11-12  本文已影响71人  努力进步的大白菜

一、数据集介绍

该数据集是跨国数据集,包含2010年12月12日至2011年12月9日期间发生的所有在英国注册的非商店在线零售业务的交易。该公司主要销售礼品,并且大部分客户是批发商。

二、字段介绍

InvoiceNo: 发票号码,每笔交易分配唯一的6位整数,而退货订单的代码以字母'c'开头。
StockCode: 产品代码,每个不同的产品分配唯一的5位整数。
Description: 产品描述,对每件产品的简略描述。
Quantity: 产品数量,每笔交易的每件产品的数量。
InvoiceDate: 发票日期和时间,每笔交易发生的日期和时间。
UnitPrice: 单价(英镑),单位产品价格。
CustomerID:顾客号码,每个客户分配唯一的5位整数。
Country: 国家的名字,每个客户所在国家/地区的名称。

三、分析目的

通过分析销售数据来了解在线零售业务的消费情况,分析用户消费数据分析用户消费行为。

分析问题:

四、分析内容

载入库和获取数据

# 载入库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# 设置中文编码和负号的正常显示
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

# 导入数据
df = pd.read_csv('data.csv', encoding='ISO-8859-1')
df.head()
数据集前5行.JPG
df.info()
数据集信息.JPG

用户ID部分缺失,无法通过其他信息填充,因此后期清洗将用户ID缺失记录删除。

df.describe()
数据集统计信息.JPG
# 将产品数量为负数的记录数与退款订单的记录数进行对比
df[df['Quantity']<0].count() == df['InvoiceNo'].str.startswith('C').sum()
验证数据.JPG

部分产品数量是负数,经过验证是退款订单的产品数为负数

数据清洗和处理

# 删除用户ID缺失的记录
df = df[df['CustomerID'].notnull()].copy()

# 数据类型转换
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
df['CustomerID'] = df['CustomerID'].astype('O')

# 计算时间变量
df['Month'] = df['InvoiceDate'].values.astype('datetime64[M]')
df['Day'] = df['InvoiceDate'].dt.day
df['Dayofweek'] = df['InvoiceDate'].dt.dayofweek
df['Hour'] = df['InvoiceDate'].dt.hour

# 计算价格
df['Price'] = df['Quantity'] * df['UnitPrice']

探索性数据分析

商店销售情况分析

原数据集粒度为订购商品,转换为订单粒度,看一下数据情况

# 订单描述性统计分析
df.groupby('InvoiceNo')[['Quantity','Price']].sum().describe()
订单粒度描述性统计分析.JPG

因为零售商大部分客户是批发商,用户每笔订单平均产品数量和平均金额波动大,中位数和75分位数相差挺大,说明部分批发商批发数量大。

每月成交金额
# 商店消费情况绘图函数
def store_plot(data, xlab, ylab, title):
    plt.figure(figsize=(10,5))
    plt.plot(data)
    plt.xlabel(xlab)
    plt.ylabel(ylab)
    plt.title(title)

# 每月成交金额,包含退款订单的金额
store_plot(data=df[df['Quantity']>0].groupby('Month')['price'].sum(), 
           xlab='月份', 
           ylab='成交金额', 
           title='每月成交金额')
每月成交金额.jpeg

从2010年12月至2011年11月月成交金额总体呈现上涨趋势,从2010年8月开始高涨,可能是因为公司主要销售的产品包括礼品,而后半年国外有各种节日导致对礼品需求大增,也有可能是接近年底有各种促销活动,2011年2月和2011年4月都有一个明显的下降,暂时不清楚具体原因。

每月销售金额
 # 每月销售金额,成交金额-退款金额
store_plot(data=df.groupby('Month')['Price'].sum(), 
           xlab='月份', 
           ylab='销售金额', 
           title='每月销售金额')
每月销售金额.jpeg

月销售金额跟月成交金额趋势非常相似,可以看到2011年1月销售金额较成交金额有较大差异,可能是由于这个月的退款金额比较大。

每月消费人数
# 每月消费人数
store_plot(data=df[df['Quantity']>0].groupby('Month')['CustomerID'].nunique(), 
           xlab='月份', 
           ylab='消费人数', 
           title='每月消费人数')
每月消费人数.jpeg
每月订单数量
# 每月订单数量
store_plot(data=df[df['Quantity']>0].groupby('Month')['InvoiceNo'].nunique(), 
           xlab='月份', 
           ylab='订单数量', 
           title='每月订单数量')
每月订单数量.jpeg
每月消费产品数量
# 每月销售产品数量
store_plot(data=df[df['Quantity']>0].groupby('Month')['Quantity'].sum(), 
           xlab='月份', 
           ylab='销售产品数量', 
           title='每月销售产品数量')
每月销售产品数量.jpeg
每月客单价
# 每月客单价
store_plot(data=df[df['Quantity']>0].groupby('Month')['Price'].sum()/df[df['Quantity']>0].groupby('Month')['InvoiceNo'].nunique(), 
           xlab='月份', 
           ylab='客单价', 
           title='每月客单价')
每月客单价.jpeg
消费国家分布
# 国家用户数量分布
plt.figure(figsize=(12,8))
df[df['Quantity']>0].groupby('Country')['CustomerID'].nunique().sort_values(ascending=True).plot.barh()
plt.xlabel('用户数量')
plt.ylabel('国家')
plt.title('不同国家用户数量')
不同国家用户数量分布.png
# 国家订单数量分布
plt.figure(figsize=(12,8))
df[df['Quantity']>0].groupby('Country')['InvoiceNo'].nunique().sort_values(ascending=True).plot.barh()
plt.xlabel('订单数量')
plt.ylabel('国家')
plt.title('不同国家订单数量')
不同国家订单数量分布.png
# 国家成交金额分布
plt.figure(figsize=(12,8))
df[df['Quantity']>0].groupby('Country')['Price'].sum().sort_values(ascending=True).plot.barh()
plt.xlabel('成交金额')
plt.ylabel('国家')
plt.title('不同国家成交金额')
不同国家成交金额分布.png
下单时间分布
# 星期
plt.figure(figsize=(12,8))
df[df['Quantity']>0].groupby('Dayofweek')['InvoiceNo'].nunique().plot.bar()
plt.xticks(range(6),['周一','周二','周三','周四','周五','周日'], rotation=0)
plt.xlabel('星期')
plt.ylabel('订单数')
plt.title('订单数随星期变化')
订单数随星期变化.jpeg

绝大多数订单在周一至周四完成。

# 小时
plt.figure(figsize=(12,8))
df[df['Quantity']>0].groupby('Hour')['InvoiceNo'].nunique().plot()
plt.xlabel('小时')
plt.ylabel('订单数')
plt.title('订单数随小时变化')
订单数随小时变化.jpeg

订单集中在上午10点至下午3点,中午12点是高峰期,这段时间内注意维持好网站的稳定性。

每月退款订单数
# 每月退款订单数量
store_plot(data=df[df['Quantity']<0].groupby('Month')['InvoiceNo'].nunique(), 
           xlab='月份', 
           ylab='退款订单数量', 
           title='每月退款订单数量')
每月退款订单数量.jpeg

每月退款订单数量2010年12月至2011年10月围绕280上下波动,略微呈上涨趋势,虽然2011年11月订单数量非常大,但同时退款订单数量也非常大。

每月退款金额
# 每月退款金额
store_plot(data=df[df['Quantity']<0].groupby('Month')['Price'].sum().abs(), 
           xlab='月份', 
           ylab='退款金额', 
           title='每月退款金额')
每月退款金额.jpeg
每月退款率
# 每月退款率
store_plot(data=df[df['Quantity']<0].groupby('Month')['InvoiceNo'].nunique()/df[df['Quantity']>0].groupby('Month')['InvoiceNo'].nunique(), 
           xlab='月份', 
           ylab='退款率', 
           title='每月退款率')
每月退款率.jpeg

每月退款率呈下降趋势,这是个不错的现象。

用户消费行为分析
用户消费次数、用户消费金额、用户购买产品数量

将数据集粒度转换成用户,看一下用户消费行为。

df[df['Quantity']>0].groupby('CustomerID').agg({'InvoiceNo':'nunique',
                                                'Quantity':'sum',
                                                'Price':'sum'}).describe()
用户粒度描述性统计.JPG
消费次数与消费金额关系
# 消费次数与消费金额关系
plt.figure(figsize=(12,8))
plt.scatter(x=df[df['Quantity']>0].groupby('CustomerID')['InvoiceNo'].nunique(),
            y=df[df['Quantity']>0].groupby('CustomerID')['Price'].sum())
plt.xlabel('消费次数')
plt.ylabel('消费金额')
plt.title('消费次数与消费金额关系')
plt.savefig('消费次数与消费金额关系.jpeg')
消费次数与消费金额关系.jpeg

消费次数和消费金额有较弱的相关性,用户消费次数越大,消费金额越大,可以引导用户多次消费,那么多长时间召回呢?我们来看看用户的消费间距。

用户消费周期
# 用户消费周期
purchase_time = df[df['Quantity']>0].groupby('CustomerID').apply(lambda x: x['InvoiceDate']-x['InvoiceDate'].shift()).dt.days
purchase_time.describe()
用户消费周期.JPG
purchase_time[purchase_time>0].describe()
老用户消费周期.JPG

至少消费两次的用户有一半消费间隔在一个月内,75%消费间隔在两个月内,可以在10天、28天、58天三个时间点对用户进行推送和提醒。

新用户、活跃用户、不活跃用户、回流用户、回流率
pivoted_amount = df[df['Quantity']>0].pivot_table(index='CustomerID',
                                                  columns='Month',
                                                  values='Price',
                                                  aggfunc='mean').fillna(0)
columns_month = df['Month'].sort_values().astype('str').unique()
pivoted_amount.columns = columns_month

pivoted_purchase = pivoted_amount.applymap(lambda x: 1 if x>0 else 0)

# 用户分层
def active_status(data):
    status = []
    for i in range(13):
        
        # 若本月没有消费
        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('active')
    return pd.Series(status, index=columns_month)

pivoted_purchase_status = pivoted_purchase.apply(lambda x: active_status(x), axis=1)

purchase_status_counts = pivoted_purchase_status.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))

purchase_status_counts.fillna(0).T.plot.area(figsize=(12,6))
plt.xticks(range(12), columns_month)
plt.title('用户分层')
用户分层.jpeg
# 回流率
return_rate = purchase_status_counts.apply(lambda x:x / x.sum(), axis=1)
return_rate.loc['return'].plot(figsize=(12,6))
plt.xticks(range(1,13), columns_month)
plt.title('回流率')
回流率.jpeg

在后期消费中新用户占比减少,回流用户和活跃用户增多,回流率一直在上涨,整体用户质量不错。

复购率和回购率
# 复购率
pivoted_counts = df[df['Quantity']>0].pivot_table(index='CustomerID', 
                                                  columns='Month',
                                                  values='InvoiceNo',
                                                  aggfunc='nunique').fillna(0)
columns_month = df['Month'].sort_values().astype('str').unique()
pivoted_counts.columns = columns_month

pivoted_counts_transf = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)

(pivoted_counts_transf.sum() / pivoted_counts_transf.count()).plot(figsize=(12,6))
plt.xticks(range(12), columns_month)
plt.title('复购率'
复购率.jpeg

由于早期新用户占比大,前期复购率比后期复购率低,且后期复购率波动大,但复购率基本在20%以上。

# 回购率
def purchase_return(data):
    status = []
    for i in range(12):
        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 pd.Series(status, index=columns_month)

pivoted_purchase_return = pivoted_purchase.apply(purchase_return,axis=1)

(pivoted_purchase_return.sum() / pivoted_purchase_return.count()).plot(figsize=(12,6))
plt.xticks(range(12), columns_month)
plt.title('回购率')
回购率.jpeg

用户的回购率高于复购率,都大于30%,后期因为回流用户增多,这段时间老用户的忠诚度表现较好,所以后期回购率较高。

RFM
# rfm
rfm = df[df['Quantity']>0].groupby('InvoiceNo').agg({'daydiff':'min',
                                                     'Quantity':'sum',
                                                     'Price':'sum'})
rfm.rename(columns={'daydiff':'R',
                    'Quantity':'F',
                    'Price':'M'},inplace=True)

def rfm_func(x):
    level = x.apply(lambda x: '1' if x > 0 else '0')
    label = level.R + level.F + level.M
    d = {
         '111':'重要价值客户', 
         '011':'重要保持客户', 
         '101':'重要挽留客户',
         '001':'重要发展客户',
         '110':'一般价值客户',
         '010':'一般保持客户',
         '100':'一般挽留客户',
         '000':'一般发展客户'
    }
    result = d[label]
    return result

rfm['label'] = rfm[['R','F','M']].apply(lambda x: x-x.median()).apply(rfm_func, axis=1)
rfm.groupby('label').sum()
rfm分层.JPG
rfm.groupby('label')['M'].count()
rfm用户数量.JPG

五、总结

上一篇下一篇

猜你喜欢

热点阅读