电商数据分析

本次分析选用kaggle网站的巴西电商数据,时间跨度从2016年9月-2018年9月,数据集中对客户id进行了处理。

kaggle

分析目标:

1.探索该电商运营情况
2.客户分类,探索不同客户群的营销策略

分析思路
一是探索电商运营情况,主要探索其订单数和销售额的增长情况,客单价情况,以及各区域销售额对比情况,各区域的商品运输时间等指标;
二是构建RFM模型,应用K-Means聚类方法对客户群进行分类,探索不同客户群的营销策略。

评估并清洗数据:

先导入分析中将要用到的python包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
from mpl_toolkits.mplot3d.axes3d import Axes3D
导入数据集
customers = pd.read_csv('olist_customers_dataset.csv')
orders = pd.read_csv('olist_orders_dataset.csv')
order_items = pd.read_csv('olist_order_items_dataset.csv')
order_payments = pd.read_csv('olist_order_payments_dataset.csv')
products = pd.read_csv('olist_products_dataset.csv')
products_name_trans = pd.read_csv('product_category_name_translation.csv')
数据说明:

因为原数据集结构比较复杂,产品数据、客户数据、支付数据都通过相应的键值与订单数据连接,本文着重分析经营情况和客户分类,对数据进行了一定取舍,数据清洗的冗杂过程不再赘述。
经过数据清理之后,汇总成3个数据集:

  • df_orders 订单数据集
  • df_items 产品条目数据集
  • df_payments 支付数据集

数据分析可视化

# 备份
df_o = df_orders.copy()
df_i = df_items.copy()
df_p = order_payments.copy()
# 观察两年来的订单数量情况
fig,ax = plt.subplots(figsize=(15,6))
# 按月汇总
ax.hist(df_o['order_purchase_timestamp'],bins = 30);
月订单数分布图
output_49_0.png

可以看到:订单数从刚开始两个月的试探期进入到17年的比较快速的增长期,尤其是12月甚至直接翻倍,猜想可能是年终大促。后有所回落,18年开始增长趋势放缓,但较17年还是获得了较大增长。

# 观察两年来的销售额情况
# 按月聚合销售额
f,ax = plt.subplots(figsize=(15,6))
ax = df_o.groupby(pd.Grouper(key='order_purchase_timestamp',freq='M'))['sales_volume'].sum().plot()
ax.set_title('月销售额走势图',fontsize=20);
output_51_0.png

观察到月销售额也呈现出和订单数量较为相近的走势,有意思的是订单数几乎翻倍的12月,销售额增长幅度并没有那么大,猜想促销活动进行了比较大幅度的降价,紧接着的18年销售额也处在一个比较高的水平,8月9月有小幅下降。

#客单价
#按月统计
f,ax = plt.subplots(figsize=(12,5))
df_kdj = pd.DataFrame()
df_kdj['sales'] = df_o.groupby(pd.Grouper(key='order_purchase_timestamp',freq='M'))['sales_volume'].sum()
df_kdj['counts'] = df_o.groupby(pd.Grouper(key='order_purchase_timestamp',freq='M'))['customer_unique_id'].nunique()
df_kdj['kdj'] = df_kdj.sales/df_kdj.counts
ax = sns.lineplot(x=df_kdj.index,y='kdj',data=df_kdj)
客单价走势图
output_53_0.png

除了16年底没有营业的特殊情况外,其他月份客单价均保持在160上下,没有明显的增长,猜测客户购买频率较低,购买商品品类比较单一,对网站的认可度还不够高。

# 观察各州销售情况
# 各州的销售额情况
fig,ax = plt.subplots(figsize = (15,6))
ax = df_o['sales_volume'].groupby(df_o['customer_state']).sum().sort_values(ascending=False).plot(kind='bar')
plt.xticks(rotation=0)
各州销售额排序
output_55_0.png

SP圣保罗州占绝对领先位置,其次是RJ,MG,RS,PR等州,销售额分布集中在前几个州,尤其是SP,市场分布极不均匀,可能和经济发展情况有较大关系

f,ax = plt.subplots(figsize=(15,6))
df_ot = df_o[['order_purchase_timestamp','customer_state','sales_volume']]
df_ot = df_ot.set_index('order_purchase_timestamp')
g = df_ot.groupby('customer_state')['sales_volume']
df_g = g.resample('M').sum().sort_values(ascending=False)
df_g = df_g.reset_index()
ax = sns.lineplot(x = 'order_purchase_timestamp',y = 'sales_volume',data = df_g,hue = 'customer_state')
ax.set_title('各州月销售额走势图',fontsize=20);
output_57_0.png

各州分开进行对比,可以发现除SP保持了较好增长趋势外,其他各州在17年大促之后都在缓慢回落,可能在SP投入的销售资源更多,客户认可度也更高。

# 查看各州平均运货时间对比
fig,ax = plt.subplots(figsize = (15,5))
df_o['deliver_time'].groupby(df_o['customer_state']).mean().sort_values().plot(kind='bar')
ax.set_title('各州平均送货时间')
plt.xticks(rotation=0);
output_59_0.png

对比各州的平均送货时间,能够在10天以内完成送货的只有SP,15天以内的也仅有PR,MG,DF,SC,RJ,RS,线上购物过程中到货时间极大影响购物体验,也说明在仓储物流等资源配置和硬件条件上各州还存在较大差距。

# 统计销售额最高的产品种类
f,ax = plt.subplots(figsize=(15,5))
ax = df_i['price'].groupby(df_i['product_category']).sum().sort_values(ascending=False).head(15).plot(kind='bar')
plt.xticks(rotation=45);
销售额产品分类Top15
output_68_0.png

累计销售额最高的分别是health_beauty健康美容,watches_gifts手表礼品,bed_bath_table浴盆,sports_lersure运动衫,computers_accessories电脑配件等等。反映了一定的顾客偏好,同时注意到一些热门的线上产品如3C类,零食等并未获得较高排名,还有潜力可挖。

# 查看付款方式订单数占比 和 金额占比
f,(ax1,ax2) = plt.subplots(nrows = 1,ncols = 2,figsize=(15,6))
df_p['payment_type'].value_counts().sort_values(ascending=False).plot(kind='bar',ax=ax1)
df_p['payment_value'].groupby(df_p['payment_type']).sum().sort_values(ascending=False).plot(kind='bar',ax=ax2)
ax1.set_title('付款方式订单数对比')
ax2.set_title('付款方式金额数对比')
ax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)
ax2.set_xticklabels(ax2.get_xticklabels(), rotation=0);
output_77_0.png

绝大多数订单和金额都采用信用卡支付,排名第二的boleto是一种巴西银联发行的票券,其他方式占比不高

客户分类

建立RFM指标模型,使用K-means聚类算法进行客户分类。选择2017年9月-2018年9月的数据集进行操作。

# 建立指标数据df_rfm
df_rfm = df_orders.drop(columns=['customer_id', 'order_status','order_approved_at', 'order_delivered_carrier_date',
       'order_delivered_customer_date', 'order_estimated_delivery_date','customer_zip_code_prefix', 'customer_city',
       'customer_state', 'geolocation_lat', 'geolocation_lng',
       'geolocation_city', 'geolocation_state','order_id' ])
# 删除空值数据
df_rfm = df_rfm.dropna()
# 合并商品价格和运费
df_rfm['order_value'] = df_rfm['price'] + df_rfm['freight_value']
# 删除单价和运费列
df_rfm = df_rfm.drop(columns = ['price','freight_value'])
# 对时间数据进行格式转换
df_rfm.order_purchase_timestamp = pd.to_datetime(df_rfm.order_purchase_timestamp)
# 按照时间由近及远排序
df_rfm = df_rfm.sort_values(by='order_purchase_timestamp',ascending=False)
# 选择购买时间在一年内的数据,即20170904——20180903
rfm_year = df_rfm[df_rfm['order_purchase_timestamp'] > '2017-09-04 00:00:00'].copy()
# 重置索引
rfm_year = rfm_year.reset_index(drop=True)
# 定义数据框rfm_model
rfm_model = pd.DataFrame()
# 筛选最近购买时间 recency
rfm_model['recency'] = rfm_year.order_purchase_timestamp.groupby(rfm_year['customer_unique_id']).max()
# 统计消费次数 frequency
rfm_model['frequency'] = rfm_year.order_value.groupby(rfm_year['customer_unique_id']).count()
# 计算购买额 monetary
rfm_model['monetary'] = rfm_year.order_value.groupby(rfm_year['customer_unique_id']).sum()
# 添加一列快捷id,方便显示
rfm_model['rapid_id'] = np.arange(100001,173253)
# 重置索引
rfm_model = rfm_model.reset_index(drop = False)
# 建立一个索引表,用来对应rapid_id和customer_unique_id
id_index = rfm_model[['customer_unique_id','rapid_id']].copy()
# 删除原customer_unique_id
rfm_model = rfm_model.drop(columns='customer_unique_id')
# 修改时间按日计数
rfm_model.recency = rfm_model.recency.dt.round('D')
# 以2018-09-05来计算时间间隔
rfm_model.recency = pd.to_datetime('2018-09-05')-rfm_model['recency']
# 修改格式显示按天计算的整数
rfm_model.recency = rfm_model.recency.dt.round('D').astype('str').apply(lambda x:x.split(' ')[0]).astype(int)
# 使用rapid做索引
data = rfm_model.set_index('rapid_id')
# 3d呈现数据的三个特征情况
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('Recency')
ax.set_ylabel('Frequency')
ax.set_zlabel('Monetary')
ax.scatter(data['recency'],data['frequency'],data['monetary'],s=30);
3D呈现RFM指标空间客户的分布情况
output_102_0.png

明显看出,大部分客户集中在Frequency坐标的0轴附近,高频客户零星散布,购买值也几乎都在2000以内。

手肘图判断K-means聚类的k值

# 标准化
data_zs = 1.0*(data-data.mean())/data.std()
# 将特征值转化成array格式
X = np.array(data_zs)
# 使用手肘图判断k值
# wcss即within cluster sums of squares 分群的面积
wcss = []
# K 经过几次迭代之后选1到15
K = range(1, 15)
for k in K:
    kmeans = KMeans(n_clusters=k, init='k-means++', random_state=0)
    kmeans.fit(X)
    wcss.append(kmeans.inertia_)
    # 这里使用wcss也就是分群的面积来判断,每一个k值对应分群的面积,面积越小表明分群越收敛,计算平均距离也可
    # distortions=[]
    # distortions.append(sum(np.min(cdist(X, kmeanModel.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])
# 画手肘图
plt.plot(K, wcss, 'bx-')
plt.xlabel('k')
plt.ylabel('WCSS')
plt.title('Elbow graph')
plt.show()

output_104_0.png

手肘图的拐点即肘的位置被认为是最恰当的分类个数,在这里尝试了两种计算方法,一种是上图所示采用计算wcss即分群面积,他的原理是分类越收敛,面积就越小,还有一种是计算平均距离,即数据点距离类中心点的平均距离越小,分类越收敛,个人感觉距离计算敏感度更高,可能分辨率更高。本次分类选用面积法,图上肘部节点4-6段,考虑到F指标区分度太小,选择k=4分类。

k=4 设置模型

# 设置模型
model_4 = KMeans(n_clusters=4, init='k-means++', random_state=0)
# 进行聚类
clusters_4 = model_4.fit_predict(X)

图示分类结果

# 设置索引
data_4 = rfm_model.set_index('rapid_id')
# 添加分类标签
data_4['clusters'] = clusters_4
# 图示分类结果
fig = plt.figure(figsize=(12,8))
dx = fig.add_subplot(111, projection='3d')
colors = ['blue', 'yellow', 'green', 'red']

for i in range(0,4):
    dx.scatter(data_4[data_4.clusters == i].recency, 
               data_4[data_4.clusters == i].frequency, 
               data_4[data_4.clusters == i].monetary, 
               c = colors[i], 
               label = 'Cluster ' + str(i), 
               s=30)

dx.set_title('Clusters of clients')
dx.set_xlabel('Recency')
dx.set_ylabel('Frequency')
dx.set_zlabel('Monetary')
dx.legend()

output_108_1.png

可以看出:红色3群购买金额较高,黄色1群购买频率较多,蓝色0群和绿色1群购买金额和频率较低,购买时间距离不同。

各群人数对比
# 各分群人数情况
f,ax = plt.subplots(figsize=(10,5))
ax = data_4['clusters'].value_counts().plot(kind='bar')
ax.set_title('各分群数量分布',fontsize=20);
clusters_num.png
data_4['clusters'].value_counts()
    2    34947
    0    34604
    3     1870
    1     1831

0群和2群人数最多,分别接近35000人,而3群和1群不足2000人,即高购买值和频次的客户非常少

详细查看每个指标范围:箱线图
# 箱线图
# 使用默认灰色背景
sns.set()
fig,((ax1,ax2,ax3),) = plt.subplots(nrows = 1,ncols = 3,squeeze = False,figsize = (18,6))
# ax1,ax2,ax3 = axes.flatten()
sns.boxplot(x= 'clusters',y = 'recency',data = data_4, ax = ax1)
sns.boxplot(x= 'clusters',y = 'frequency',data = data_4, ax = ax2)
sns.boxplot(x= 'clusters',y = 'monetary',data = data_4, ax =ax3)
ax3.set(ylim=(0,1500))
output_109_1.png
发现特征如下:
  • 0群购买时间:距离225-300天; 购买频率:1次; 购买金额:70-200
  • 1群购买时间:距离90-230天; 购买频率:2次; 购买金额:150-350
  • 2群购买时间:距离50-140天; 购买频率:1次; 购买金额:70-200
  • 3群购买时间:距离100-260天; 购买频率:1次; 购买金额:800-1350
继续观察:核密度曲线
# 核密度曲线绘制购买时间和金额
fig,((ax1,ax2,ax3),) = plt.subplots(nrows=1,ncols = 3,figsize=(18,5),squeeze=False)
for i in range(0,4):
    sns.kdeplot(data_4[data_4.clusters==i].recency,label = 'cluster'+str(i),ax=ax1)
    sns.kdeplot(data_4[data_4.clusters==i].monetary,label = 'cluster'+str(i),ax=ax2)
ax2.set(xlim=(0,2500))
# 0群和2群频率值全都为1,无法绘制kde曲线
for i in [1,3]:
    sns.kdeplot(data_4[data_4.clusters==i].frequency,label = 'cluster'+str(i),ax=ax3)
ax3.set(xlim=(0,4));
output_111_0.png
对照箱线图修正:
  • 购买时间:0群购买时间距离0-180天,2群购买时间距离180-365天,刚好一年对半分;1群和3群基本覆盖全年,比较接近。
  • 购买金额:0群和2群购买金额主要集中在200以内,少量突破500;1群购买金额主要150-350,个别接近1000;3群购买金额主要在700-1200,个别更高。
  • 购买频率:0群2群均为1次;3群大部分1次,少量2次;1群大部分购买2次,少量更高。
综合客户分类情况:
  • 3群属于高价值客户,虽然大部分仅购买一次,但购买金额远高于其他用户,对平台有一定的信任度,购买时间距离近的客户需要重点维持,购买间隔过长的需要重点挽留;
  • 1群属于重要保持客户,均有回购记录,且消费价值较高,对平台较为认可,可重点挖掘购买潜力,维持购买频率;
  • 2群属于重点发展客户,购买时间较近,属于新客户,需要进一步跟进转化;
  • 0群属于一般挽留客户,购买时间较远,且购买金额较低,可尝试对其进行重新激活。

综述:

就数据反映的情况来说,该网站的客户流失率比较高,客户保持做的不好,推测可能发展战略不够平衡,过于注重拉新,而忽视了保留,导致购买频率普遍偏低。而平均送货时间过长也极大影响购买体验,这一定程度上反映了仓储保障能力,在前期如此多新客户进行尝试的情况,及早转换思路,提高留存,可能会取得更好的市场收益。

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

推荐阅读更多精彩内容