数据分析的流程:
1、明确分析目的和思路/提出假设
2、数据收集
3、数据处理/整理
4、数据分析/验证假设
5、数据展现/可视化图表
6、报告撰写
1.分析背景
2019年,平台经过一年的发展,销售体量、客户群体、平台规模达到新的高度。
现对平台2019年销售数据进行分析。
分析数据:(10万+)
2019-01-01-00:00:00 — 2019-12-31-23:59:59
2.分析思路
2.1.整体运营
2.2.用户行为
2.3.销售
2.4.渠道流量
3.分析过程
3.1.整体运营:
—>GMV,成交总额,净成交总额
—>成交总额占GMV
—>净成交总额占GMV
—>退货率(退单数/总单)
3.2.销售:
—>每个月的销售趋势折线图【GMV,销售额,不含退单】
【关注最高点,最低点,上升点】
—>销售复购率
3.3.流量分析:
—>渠道来源用户对比
3.4.用户分析:
—>每周订单量【用来调整平台人员的作息时间】
—>一天当中活跃时间段
—>RFM模型【针对不同层次的客户制定相应的策略】
4.结论建议
4.1.整体运营:
提高GMV,成交额,净成交额
4.2.销售:
5月、11月运营促销策略推广;复购率低,平台数据加强用户销售数据埋点
4.3.流量:
提高渠道-0896,渠道-9527,渠道-0530流量投入更改或暂停渠道-330,渠道-0318,渠道-0191流量
一.加载数据
#1.加载处理数据所需要的库
import pandas as pd
#2.读取数据文件
#添加索引,不然默认的要加一列且从0开始
df = pd.read_excel('order2019.xlsx',index_col='id')
df.head()#104557
二.提取数据
2.1根据业务需要提取数据,提取2019年数据
#1.引入时间模块, 确定周期时间
import datetime
#2.确定开始时间节点与结束时间节点
# 2019,1,1,0,0,0
startTime = datetime.datetime(2019, 1, 1)
endTime = datetime.datetime(2019, 12, 31, 23, 59, 59)
#3.将数据源中的时间数据转换成datetime形式 (以防是字符串)
# print(type(df['orderTime']))
df.orderTime = pd.to_datetime(df.orderTime)
#4.将2019年1月1日前数据删除
df[df.orderTime < startTime] # True False
#删除数据 drop(index="索引", inplace=True,False)
df.drop(index=df[df.orderTime < startTime].index, inplace=True)#在df上直接修改
df
#5.将2019年12月31日后数据删除
df[df.orderTime > endTime]
df.drop(index=df[df.orderTime > endTime].index, inplace=True)
df #104296
2.2提取数据时,处理与业务流程不符合数据,支付时间间隔过长
#1.下单时间与支付时间间隔 (创建一个新字段)
df['payinterval'] = (df.payTime-df.orderTime).dt.total_seconds()#间隔都转化成秒
df
#2.支付时间间隔大于30分钟与支付时间早于下单时间
df[df.payinterval>1800]
df.drop(index=df[df.payinterval>1800].index, inplace=True)
df.drop(index=df[df.payinterval<0].index, inplace=True)
df #103354
2.3提取数据时,处理与业务流程不符合数据,订单金额与支付金额为负
# 1.订单金额为负
df[df.orderAmount < 0]
# # # 2.付款金额为负
df[df.payment < 0]#(优惠券的面额大于商品价格)
# # # 3.删除相应订单
df.drop(index=df[df.payment < 0].index, inplace=True)
df.head() #103348
三.清洗数据
3.1 查看数据
#1.查看非空信息
df.info()
#2. 查看整体描述
# df.describe()#对数值型的数据作基本描述
3.2 清洗orderID
#1.订单orderID不重复的个数
df.orderID.unique().size #103321
# df.orderID.size #103348
# 2.删除重复数据
df.drop(index=df[df.orderID.duplicated()].index, inplace=True)
df.info() #103321
3.3 清洗userID
#用户个数
df.userID.unique().size #78634
df.userID.size #103321
#复购用户,正常的
3.4 清洗goodsID
#PR000000
#商品下架 这个得结合业务知识,哪种代号表示下架商品
df.goodsID[df.goodsID == 'PR000000'].size #175
df.drop(index=df[df.goodsID == 'PR000000'].index, inplace=True)
df.info() #103146
3.5 清洗chanelID
#1.查看chanelID空值的信息
df[df.chanelID.isnull()]#是空为True
# #2.对空值进行修补
# value= inplace
df['chanelID'].fillna(value=df.chanelID.mode()[0], inplace=True)
df.info()
3.6 清洗platformtype
#由于空格的存在导致了成为了不同
df.platformType.unique().size #不重复的有几个
df.platformType.unique()#不重复的是哪几个
# " "字符串,存在空格 "" 空字符串
df['platformType']=df['platformType'].str.replace(" ","")
df.platformType.unique()
3.7 清洗payment
#【支付金额大于订单金额】,则计算平均折扣,用订单金额*平均折扣
#1.创建折扣字段
df['discount'] = (df.payment/df.orderAmount)
df.describe()
# 200, 200 190
#2.平均折扣(已经排查了异常数据)
meanDiscount = df[df['discount']<=1].discount.sum() / df[df['discount']<=1].discount.size
meanDiscount
#3.找到折扣大于1的数据
df[df['discount']>1]
df['payment'] = df['payment'].mask(df['discount']>1,None)
#mask,一一对应的,就是discount>1,就设置为None,类似于map函数
#4.对折扣大于1的数据进行填补
df['payment'].fillna(value=df.orderAmount*meanDiscount , inplace=True)
df.info()
# #5.处理折扣
df['discount'] = round((df.payment/df.orderAmount),2)
df
3.8清洗结束 查看数据
df.describe()
四.分析数据
4.1.分析数据,整体销售情况
#总体概览
# 1.销售GMV
df.orderAmount.sum()/10000 #10835
# # 2.成交总和
df.payment.sum()/10000 #10246
# # # 3.实际成交额
df[df.chargeback=="否"].payment.sum()/10000 # 8879
# # # 4.订单数量
df.orderID.unique().size #103146
# # # 5.退货订单数
df[df.chargeback=="是"].orderID.size #13590
# # # 6.退货率 (15%算低的了)
df[df.chargeback=="是"].orderID.size/df.orderID.unique().size
# # # 7.用户数
df.userID.unique().size #78525
4.2销售情况,各月份GMV\成交额趋势
#翻转维度,以月份为坐标轴
#1.月份字段
df['month'] = df['orderTime'].dt.month
df
#2.绘制图形
#2.1引入相应的库
import matplotlib.pyplot as plt
from matplotlib import font_manager #管理字体
#能够让图在这里面立即展示
%matplotlib inline
#2.2设置字体(照写)
#mac linux fc-list : lang= zh
my_font = font_manager.FontProperties(fname='C:\Windows\Fonts\msyh.ttc',size=12)
#2.3设置画布大小
plt.figure(figsize=(10,6))
#2.4设置网格线 辅助线
plt.grid(alpha=0.4)
# #2.5横纵坐标值
# #所有横坐标都一致
x = df.groupby('month')['orderAmount'].sum().index
x
# #GMV
y1 = df.groupby('month')['orderAmount'].sum().values/10000
#销售实际付款
y2 = df.groupby('month')['payment'].sum().values/10000
#不含退单销售额
y3 = df[df.chargeback=="否"].groupby('month')['payment'].sum().values/10000
#2.6 X横轴坐标文字
x_ticks_label = ["{}月份".format(i) for i in x]
# x刻度,标签文字
plt.xticks(x,x_ticks_label,rotation = 45,fontproperties = my_font)
#2.7绘制三条折线走势
#plot 折线图
#color 单词,#0022FF rgb(0,255)
plt.plot(x,y1,label='GMV',color="red",marker='o')
plt.plot(x,y2,label='销售额',color="orange",marker='*')
plt.plot(x,y3,label='不含退单',color="blue",marker = '.')
#2.8标记横纵轴名字与标题
plt.xlabel('月份',fontproperties=my_font)
plt.ylabel("销售额万元",fontproperties=my_font)
plt.title('销售额走势',fontproperties=my_font,color='red',size=20)
#2.9添加折点坐标
for a,b in zip(x,y1):
plt.annotate('(%.2f)'%(b),xy=(a,b),xytext=(-10,10),textcoords='offset points')
#2.10设置图例
plt.legend(prop=my_font,loc='upper left')
#2.11显示图形
plt.show()
4.3 流量渠道来源分析
#1.按照渠道分组聚合,统计用户数
custom = df.groupby('chanelID')['userID'].count()
custom
#2.设置字体
plt.rcParams['font.sans-serif']=['SimHei']
#3.使用pandas中方法直接画图
#pandas % 1.1f% %
custom.plot.pie(figsize=(15,10),labels=custom.index,autopct="%1.1f%%")
#4.设置标题
plt.title('各渠道来源用户占比')
plt.savefig('t2.png')
4.4 用户行为, 研究周一到周日哪天订单量最高
#1.处理周几字段
df['dayofweek'] = df['orderTime'].dt.dayofweek
df['dayofweek'].unique() #【1,2,3,4,5,6,0】
# #2.引入numpy
import numpy as np
#3.按周几做聚合
week = df.groupby('dayofweek')['orderID'].count()
week
# #4.设置横纵坐标
weekX = ['周一','周二','周三','周四','周五','周六','周日']
weekY = week.values
# weekX
#5.设置X轴
#刻度,值,字体
plt.xticks(range(len(weekX)),weekX,fontproperties=my_font)
# # #6.设置条形图
rects = plt.bar(range(len(weekX)),weekY,width=0.3,color=['r','g','b'])
# #7.设置每个数据条的位置
for rect in rects:
height = rect.get_height()#拿到每个条的高度
plt.text(rect.get_x() + rect.get_width() / 2, height+0.5, str(height),ha="center")
#第一个参数:使得数据在柱子的正中间,
# #8.显示
plt.show()
4.5 用户行为, 哪个时间段下单量最高
#1.备份整个数据
df1 = df.copy()
df1
# df1 = df 这是错误的,他们是对象类型,不能直接引用
# #2.设置时间周期30min
s = df1['orderTime'].dt.floor('30T') #直接在ordertime上面改
s #显示每条记录的时间段(30分钟为一段,年月日时分秒)
# #3.将下单时间转换成时间段
df1['orderTime'] = s.dt.strftime('%H:%M') + '-' + (s + pd.Timedelta(30 * 60, unit='s')).dt.strftime('%H:%M')
df1#时间段,00:00-00:30 ~ 23:30-00:00
# # #4.根据时间段分组聚合
tiemdf = df1.groupby('orderTime')['orderID'].count()
tiemdf
tiemdfX = tiemdf.index #各个时间段
tiemdfY = tiemdf.values#每个时间段里的订单量
tiemdfY
# #5.设置画布大小
plt.figure(figsize=(20,8),dpi=80 ) #分辨率
#6.设置样式风格
plt.style.use('ggplot')
#7.X轴形式
plt.xticks(range(len(tiemdfX)),tiemdfX,rotation=90)
#8.绘制数据条形图
rect = plt.bar(tiemdfX,tiemdfY,width=0.3,color=['orange'])
plt.title('用户下单时间段研究')
plt.savefig('t3.png')
#波峰的时候:抢购活动(中午休息的时间)
#波谷的时候:更新什么
4.6用户行为,客户情况
#1.客单价 订单总金额/去重的用户总数【每个用户贡献的金额】
df.orderAmount.sum()/df.userID.unique().size #1379.8
需要将userID作为一个纯粹的索引
#需要将userID作为一个纯粹的索引
#因为这里全为user-的形式
#如果数字前面有 user-,哈哈-,呵呵- 等,那么就转化成11,22,33等数字形式
#1.检查数据字段
df['userid'] = df["userID"].str[0:4]
df['userid'].unique() #user
df['userid'].unique().size #1,说明都是user
#2.userID只保留数字
df['userID'] = df["userID"].str[5:]
df
4.7用户行为,客户复购率
# 分析复购率
# 将用户消费数据进行数据透视。
# 统计每个用户在每月的订单量,所以userID是index,month是column。
pivoted_counts=df.pivot_table(index='userID',columns='month',
values='orderTime',aggfunc='count').fillna(0)
pivoted_counts.head()
# pivoted_counts=df.pivot_table(index='userID',
# values='orderTime',aggfunc='count').fillna(0)
#每个用户一整年的复购情况
pivoted_counts.sum() #按照每个月
# pivoted_counts.count() #按照每个月,只不过每个月都一样 78525
#复购率
#区分出没有购买的,购买1次的,1次以上的
# 复购率的定义是在某时间窗口内消费两次及以上的用户在总消费用户中占比。这里的时间窗口是月。
# if x>1 :
# x = 1
# else :
# if x==0:
# np.NaN
# else :
# 0
#1.引入numpy
import numpy as np
#2. 将数据转换一下,消费两次及以上记为1,消费一次记为0,没有消费记为NaN。
pcRepeatBuy =pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
pcRepeatBuy.head()
#3.绘图
#用sum和count相除即可计算出复购率。
(pcRepeatBuy.sum()/pcRepeatBuy.count()).plot(figsize=(15,6))
pcRepeatBuy.sum() #两次以上的消费用户数
pcRepeatBuy.count() #总的消费用户数(NaN值不计数)
plt.savefig('t4.png')
4.8用户行为,客户RFM模型
'''
#RFM根据用户的活跃程度,频率,贡献程度 分类
# Recency 最后一次消费距离现在时间距离 R越小越好,
# Frequency 消费频率 F越大越好
# Monetary 消费金额 M越大越好
# 8类用户, 打标签
'''
#1.备份整个数据
customdf = df.copy()
# 问题:什么时候需要复制数据,什么时候直接修改
#2.删除退单
customdf.drop(index=df[df.chargeback == '是'].index, inplace=True)
customdf
#3.转换日期格式
customdf['orderTime'] = customdf['orderTime'].dt.date #只显示年月日
# customdf['orderTime'] = pd.to_datetime(customdf['orderTime'], format='%Y-%m-%d')
customdf
# #4.将userID设置为索引,即去除了原有的ID号
customdf.set_index('userID',drop=True,inplace=True)
# #5.将原始订单中订单量全部置为1
customdf['orders'] = 1
customdf
# #6.数据透视
rfmdf = customdf.pivot_table(index=['userID'],
values=['orderAmount','orderTime','orders'],
aggfunc={'orderTime':'max',
'orderAmount':'sum',
'orders':'sum'})
rfmdf
#7.处理RFM模型中的R
#最后一个单子的时间和每一个用户的单子时间相减,转化为天数
rfmdf['R'] = (rfmdf.orderTime.max()-rfmdf.orderTime).dt.days
#8.处理RFM模型中的F与M
rfmdf.rename(columns={'orderAmount':'M','orders':'F'},inplace=True)
rfmdf.head()
#1. 对用户分类,设置标签
def rfm_func(x):
level = x.apply(lambda x: "1" if x >= 1 else '0')
label = level.R + level.F + level.M
d = {
'011':'重要价值客户',
'111':'重要唤回客户',
'001':'重要深耕客户',
'101':'重要挽留客户',
'010':'潜力客户',
'110':'一般维持客户',
'000':'新客户',
'100':'流失客户'
}
result = d[label]
return result
#2.根据模型打标签
rfmdf['label'] = rfmdf[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
#3.分组聚合
rfmdf_res = rfmdf.groupby('label').count()
rfmdf_res
#1.绘制图形
rfmdf.label.value_counts().plot.bar(figsize=(20,9))
#2.设置X轴
plt.xticks(rotation=0,fontproperties=my_font)