目录
数据来源:
该数据集为在线零售商在2010年12月1日至2011年12月9日间发生的所有网络交易订单信息。
该公司主要销售礼品为主,并且多数客户为批发商。
数据集介绍及来源:
https://www.kaggle.com/carrie1/ecommerce-data
特征说明:
InvoiceNo:订单编号,由六位数字组成,退货订单编号开头有字幕’C’
StockCode:产品编号,由五位数字组成
Description:产品描述
Quantity:产品数量,负数表示退货
InvoiceDate:订单日期与时间
UnitPrice :单价(英镑)
CustomerID:客户编号,由5位数字组成
Country:国家
数据处理
利用pandas库检查缺失值和异常值,删除InvoiceNo列异常值数据(运行后可知该列异常值是A开头+六位数字)并将UnitPrice列的异常值数据进行线性插值填补后存入data2.csv表中。
import pandas as pd
# 读取CSV文件
dataframe = pd.read_csv('data.csv')
# 检查缺失值
missing_values = dataframe.isnull().sum()
print("缺失值数量:\n", missing_values)
# 检查InvoiceNo列异常值
# 定义条件1:订单编号由六位数字组成
condition1 = dataframe['InvoiceNo'].str.isnumeric() & (dataframe['InvoiceNo'].str.len() == 6)
# 定义条件2:退货订单编号以字母'C'开头(即C+六位数字)
condition2 = dataframe['InvoiceNo'].str.match(r'^C\d{6}$')
# 通过逻辑与操作符 (&) 组合两个条件
valid_invoice_conditions = condition1 | condition2
# 通过布尔索引选择满足条件的行,得到清洗后的DataFrame
cleaned_dataframe = dataframe[valid_invoice_conditions]
# 计算'InvoiceNo'列异常值数量
invoice_no_outliers_count = len(dataframe[~valid_invoice_conditions])
print("InvoiceNo列异常值数量:", invoice_no_outliers_count)
# 打印InvoiceNo列异常值
invoice_no_outliers = dataframe[~valid_invoice_conditions]['InvoiceNo']
print("InvoiceNo列异常值:\n", invoice_no_outliers)
# 利用标准差方法检测UnitPrice列的异常值
unit_price_mean = cleaned_dataframe['UnitPrice'].mean()
unit_price_std = cleaned_dataframe['UnitPrice'].std()
upper_bound = unit_price_mean + 3 * unit_price_std
lower_bound = unit_price_mean - 3 * unit_price_std
# 获取UnitPrice列的异常值
unit_price_outliers = (cleaned_dataframe['UnitPrice'] > upper_bound) | (cleaned_dataframe['UnitPrice'] < lower_bound)
# 使用线性插值填充异常值
cleaned_dataframe.loc[:, 'UnitPrice'] = cleaned_dataframe['UnitPrice'].interpolate(method='linear')
# 计算'UnitPrice'列异常值数量
unit_price_outliers_count = len(cleaned_dataframe[unit_price_outliers])
print("UnitPrice列异常值数量:", unit_price_outliers_count)
# 打印UnitPrice列异常值
unit_price_outliers = cleaned_dataframe[unit_price_outliers]['UnitPrice']
print("UnitPrice列异常值:\n", unit_price_outliers)
# 保存清洗后的数据到新表data2.csv
cleaned_dataframe.to_csv('data2.csv', index=False)
结果展示
缺失值数量:
InvoiceNo 0
StockCode 0
Description 1454
Quantity 0
InvoiceDate 0
UnitPrice 0
CustomerID 135080
Country 0
dtype: int64
InvoiceNo列异常值数量: 3
InvoiceNo列异常值:
299982 A563185
299983 A563186
299984 A563187
Name: InvoiceNo, dtype: object
UnitPrice列异常值数量: 377
UnitPrice列异常值:
1814 569.77
3041 607.49
4989 295.00
5685 498.47
6165 887.52
...
536834 1008.96
537254 1683.75
539368 938.59
540908 933.17
541540 1714.17
数据可视化
客户世界分布热力图的数据是利用data2.csv数据做数据透视表后得到的国家以及该国客户数量,其余画图的数据都是通过data2.csv的列数据直接进行的。
以上饼图可以直观看到进货数量为5660978,退货数量为484531,,退货数量占总数的7.9%。
该热力图向我们展示了客户在全球的分布以及客户数量,可以直观看到英国客户数量非常多。
上图展示了网络交易订单的价格分布,0-5英镑的商品数量最多,价格越高商品数越少。
上图展示了2010年12月到2011年12月每个月的销售量,2011年2月销售量最低,2011年11月销售量最高。
数据可视化代码:
import pandas as pd
import matplotlib.pyplot as plt
#import plotly.express as px
# 读取数据
df = pd.read_csv('data2.csv')
# 进货与退货数量的饼图
quantity = df['Quantity']
pos_quantity = quantity[quantity > 0].sum()
neg_quantity = -quantity[quantity < 0].sum()
#绘制饼图
labels = ['进货', '退货']
values = [pos_quantity, neg_quantity]
fig1, ax1 = plt.subplots()
patches, texts, autotexts = ax1.pie(values, labels=labels, autopct='%1.1f%%')
ax1.axis('equal')
plt.title('进货与退货之比')
plt.rcParams['font.sans-serif'] = 'SimHei'
# 添加数据标签
for i, text in enumerate(autotexts):
value = values[i]
percentage = '{:.1f}%'.format(value / sum(values) * 100)
text.set_text('{}\n{}'.format(percentage, value))
# 添加图例
ax1.legend(labels, loc='best')
plt.show()
#价格条形图
# 创建数据
data = df['UnitPrice']
df2 = pd.DataFrame(data)
# 将价格分组
bins = [0,1,2,3, 5,10, 50, 100,500,1000, float('inf')]
group_names = ['0-1','1-2','2-3','3-5', '5-10','10-50', '50-100','100-500','500-1000', '>1000']
df2['UnitPriceGroup'] = pd.cut(df2['UnitPrice'], bins, labels=group_names)
# 统计每个分组的数量
unitprice_count = df2['UnitPriceGroup'].value_counts().sort_index()
# 设置中文显示字体
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
# 绘制条形图
fig, ax = plt.subplots()
bars = ax.bar(unitprice_count.index, unitprice_count.values)
plt.title('UnitPrice列价格分布')
# 添加数据标签
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2, height, height, ha='center', va='bottom')
plt.show()
# 月销售量折线图
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
df['YearMonth'] = df['InvoiceDate'].dt.strftime('%Y-%m')
sales_count = df.groupby('YearMonth')['Quantity'].sum().reset_index()
fig4, ax4 = plt.subplots()
ax4.plot(sales_count['YearMonth'], sales_count['Quantity'])
plt.xticks(rotation=45)
plt.title('2010年12月到2011年12月每个月销售量的折线图')
plt.show()
plt.rcParams['font.sans-serif']='SimHei'
rfm模型预测
rfm 模型介绍
RFM主要是针对顾客有一定频次的消费,比较适合传统企业里的运营商、银行、航空,或者互联网里的电商、出行,这种必须捆绑客户ID,有一定频次的小额消费的场景,RFM模型主要指:R (Recency-近度)、F (Frequency-频度)和M(Monetary-额度),即根据客户的活跃程度和交易金额贡献所做的分类。
R值:最近一次消费(Recency):客户在店铺消费最近一次和上一次的时间间隔,理论上R值越小的客户是价值越高的客户,即对产品及品牌印象度越高。
F值:消费频率(Frequency):客户在固定时间内的购买次数(一般是1年),购买频率越高,对品牌忠诚度越高。但实际店铺由于受品类宽度的原因(如电子产品等),即使是忠实粉丝用户也很难在1年内购买多次。所以,一般会把F值的时间范围去掉,替换成累计购买次数。
M值:消费金额(Monetary):M值相对于R值和F值最难使用,但最具有价值,金额越高,给店铺创造的收入和利润越高。理论上M值和F值是一样的,都带有时间范围,指的是一段时间(通常是1年)内的消费金额。
rfm模型最后根据这三个指标对客户打分后,会分为八个类型的客户:
特征提取:
为了获得rfm模型的特征,我们对data2.csv表:删除退货订单(退货订单一般都有对应进货订单,去除重复项),给顾客ID为空的行增加NULL,订单时间类型转换。然后再根据数据特点分别计算出R,F,M的值,将其和顾客ID共同放入新表rfm.csv中。最后给rfm.csv表数据的每个指标进行打分,然后将客户分类。
import pandas as pd
# 读取数据集
df = pd.read_csv('data2.csv')
# 把顾客ID类型转为字符串
df['CustomerID'] = df['CustomerID'].astype(str)
# 删除取消订单
df = df[~df['InvoiceNo'].str.contains('C')]
.................................................
#篇幅过长,只展示部分代码
# 定义转换函数
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 '流失客户'
# 得到最后的客户分级
df1['value'] = df1['R_rank'] + df1['F_rank'].astype(str) + df1['M_rank'].astype(str)
df1['customer_level'] = df1['value'].apply(trans_value)
# 将客户分级信息添加到 df1 中
df1['customer_level'] = df1['value'].apply(trans_value)
# 保存至CSV文件
df1.to_csv('rfm_分类.csv', index=False)
from pyecharts import options as opts
from pyecharts.charts import Pie
# 读取分类表
df_rfm = pd.read_csv('rfm_分类.csv')
# 统计每个客户类型的数量
C_type = df_rfm['customer_level'].value_counts()
c_num = C_type.tolist()
c_type = C_type.index.tolist()
# 绘制饼图
pie = (
Pie()
.add("", [list(z) for z in zip(c_type, c_num)],
rosetype="radius",
radius=["30%", "55%"],
label_opts=opts.LabelOpts(formatter='占比{d}%'),
center=["50%","40%"])
.set_global_opts(
title_opts=opts.TitleOpts(title="客户类型", pos_left="80%", pos_top="5%"),
legend_opts=opts.LegendOpts(type_="scroll", pos_left="80%", orient="vertical", pos_top="15%"),
)
)
生成的rfm客户类型图:
k-means聚类
k值确定:使用轮廓系数法等方法确定k值的选择。
综合手肘法和轮廓系数的值选出k最佳为3。
使用聚类算法对数据进行客户价值分析,并给出对应的雷达图:
根据上面画出的雷达图,可以知道Cluster2是消费近度和消费额都高的一类客户,这类客户一般是高价值客户、重点深耕客户、重点挽留客户;Clsuter1是消费额低,消费频率低,消费近度高的一类客户,一般属于流失客户;Cluster3是消费额一般,消费频率一般,消费近度也不算高的一类客户,一般属于潜力客户、一般保持客户、重点唤回客户。
通过对比RFM模型刻画出的客户分类,K-mens聚类出的客户类型分类少,效果一般。