案例分析练习:
# 加载数据分析需要使用的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
一、首先明确分析的目标
1、加载数据文件,以id列为索引
# 加载数据,加载之前先用文本编辑器看下数据格式,首行是什么,分隔符是什么的
df = pd.read_csv('order_info_2016.csv',index_col='id')
2、查看下数据文件头部信息
df.head()
# 加载好数据之后,第一步先分别使用describe和info方法看下数据的大概分布
# 这两个方法放到两个cell中
df.describe()
# 加载device_type
device_type = pd.read_csv('device_type.txt')
device_type
df.info()
二、对数据进行清洗
1、首先要做一个数据的清洗,order_id在一个系统里是唯一值
# 先看下有没有重复值
# 注意:当我们对一列取size属性的时候,返回的是行数,如果对于dataframe使用size,返回的是行乘以列的结果,也就是总的元素数
df.orderId.unique().size
注意:当我们对一列取size属性的时候,返回的是行数,如果对于dataframe使用size,返回的是行乘以列的结果,也就是总的元素数
2、userId我们只要从上面的describe和info看下值是不是在正常范围就行了
# 对于订单数据,一个用户有可能有多个订单,重复值是合理的
df.userId.unique().size
3、productId最小值是0,先来看下值为0的记录数量
# productId
# productId最小值是0,先来看下值为0的记录数量
df.productId[(df.productId == 0)].size
3、cityId类似于userId,值都在正常范围,不需要处理
# cityId类似于userId,值都在正常范围,不需要处理
df.cityId.unique().size
4、price没有空值,且都大于0,注意单位是分,我们要把它变成元
# price没有空值,且都大于0,注意单位是分,我们把它变成元
df.price = df.price /100
df.price
5、payMoney有负值,我们下单不可能是负值,所以这里对于负值的记录要删除掉
# 展示负值的记录
df[df.payMoney < 0 ]
# 删除负值的记录
df.drop(index=df[df.payMoney < 0].index, inplace=True)
# 在看下,已经没有了
df[df.payMoney < 0].index
3. 将payMoney 列数据变成元为单位
# 变成元
df.payMoney = df.payMoney / 100
df.payMoney
6、channelId根据info的结果,有些null的数据,可能是端的bug等原因,在下单的时候没有传channelId字段。
数据量大的时候,删掉少量的null记录不会影响统计结果,这里我们直接删除
# 展示null记录
df[df.channelId.isnull()]
# 删除
df.drop(index=df[df.channelId.isnull()].index, inplace=True)
# 在查看
df[df.channelId.isnull()]
7、deviceType的取值可以看device_type.txt文件,没有问题,不需要处理。
8、使用 df.isnull() 我们观察到,createTime和payTime都没有null, 不过我们是要统计2016年的数据,所以把非2016年的删掉; payTime类似,这里只按创建订单的时间算,就不做处理了
# 先把createTime和payTime转换成datetime格式
df.createTime = pd.to_datetime(df.createTime)
df.payTime = pd.to_datetime(df.payTime)
df.dtypes
import datetime
startTime = datetime.datetime(2016, 1, 1)
endTime = datetime.datetime(2016, 12, 31, 23,59, 59)
# 有16年之前的数据,需要删掉
df[df.createTime < startTime]
df.drop(index=df[df.createTime < startTime].index, inplace=True)
df[df.createTime < startTime]
# 处理16年之后的数据
df[df.createTime > endTime]
# 看下支付时间有没有16年以前的,支付时间在16年之后的这里就不处理了
df[df.payTime < startTime]
9、我们再把orderId重复的记录删掉
# 回过头来我们把orderId重复的记录删掉
df.orderId.unique().size
# 删除orderId重复数据
df.drop(index=df[df.orderId.duplicated()].index, inplace=True)
# 查看下
df.orderId.unique().size
10、把productId为0的也删除掉
# 把productId为0的也删除掉
df.drop(df[df.productId==0].index, inplace=True)
# 查看下
df[df.productId==0]
三、分析与可视化
1、一般都是先看下数据的总体情况,比如:总订单数,总下单用户,总销售额,有流水的商品数
# 看下数据的总体情况
# 总订单数,总下单用户,总销售额,有流水的商品数
print(df.orderId.count())
print(df.userId.unique().size)
print(df.payMoney.sum()/100)
print(df.productId.unique().size)
分析数据可以从两方面开始考虑,一个是维度,一个是指标,维度可以看做x轴,指标可以看成是y轴,同一个维度可以分析多个指标,同一个维度也可以做降维升维。
2、按照商品的productId,先看下商品销量的前十和后十个
# 先看下商品销量的前十和后十个
productId_orderCount = df.groupby('productId').count()['orderId'].sort_values(ascending=False)
# 商品销量的前十个
productId_orderCount_head_10 = productId_orderCount.head(10)
fig = plt.figure(figsize = (20, 15),dpi=80)
f1 = fig.add_subplot(2, 2, 1)
f1.set_xlabel("商品ID", fontproperties="SimHei", size = 22)
f1.set_ylabel("销量(件)", fontproperties="SimHei", size = 22)
f1.set_title("2016年不同商品销量前10名", fontproperties="SimHei", size = 25)
f1.set_xticks(range(0, 10))
f1.set_xticklabels(productId_orderCount_head_10.index, size = 20)
f1.set_yticklabels(range(0, 351, 50), size = 20)
rects = f1.bar(range(0, len(productId_orderCount_head_10.index)), productId_orderCount_head_10.values, width = 0.5, color = "g")
for rect in rects:
height = rect.get_height()
f1.text(rect.get_x(), height+2, str(height), size = 15)
plt.grid(ls = "--", alpha = 0.5)
# 商品销量的后十个
productId_orderCount_tail_10 = productId_orderCount.tail(10)
fig = plt.figure(figsize = (20, 15),dpi=80)
f1 = fig.add_subplot(2, 2, 1)
f1.set_xlabel("商品ID", fontproperties="SimHei", size = 22)
f1.set_ylabel("销量(件)", fontproperties="SimHei", size = 22)
f1.set_title("2016年不同商品销量后10名", fontproperties="SimHei", size = 25)
f1.set_xticks(range(0, 10))
f1.set_xticklabels(productId_orderCount_tail_10.index, size = 20)
f1.set_yticklabels(range(0, 31, 5), size = 20)
rects = f1.bar(range(0, len(productId_orderCount_tail_10.index)), productId_orderCount_tail_10.values, width = 0.5, color = "g")
for rect in rects:
height = rect.get_height()
f1.text(rect.get_x(), height+0.1, str(height), size = 15)
plt.grid(ls = "--", alpha = 0.5)
3、使用sum聚合函数汇总统计下payMoney销售额前十和倒数前十的商品
# 看下销售额前十和倒数前十的商品
productId_turnover = df.groupby('productId').sum()['payMoney'].sort_values(ascending=False)
## 销售额前十
productId_turnover_head_10 = productId_turnover.head(10)
productId_turnover_head_max = round(productId_turnover_head_10.max())
# print(productId_turnover_head_10.index)
# print(productId_turnover_head_10.values)
fig = plt.figure(figsize = (20, 15))
f1 = fig.add_subplot(2, 2, 1)
f1.set_xlabel("商品ID", fontproperties="SimHei", size = 18)
f1.set_ylabel("销售额(元)", fontproperties="SimHei", size = 18)
f1.set_title("2016年不同商品销售额前10名", fontproperties="SimHei", size = 18)
f1.set_xticks(range(0, 10))
f1.set_xticklabels(productId_turnover_head_10.index, size = 18)
f1.set_yticklabels(range(0, productId_turnover_head_max, 50000), size = 18)
rects = f1.bar(range(0, len(productId_turnover_head_10.index)), productId_turnover_head_10.values, width = 0.5, color = "g")
for rect in rects:
height = rect.get_height()
f1.text(rect.get_x(), height+2, str(height), size = 15, rotation=15)
plt.grid(ls = "--", alpha = 0.5)
## 商品销售额后10名
productId_turnover_tail_10 = productId_turnover.tail(10)
productId_turnover_tail_max = round(productId_turnover_tail_10.max())
fig = plt.figure(figsize = (20, 15))
f1 = fig.add_subplot(2, 2, 1)
f1.set_xlabel("商品ID", fontproperties="SimHei", size = 18)
f1.set_ylabel("销售额(元)", fontproperties="SimHei", size = 18)
f1.set_title("2016年不同商品销售额后10名", fontproperties="SimHei", size = 18)
f1.set_xticks(range(0, 10))
f1.set_xticklabels(productId_turnover_head_10.index, size = 18)
f1.set_yticklabels(range(0, productId_turnover_tail_max, 5000), size = 18)
rects = f1.bar(range(0, len(productId_turnover_tail_10.index)), productId_turnover_tail_10.values, width = 0.5, color = "g")
for rect in rects:
height = rect.get_height()
f1.text(rect.get_x(), height+2, str(height), size = 15, rotation=15)
plt.grid(ls = "--", alpha = 0.5)
4、看下销量和销售额最后100个的交集,如果销量和销售额都不达标,这些商品需要看看是不是要优化或者下架
# 看下销量和销售额最后100个的交集,如果销量和销售额都不达标,这些商品需要看看是不是要优化或者下架
problem_productIds = productId_turnover.tail(100).index.intersection(productId_orderCount.tail(100).index)
problem_productIds
# 城市的分析可以和商品维度类似
# 城市的商品销量
cityId_orderCount = df.groupby('cityId').count()['orderId'].sort_values(ascending=False)
cityId_orderCount
# 城市的商品销售额
cityId_payMoney = df.groupby('cityId').count()['payMoney'].sort_values(ascending=False)
cityId_payMoney
6、对于价格price,可以看下所有商品价格的分布,这样可以知道什么价格的商品卖的最好
# 按照100的区间取分桶,价格是分,这里为了好看把他转成元
bins = np.arange(0, 25000, 100)
pd.cut(df.price, bins).value_counts()
# 觉得尺寸小的话可以先设置figsize,觉得后面的值没有必要展示,可以不用25000,改成10000
plt.figure(figsize=(10, 8))
plt.hist(df['price'], bins)
plt.show()
# 很多价格区间没有商品,如果有竞争对手的数据,可以看看是否需要补商品填充对应的价格区间
price_cut_count = pd.cut(df.price, bins).value_counts()
zero_cut_result = (price_cut_count == 0)
zero_cut_result[zero_cut_result.values].index
# 按1000分桶再看下
bins = np.arange(0, 5001, 1000)
price_cut = pd.cut(df.price, bins).value_counts()
price_cut
# 看看1000分桶的时候5000以下的饼图
plt.figure(figsize=(8,8), dpi=80)
m = plt.pie(x=price_cut.values, labels=price_cut.index, autopct='%d%%', shadow=True)
plt.show(m)
7、channelId的分析类似于productId,可以找出成交量最多的渠道,交易额最多的渠道等;渠道有时候是需要花钱买流量的,所以还需要根据渠道盈利情况和渠道成本进行综合比较。
1> 按一天中下单时间分布情况分析
# 按下单时间分布情况分析
df['orderHour'] = df.createTime.dt.hour
df['orderHour']
order_hour_count = df['orderHour'].value_counts().sort_index()
# print(order_hour_count)
plt.figure(figsize = (12, 8))
_xticks = list(order_hour_count.index)
_xticks.append(24)
_x = [i-0.5 for i in range(0, 25)]
plt.xticks(_x, _xticks, size = 15)
plt.yticks(range(0, 14001, 2000), [i for i in range(0, 14001, 2000)], size = 15)
plt.xlabel("时间段", size = 18, fontproperties="SimHei")
plt.ylabel("订单数(件)", size = 18, fontproperties="SimHei")
plt.title("2016年在每日不同时间段的总订单数", size = 20, fontproperties="SimHei")
colors = ["#1C86EE"] * 24
colors[13] = "r"
colors[20] = "r"
rects = plt.bar(range(0, 24), order_hour_count.values, width = 1, edgecolor = "#B8B8B8", color = colors)
for rect in rects:
height = rect.get_height()
plt.text(rect.get_x(), height+80, str(height), size = 10)
plt.grid(ls = "--", alpha = 0.4)
plt.show()
2> 按一周下单时间分布情况分析
#
df['orderWeek'] = df.createTime.dt.dayofweek
df['orderWeek']
# 不同星期的订单数
order_week_groupby = df.groupby('orderWeek').count()['orderId']
order_week_groupby.index = [i for i in range(1, 8)]
# ---- 绘图 ----
plt.figure(figsize = (8, 8))
_xticks = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日", ]
plt.xticks(order_week_groupby.index, _xticks, size = 15, fontproperties="SimHei")
plt.yticks(size = 15)
plt.xlabel("时间", size = 18, fontproperties="SimHei")
plt.ylabel("订单数", size = 18, fontproperties="SimHei")
plt.title("2016年在每周不同时间的总订单数", size = 20, fontproperties="SimHei")
rects = plt.bar(order_week_groupby.index, order_week_groupby.values, width = 0.4)
for rect in rects:
height = rect.get_height()
plt.text(rect.get_x()-0.1, height+200, str(height), size = 15)
plt.grid(ls = "--", alpha = 0.4)
plt.show()
3> 下单后多久支付
# 下单后多久支付
def get_seconds(x):
return x.total_seconds()
df['payDelta'] = (df['payTime'] - df['createTime']).apply(get_seconds)
bins = [0, 50, 100, 1000, 50000, 100000]
pd.cut(df['payDelta'], bins).value_counts()
# 饼图展示
pd.cut(df.payDelta, bins).value_counts().plot(kind='pie', autopct='%d%%', shadow=True, figsize=(10,10))
plt.show()
4> 月成交额
# 先把创建订单的时间设置为索引
df.set_index('createTime', inplace=True)
df.head()
turnover = df.resample('M').sum()['payMoney']
turnover.plot()
plt.show()
order_count = df.resample('M').count()['orderId']
order_count.plot()
plt.show()
总结:
本案例主要对电商交易数据进行了一些常见的分析,包括了商品ID、商品价格、设备类型、下单时间等多个维度。因为不是多年的数据,因此无法做同比分析,而且数据不是企业内部的全部数据,所以原数据并没有出现像加购转化漏斗、网站流量等电商数据分析中常见的指标。不过,从仅有的数据来看,分析的结果基本符合我们的生活习惯,例如手机购物占多数、午休和晚饭后的休闲时间达到购物高峰期等。