数据来源:https://www.kesci.com/home/project/5f4b17336476cf0036f7d40b/dataset
字段名 | 解释 |
---|---|
InvoiceNo | 发票编号 |
StockCode | 商品编号 |
Description | 商品描述 |
Quantity | 购买数量 |
InvoiceDate | 发票日期 |
UnitPrice | 每单位的价格 |
CustomerID | 客户编号 |
Country | 国家 |
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as sci
from pyecharts.charts import Bar
from pyecharts import options as opts
import pyecharts.charts as pyec
import warnings
warnings.filterwarnings("ignore")
# 显示所有行
pd.set_option('display.max_columns', 100)
pd.set_option('display.width', 500)
# 解决中文无法显示的问题
plt.rc('font', family='SimHei', size='12')
io = '.../online_retail.csv'
data = pd.read_csv(io, parse_dates=[4])
df = pd.DataFrame(data)
# 查看数据的描述性统计
print(df.head())
print(df.info())
print(df.describe())
print('数据共{}行'.format(df.shape[0]), ',共{}列'.format(df.shape[1]))
print(df.count())
输出结果如下:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
--------------------------------------------------------------------------
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 InvoiceNo 541909 non-null object
1 StockCode 541909 non-null object
2 Description 540455 non-null object
3 Quantity 541909 non-null int64
4 InvoiceDate 541909 non-null datetime64[ns]
5 UnitPrice 541909 non-null float64
6 CustomerID 406829 non-null float64
7 Country 541909 non-null object
dtypes: datetime64[ns](1), float64(2), int64(1), object(4)
--------------------------------------------------------------------------
Quantity UnitPrice CustomerID
count 541909.000000 541909.000000 406829.000000
mean 9.552250 4.611114 15287.690570
std 218.081158 96.759853 1713.600303
min -80995.000000 -11062.060000 12346.000000
25% 1.000000 1.250000 13953.000000
50% 3.000000 2.080000 15152.000000
75% 10.000000 4.130000 16791.000000
max 80995.000000 38970.000000 18287.000000
--------------------------------------------------------------------------
数据共541909行 ,共8列
InvoiceNo 541909
StockCode 541909
Description 540455
Quantity 541909
InvoiceDate 541909
UnitPrice 541909
CustomerID 406829
Country 541909
删除数据中的销售数量Quantity和单位价格UnitPrice存在负数的记录。
# 将负数替换为空值
df[df['Quantity'] < 0] = np.nan
df[df['UnitPrice'] < 0] = np.nan
# 删除含有空值的数据行
df.dropna(subset=['Quantity'], inplace=True)
df.dropna(subset=['UnitPrice'], inplace=True)
新增两列:月份InvoiceDate(M)$和销售金额Sale_Amount。
# 对时间列'InvoiceDate'进行处理
df['InvoiceDate'] = df['InvoiceDate'].values.astype('datetime64[D]')
df['InvoiceDate(M)'] = df['InvoiceDate'].values.astype('datetime64[M]')
# print(df.tail(10))
# 计算每条记录的销售金额Sale_Amount = Quantity × UnitPrice
df['Sale_Amount'] = df['Quantity'] * df['UnitPrice']
print(df.head())
结果如下:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country InvoiceDate(M) Sale_Amount
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6.0 2010-12-01 2.55 17850.0 United Kingdom 2010-12-01 15.30
1 536365 71053 WHITE METAL LANTERN 6.0 2010-12-01 3.39 17850.0 United Kingdom 2010-12-01 20.34
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8.0 2010-12-01 2.75 17850.0 United Kingdom 2010-12-01 22.00
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6.0 2010-12-01 3.39 17850.0 United Kingdom 2010-12-01 20.34
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6.0 2010-12-01 3.39 17850.0 United Kingdom 2010-12-01 20.34
# 绘制折线图
plt.figure(1, figsize=(12, 12))
n = 0
for x in ['Quantity', 'Sale_Amount']:
n += 1
plt.subplot(2, 1, n)
plt.subplots_adjust(hspace=5, wspace=5)
df.groupby('InvoiceDate')[x].sum().plot()
plt.xlabel('date', color='k', fontsize=10)
plt.ylabel(x, color='k', fontsize=10)
# plt.title('{} 趋势图'.format(x), fontsize=10)
plt.tight_layout()
plt.grid(linestyle='-.')
plt.show()
# 绘制折线图
plt.figure(1, figsize=(12, 12))
n = 0
for x in ['Quantity', 'Sale_Amount']:
n += 1
plt.subplot(2, 1, n)
plt.subplots_adjust(hspace=5, wspace=5)
df.groupby('InvoiceDate(M)')[x].sum().plot()
plt.xlabel('date', color='k', fontsize=10)
plt.ylabel(x, color='k', fontsize=10)
# plt.title('{} 趋势图'.format(x), fontsize=10)
plt.tight_layout()
plt.show()
按照商品编号StockCode进行分组。
df_StockCode = df.groupby('StockCode')
由于商品数量比较多,这里仅取前1%的商品。
df_StockCode_q = df_StockCode['Quantity'].sum().sort_values(ascending=False)
# 前1%的商品
df_StockCode_q_len = len(df_StockCode_q)
df_StockCode_q_temp = df_StockCode_q[: int(df_StockCode_q_len * 0.01)]
绘图。
# 各产品的销售数量柱形图
product_bar1 = Bar(init_opts=opts.InitOpts(width='1300px', height='650px'))
# 增加x轴数据
product_bar1.add_xaxis(df_StockCode_q_temp.index.tolist())
# 增加y轴数据
product_bar1.add_yaxis('', df_StockCode_q_temp.values.tolist())
# 全局配置设置
product_bar1.set_global_opts(title_opts=opts.TitleOpts(title="各产品的销售数量分布"),
yaxis_opts=opts.AxisOpts(name="销售数量"),
xaxis_opts=opts.AxisOpts(name="商品编号", axislabel_opts=opts.LabelOpts(font_size=10, interval=0, rotate=60)),
visualmap_opts=opts.VisualMapOpts(is_show=True, type_="color",
max_=max(df_StockCode_q_temp.values.tolist()), pos_left='-20'))
# 系列配置设置
product_bar1.set_series_opts(markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="max"),
opts.MarkPointItem(name="min", type_="min")]),
markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(name="average", type_="average")]))
product_bar1.render('StockCode_sale_bar.html')
购物篮分析来自于线下零售,可以对顾客的购物清单进行分析来洞悉消费者的购物行为。这个概念参考《数据化管理-洞悉零售及电子商务运营》一书。
购物篮系数是指客户平均购买数量,一个购物篮类似一张客户消费订单,公式如下: 购 物 篮 系 数 = 某 段 时 间 商 品 销 售 数 量 某 段 时 间 的 购 物 篮 总 数 购物篮系数=\frac{某段时间商品销售数量}{某段时间的购物篮总数} \quad 购物篮系数=某段时间的购物篮总数某段时间商品销售数量
# 计算购物篮总数
gwl_count = df['InvoiceNo'].count()
# 计算购物篮系数
gwl_coefficient = round((df['Quantity'].sum() / gwl_count), 2)
print('购物篮系数为: {}'.format(gwl_coefficient))
结果如下,即平均每位客户一次性购买10.66件商品。
购物篮系数为: 10.66
指 定 商 品 购 物 篮 系 数 = 某 段 时 间 指 定 商 品 购 物 篮 的 销 售 总 数 某 段 时 间 含 指 定 商 品 的 购 物 篮 总 数 指定商品购物篮系数=\frac{某段时间指定商品购物篮的销售总数}{某段时间含指定商品的购物篮总数} \quad 指定商品购物篮系数=某段时间含指定商品的购物篮总数某段时间指定商品购物篮的销售总数 举个例子,上个月中,共有2000张销售订单中包含某品牌巧克力,而这2000张销售单一共包含8000件商品,则某品牌巧克力的购物篮系数为4。
df_StockCode_q_temp = df_StockCode_q.reset_index()
df_i_s = df[['InvoiceNo', 'StockCode']]
# 统计各张订单的总销售量
df_InvoiceNo = df.groupby('InvoiceNo')['Quantity'].sum()
df_i_s_q = pd.merge(df_i_s, df_InvoiceNo, on=['InvoiceNo'], how='inner')
自定义函数
# 计算含有某商品的订单数
def search_stockcode(data):
# 将循环的每一行(series)转换成Dataframe
stockcode_temp = data.to_frame()
stockcode = pd.DataFrame(stockcode_temp.values.T, columns=stockcode_temp.index)
# 表链接,查询原有数据中包含当前产品编号的订单记录。
merge_temp = pd.merge(df_i_s_q, stockcode['StockCode'], on=['StockCode'], how='inner')
# 分别统计有多少张订单包含该商品,以及这些订单的销售总数量
order_count = len(merge_temp)
quantity_sum = merge_temp['Quantity'].sum()
return order_count, quantity_sum
df_StockCode_q_temp[['order_count', 'order_quantity_sum']] = df_StockCode_q_temp.apply(lambda x: search_stockcode(x), axis=1, result_type='expand')
df_StockCode_temp['gwl'] = df_StockCode_temp['order_quantity_sum'] / df_StockCode_temp['order_count']
# 删除“异常值”
df_StockCode_temp_1 = df_StockCode_temp[df_StockCode_temp['order_quantity_sum'] < 10000]
# 绘制散点图
sns.jointplot(x=df_StockCode_temp_1['gwl'], y=df_StockCode_temp_1['order_quantity_sum'],
data=df, # 设置数据
color='b', # 设置颜色
s=30, edgecolor='w', linewidth=1, # 设置散点大小、边缘颜色及宽度(只针对scatter)
stat_func=sci.pearsonr,
kind='scatter', # 设置类型:'scatter','reg','resid','kde','hex'
space=0.05, # 设置散点图和布局图的间距
size=9, # 图表大小(自动调整为正方形))
ratio=5, # 散点图与布局图高度比,整型
marginal_kws=dict(bins=10, rug=True), # 设置柱状图箱数,是否设置rug
)
plt.show()
指 定 商 品 的 人 气 指 数 = 某 段 时 间 含 指 定 商 品 购 物 篮 的 销 售 总 数 某 段 时 间 的 购 物 篮 总 数 指定商品的人气指数=\frac{某段时间含指定商品购物篮的销售总数}{某段时间的购物篮总数} \quad 指定商品的人气指数=某段时间的购物篮总数某段时间含指定商品购物篮的销售总数人气指数并不是指定商品的销售数量的比重,销售数量比重只能判断该商品卖得还不好。人气指数高的商品不一定是卖得最好的,但是它带来的销售量是最大的。
例如,上月销售了10万件商品,共2万个购物篮。部分数据如下。虽然商品B销量比A少,但是含商品B的购物篮一共销售了4200件,大于A的3200,人气指数为0.21。
/ | 商品A | 商品B |
---|---|---|
销售量 | 2600 | 1500 |
占总销售 | 2.6% | 1.5% |
含该商品的购物篮数 | 3200 | 4200 |
人气指数 | 0.16 | 0.21 |
# 计算商品的人气指数
df_StockCode_temp['rqzs'] = df_StockCode_temp['order_quantity_sum'] / gwl_count
df_StockCode_temp_len = len(df_StockCode_temp)
df_StockCode_temp_2 = df_StockCode_temp.sort_values(by='rqzs', ascending=False)[: int(df_StockCode_q_len * 0.01)]
# 各产品的销售数量柱形图
product_bar2 = Bar(init_opts=opts.InitOpts(width='1300px', height='650px'))
# 增加x轴数据
product_bar2.add_xaxis(df_StockCode_temp_2['StockCode'].tolist())
# 增加y轴数据
product_bar2.add_yaxis('', df_StockCode_temp_2['rqzs'].tolist())
# 全局配置设置
product_bar2.set_global_opts(title_opts=opts.TitleOpts(title="商品人气指数"),
yaxis_opts=opts.AxisOpts(name="人气指数"),
xaxis_opts=opts.AxisOpts(name="商品编号", axislabel_opts=opts.LabelOpts(font_size=10, interval=0, rotate=60)),
visualmap_opts=opts.VisualMapOpts(is_show=True, type_="color",
max_=max(df_StockCode_temp_2['rqzs'].tolist()), pos_left='-20'))
# 系列配置设置
product_bar2.set_series_opts(markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="max"),
opts.MarkPointItem(name="min", type_="min")]),
markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(name="average", type_="average")]),
label_opts=opts.LabelOpts(is_show=False))
product_bar2.render('StockCode_rqzs_bar.html')
df_user = df.groupby('CustomerID')
sns.jointplot(x=df_user['Sale_Amount'].sum(), y=df_user['Quantity'].sum(),
data=df, # 设置数据
color='b', # 设置颜色
s=50, edgecolor='w', linewidth=1, # 设置散点大小、边缘颜色及宽度(只针对scatter)
stat_func=sci.pearsonr,
kind='scatter', # 设置类型:'scatter','reg','resid','kde','hex'
space=0.05, # 设置散点图和布局图的间距
size=8, # 图表大小(自动调整为正方形))
ratio=5, # 散点图与布局图高度比,整型
marginal_kws=dict(bins=10, rug=True), # 设置柱状图箱数,是否设置rug
)
plt.show()
df_user['InvoiceDate(M)'].min().value_counts().plot(marker='o')
plt.grid(linestyle='-.')
plt.show()
df_pivot_counts = df.pivot_table(index='CustomerID', columns='InvoiceDate(M)', values='Quantity', aggfunc='count')
print()
print(df_pivot_counts)
# 将数据中两次及以上的转为1,以下的转为0
df_pivot_counts_repurchase = df_pivot_counts.applymap(lambda x: 1 if x >= 2 else 0 if pd.notnull(x) else np.nan)
(df_pivot_counts_repurchase.sum()/df_pivot_counts_repurchase.count()).plot(marker='o')
plt.title('每月的复购率')
plt.grid(linestyle='-.')
plt.show()
自定义函数:
def cal_hgl(x):
status = []
for i in range(len(x)-1):
# 该顾客在当月有消费
if x[i] > 0:
if x[i+1] > 0:
# 下个月依然消费
status.append(1)
else:
status.append(0)
else:
status.append(np.nan)
# 数据填充
status.append(np.nan)
return status
计算每月回购率。
df_pivot_counts_hgl = df_pivot_counts.apply(lambda x: pd.Series(cal_hgl(x), index=x.index), axis=1) # axis=1指对行数据进行操作
(df_pivot_counts_hgl.sum()/df_pivot_counts_hgl.count()).plot(marker='o')
plt.title('每月的回购率')
plt.grid(linestyle='-.')
plt.show()
后续可以继续做客户的留存分析