本次分析选用kaggle网站的巴西电商数据,时间跨度从2016年9月-2018年9月,数据集中对客户id进行了处理。
分析目标:
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.pngSP圣保罗州占绝对领先位置,其次是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群属于一般挽留客户,购买时间较远,且购买金额较低,可尝试对其进行重新激活。
综述:
就数据反映的情况来说,该网站的客户流失率比较高,客户保持做的不好,推测可能发展战略不够平衡,过于注重拉新,而忽视了保留,导致购买频率普遍偏低。而平均送货时间过长也极大影响购买体验,这一定程度上反映了仓储保障能力,在前期如此多新客户进行尝试的情况,及早转换思路,提高留存,可能会取得更好的市场收益。
网友评论