在线零售用户行为数据分析

01. 数据来源及说明

背景简介:

英国注册的非商店在线零售的所有交易,该公司主要销售独特的全时礼品。公司的许多客户都是批发商。

数据来源:

数据集来自UCI加州大学欧文分校机器学习库:archive.ics.uci.edu/ml/datasets/online+retail#

数据集为xlsx格式,有2个表,一个Year 2009-2010,另一个表Year 2010-2011。数据共计8个字段,541908条。具体字段如下:

InvoiceNo: 发票号码;6位整数,每次交易都有一个不重复的发票号码,若以字母'C'开头,则表示该订单被取消。

StockCode: 产品代码;5为整数,每个单品都有一个唯一的商品代码

Description: 商品名称;例如:CARD I LOVE LONDON

Quantity: 每次交易中每个商品的数量

UnitPrice: 每单位商品价格,以英镑表示: £45.23

InvoiceDate: 每次交易发生的日期和时间

CustomerID: 顾客ID,每个顾客有一个唯一的5位整数

Country: 顾客所在国家/地区名称

02. 提出问题

客户行为:客户的生命周期、留存情况、购买周期如何?

03. 数据清洗

1. 导入数据


import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

%matplotlib inline

plt.style.use('ggplot')

plt.rcParams["font.sans-serif"]='SimHei'#解决中文乱码

plt.rcParams['axes.unicode_minus'] = False#解决负号无法正常显示的问题

# 数据读取

df = pd.read_excel(r"C:\Users\wxw\Downloads\Online Retail.xlsx",sheet_name='Online Retail',dtype=str)

df.head()

image

2.缺失值与重复值


# 查看数据情况

df.info()

image

# 重复值处理

a1 = df.shape[0]

df.drop_duplicates(inplace =True)

a2 = df.shape[0]

print('删除后记录量:',a2,'  删除记录量:',a1-a2)

image

# 重置索引

df.reset_index(drop = True,inplace =True)

# 缺失值处理

df.isnull().sum()

image

3.一致化处理


# 一致化处理

# 调整数据类型

df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'],errors = 'coerce')

df['Quantity'] = df['Quantity'].astype('int32')

df['UnitPrice'] = df['UnitPrice'].astype('float')

# 新增时间维度字段

df['Date'] = pd.to_datetime(df['InvoiceDate'].dt.date,errors ='coerce')

df['Month'] = df['InvoiceDate'] .astype('datetime64[M]')

# 新增销售金额字段

df['Sales_volume'] = df['Quantity']*df['UnitPrice']

# 异常值处理

df.describe()

image

# 删除Quantity & UnitPrice 为负值的数据

df =df[(df['Quantity'] > 0) & (df['UnitPrice'] > 0)]

df.describe()

image

04. 构建模型和分析问题

** 1.用户的生命周期**

1.1. 整体用户的生命周期情况


# 用户的生命周期

max_date = df.groupby(['CustomerID'])[['Date']].max()

min_date = df.groupby(['CustomerID'])[['Date']].min()

life_time = max_date - min_date

life_time.describe()

image

共有4339个有Customer ID的客户,其平均生命周期为132天,中位数则是93天,说明有部分生命周期很长的忠实客户拉高了均值;而最小值和Q1分位数都为0天,说明存在25%以上的客户仅消费了一次,生命周期的分布呈两极分化的状态。


life_time.head()

image

life_time['life_time'] = life_time['Date'].dt.days

life_time['life_time'].hist( bins=30,figsize =(15,8), color = 'r')

plt.xlabel(' 生命周期(天)')

plt.ylabel('用户量')

plt.title('用户生命周期图')

image

许多客户仅消费过一次,没有留存下来,需要更加重视客户初次购买的体验感

**1.2. 去掉消费一次的用户的生命周期情况**
# 去掉消费一次的用户

life_time[life_time['life_time']> 0].life_time.hist(bins =200,figsize =(15,8),color = 'r')

plt.xlabel('生命周期(天)')

plt.ylabel('用户量')

plt.title('用户生命周期(不含一次消费者)')

image

生命周期在0-75天的用户数略高于75-170天,可以考虑加强前70天内对客户的引导。约25%的用户集中在170天-300天,属于较高质量用户的生命周期;而在300天以后,则是数量可观的死忠客户,拥有极高的用户粘性。考虑到这些客户中有许多未进行完整的生命周期 ,实际的客户平均生命周期会更长。

**1.3. 铁粉用户的生命周期情况**
#计算生命周期在300天以上的人数

len(life_time[life_time.life_time > 300])

customer_retention = pd.merge(left = df,right=min_date,on = 'CustomerID',how = 'inner',suffixes = ('','_min'))

customer_retention.head()

image

** 2.用户的留存情况**


customer_retention['DateDiff'] = ((customer_retention.Date - customer_retention.Date_min)).dt.days

customer_retention.head()

image

date_bins = [0, 3, 7, 30, 60, 90, 180]

customer_retention['DateDiffB'] = pd.cut(customer_retention.DateDiff, bins = date_bins)

customer_retention['DateDiffB'].value_counts()

image

retention_pivot = customer_retention.pivot_table(index = ['CustomerID'], columns = ['DateDiffB'], values = ['Sales_volume'], aggfunc= np.sum)

retention_pivot.head(5)

image

retention_pivot_trans = retention_pivot.fillna(0).applymap(lambda x:1 if x > 0 else 0)

retention_pivot_trans.head()

image

(retention_pivot_trans.sum()/ retention_pivot_trans.count()).plot.bar(width=0.5,align="center",figsize =(15,8))

plt.xlabel('时间跨度(天)')

plt.ylabel('百分数(%)')

plt.title('各个时间段的用户留存率')

image

3.2%在第一次消费的次日至3天内有过消费
6.6%的用户在4到7天有过消费,分别有40.5%和37.4%的用户在首次消费后的第二个月内和第三个月内有过购买行为。说明该电商网站的客户群体,其采购并非高频行为,但留存下来的老用户忠诚度却极高。
结合前文,仅有首次购买行为的客户占总客户的37.5%,如能提高这部分群体的留存率,将会带来很高的收益

** 3.用户的消费周期**


df_cycle = customer_retention.drop_duplicates(subset=['CustomerID', 'Date'])

df_cycle.sort_values(by = 'Date') 

image

def diff(g):

    d = g.DateDiff - g.DateDiff.shift()

    return d

last_diff = df_cycle.groupby('CustomerID').apply(diff)

last_diff.hist(bins = 70, figsize = (15, 8), color = 'r')

plt.xlabel('消费周期')

plt.ylabel('频数')

plt.title('用户消费周期分布图')

image

典型的长尾分布,大部分购买行为的消费间隔比较短。但这是所有订单的购买周期分布,并不是对客户个体为统计单位的购买周期分布

所以更细化下


last_diff_customer = last_diff.groupby('CustomerID').mean()

last_diff_customer.hist(bins = 70, figsize = (15, 6), color = 'r')

plt.xlabel('消费周期')

plt.ylabel('用户量')

plt.title('用户消费周期分布图')

image

一个右偏分布,峰值在15-65天,说明大部分留存用户的购买周期集中于此。

** 4. 用户复购情况**
复购率的定义是在某时间窗口内消费两次及以上的用户在总消费用户中占比


df_order = df.groupby(['CustomerID','Date']).sum()

df_order.reset_index(inplace = True)

df_order['month'] = df_order.Date.astype('datetime64[M]')

pivoted_counts = df_order.pivot_table(index = 'CustomerID' ,columns = 'month',

                              values = 'Date' , aggfunc = 'count').fillna(0)

columns_month = df_order.month.sort_values().unique()

pivoted_counts.columns = columns_month

pivoted_counts.head()

image

#进行数据转化吧消费2次以上的记为1,消费一次的记为0,没有消费的记为NaN

pivoted_counts_t = pivoted_counts.applymap(lambda x: 1 if x > 1

                                              else np.NaN if x == 0 else 0)

pivoted_counts_t.head()

image

ax = (pivoted_counts_t.sum() / pivoted_counts_t.count()).plot(figsize = (15,8))

plt.xlabel('月份')

plt.ylabel('复购率')

plt.title('用户月度复购率情况')

image

从图上看,复购率有很强的季节性,复购率从11月 至 第二年1月 保持上升的势头,而在1 月 至 11月 表现的相对平稳,在17%的值上下波动。

05. 结论

1、从用户的消费周期来看,呈现典型的长尾分布,大部分用户的消费间隔确实比较短。不妨将时间召回点设为消费后立即赠送优惠券,从策略看,用户首次消费后应该花费更多的引导其进行多次消费,这会带来更高的增量。
2、要维护铁粉用户,将100天后活跃用户引导成铁粉用户。
3、客户平均生命周期为132天,两极分化严重;有重复购买行为的用户生命周期为200多天,远超总体均值132天,其中总体25%顾客只有一次消费,开展回归优惠活动,引导顾客再次消费,大幅提升顾客生命周期

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 一、背景介绍 电子商务相对于传统零售业来说,最大特点就是一切都可以通过数据化来监控和改进。通过数据可以看到用户从哪...
    全糖布丁烤奶阅读 2,975评论 0 7
  • 前言: 数据分析的一般流程,首先确定问题,再带着问题进行下面的分析步骤 0、分析问题 一份数据可以分析很多维度,为...
    tuimer阅读 1,699评论 1 5
  • 做事的那份纯粹度还是没完全出来,条理好像也还不是很清楚。今天专程去公司看九月份的销售及公司氛围,又有半余月...
    李丁梅阅读 241评论 2 2
  • 今天翻阅空间日志,看到一篇6年前写的一篇文章,看完还是忍不住激动得直落泪,让我不时地重温一下新生命带来的感动。 今...
    梁兰芳阅读 747评论 0 0
  • 从2000年开始玩计算机,不知不觉18个年头过去了,感叹时间飞逝,网络安全技术日新月异。出于对计算机的热爱,从材料...
    simeon2015阅读 561评论 0 0