外卖门店经营情况的数据监控_蛙小辣火锅杯-程序员宅基地

技术标签: python  Tableau  

蛙小辣&拌客的外卖门店经营情况数据监控

Excel

 

python

import pandas as pd
import matplotlib.pyplot as plt    # 绘图库
import numpy as np
plt.rcParams['font.sans-serif'] = 'MyriadPro'#设置中文字体为黑体
%matplotlib inline
plt.rcParams['axes.unicode_minus'] = False #正常显示负号

data1 = pd.read_csv('cpc.csv')
data2 = pd.read_csv('shop.csv')
data3 = pd.read_csv('orders.csv')
data = pd.concat([data1,data2,data3],axis = 0)
#data1.head(5)
#data2.info
data.dropna(axis=1,inplace=False)
data.info()
平台门店名称_count = data['平台门店名称'].value_counts()[:10]
print(平台门店名称_count)
平台门店名称_count.plot(kind='line',color=['r'])
平台门店名称_count.plot(kind='bar',fontsize=16)
for x,y in enumerate(平台门店名称_count):
    print(x,y)
    plt.text(x,y+2,y,ha='center',fontsize=12)
蛙小辣火锅杯(宝山店)          191
蛙小辣·美蛙火锅杯麻辣烫(宝山店)    164
蛙小辣·美蛙火锅杯(宝山店)       107
蛙小辣·美蛙火锅杯(虹口足球场店)     88
拌客·干拌麻辣烫(武宁路店)        83
蛙小辣火锅杯(五角场店)          76
蛙小辣·美蛙火锅杯(真如店)        58
蛙小辣·美蛙火锅杯(虹口足球场店)     55
利芳·一人食大盘鸡(国定路店)       44
蛙小辣火锅杯(合生汇店)          34
Name: 平台门店名称, dtype: int64
0 191
1 164
2 107
3 88
4 83
5 76
6 58
7 55
8 44
9 34

round(data['GMV'].mean(),2)
1721.94
data_group = data['平台'].value_counts()
data_group.plot(kind='bar',color=['orange','blue'])
plt.title('平台')
plt.xlabel('平台名称',fontsize=16)
plt.ylabel('平台数量',fontsize=16)

#绘制出,各平台在6、7月的GMV占比 (饼图)
pin_tai = data2[['平台','GMV']].groupby('平台').agg(GMV = ('GMV','sum'))
#我们按照平台进行分组,对GMV进行聚合
pin_tai = pd.Series(pin_tai.GMV,index=pin_tai.index)
#将DataFrame类型转换为Series类型
plt.pie(pin_tai,autopct='%2.1f%%',labels=['饿了么','美团'])
#调用pie方法,传入数据pin_tai;autopct:数据标签;%2.1f 输出宽度为2的浮点数,小数点宽度为1;labels 为标签
([<matplotlib.patches.Wedge at 0x1db73635490>,
  <matplotlib.patches.Wedge at 0x1db73635c10>],
 [Text(-0.547826998539216, 0.9538792269839584, '饿了么'),
  Text(0.5478269985392162, -0.9538792269839582, '美团')],
 [Text(-0.29881472647593593, 0.5202977601730682, '66.6%'),
  Text(0.2988147264759361, -0.5202977601730681, '33.4%')])
#绘制出6-7月的GMV
data2.日期 = pd.to_datetime(data2.日期)
#先对日期格式转换为datetime64格式,方便我们对日期进行采样操作
data2['下单率'] = data2.下单人数/data2.进店人数
data2['进店率'] = data2.进店人数/data2.曝光人数
#分别创建字段:下单率&进店率

 

gmv_rates = data2.groupby(data2.日期).agg(GMV总和 = ('GMV','sum'),进店率 = ('进店率','sum'),下单率 = ('下单率','sum')).resample('1W').asfreq().dropna(how = 'any')
gmv_rates
#groupby日期--按照日期进行分组
#agg--分别对gmv,进店率,下单率进行sum聚合
#resample--让groupby以1周为单位的进行数据的分组
#asfreq--让resample生效
#dropna--删除掉有空值的行

plt.bar(gmv_rates.index, gmv_rates.GMV总和, label = 'GMV')

#bar--条形图
#x横坐标为gmv_rates的索引,y纵坐标为gmv_rates的GMV总合
#label--标签名:GMV
plt.legend()
#显示标签
for a,b in zip(gmv_rates.index,gmv_rates.GMV总和):
    plt.text(a,b+0.1,'%.0f'%b,ha='center',va='bottom',fontsize=16)
    
plt.xticks(rotation=-15)
#a,b 分别遍历 gmv_rates的index,gmv_rates的GMV总合的内容
#zip--生成(a[0],b[0]),(a[1],b[1]).....我们将它们看作是位置的坐标
#a,b+0.1--放入我们的坐标
#'%.0f'%b--在坐标位置,显示我们的b的值,作为标签
#ha、va 调整标签的位置
(array([18421., 18428., 18435., 18444., 18451., 18458., 18465.]),
 [Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, '')])

plt.plot(gmv_rates.index, gmv_rates.进店率,label = '进店率')
plt.plot(gmv_rates.index, gmv_rates.下单率,label = '下单率')
plt.legend()

for a,b in zip(gmv_rates.index, gmv_rates.进店率):
    plt.text(a,b+0.01,'%.2f'%b,ha='center',va='bottom',fontsize=16)
for a,b in zip(gmv_rates.index, gmv_rates.下单率):
    plt.text(a,b+0.01,'%.2f'%b,ha='center',va='bottom',fontsize=16)
plt.xticks(rotation=-15)
(array([18421., 18428., 18435., 18444., 18451., 18458., 18465.]),
 [Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, '')])

#先对日期格式转换为datetime64格式,方便我们对日期进行采样操作
data3.下单日期 = pd.to_datetime(data3.下单日期)
data3.下单时间 = pd.to_datetime(data3.下单时间)

data['daycount'] = 1  #解析出天
gp_by_下单日期 = data.groupby(by='下单日期').count()['daycount']
gp_by_下单日期.plot(kind='line',fontsize=16)
plt.title('点菜数量与日期关系图')
plt.xlabel('日期')
plt.ylabel('订单数量')
plt.xticks(rotation=15)
array([-10.,   0.,  10.,  20.,  30.,  40.,  50.]),
 [Text(-10.0, 0, '2020/9/13'),
  Text(0.0, 0, '2020/7/28'),
  Text(10.0, 0, '2020/8/15'),
  Text(20.0, 0, '2020/8/24'),
  Text(30.0, 0, '2020/8/9'),
  Text(40.0, 0, '2020/9/5'),
  Text(50.0, 0, '')])

 

data3.下单日期 = pd.to_datetime(data3.下单日期)
data3.下单时间 = pd.to_datetime(data3.下单时间)

data['timecount'] = 1  #解析出时间
gp_by_下单时间 = data.groupby(by='下单时间').count()['timecount']
gp_by_下单时间.plot(kind='line',fontsize=16)
plt.title('点菜数量与时间关系图')
plt.xlabel('时间')
plt.ylabel('订单数量')
plt.xticks(rotation=15)
(array([-1000.,     0.,  1000.,  2000.,  3000.,  4000.,  5000.]),
 [Text(-1000.0, 0, '18:37:20'),
  Text(0.0, 0, '10:00:37'),
  Text(1000.0, 0, '11:31:07'),
  Text(2000.0, 0, '13:14:50'),
  Text(3000.0, 0, '18:20:14'),
  Text(4000.0, 0, '23:42:33'),
  Text(5000.0, 0, '')])

#用户个体消费分析
data.plot(kind='scatter',x='菜品个数',y='订单金额')
#从图中可知,用户的订单金额和点菜个数呈线性趋势,菜品金额不贵可能是平台补贴或者促销
#菜品数量极值点较少,可以忽略

 

#plt.subplot(121)
plt.xlabel('每个订单的消费金额')
data['订单金额'].plot(kind='hist',bins=20) #bins:区间分数,影响数字宽度,值越大柱子越细,宽度=(列最大值-最小值)/bins

 

data.groupby(by='用户id')['订单金额'].sum().sort_values().reset_index()

plt.xlabel('用户订单数')
data.groupby(by='用户id')['订单数'].sum().plot(kind='hist',bins=20)

#每个用户消费金额累加
data['amount_cumsum'] = data['订单金额'].cumsum()
data.tail()#查看最后五行

amount_total = data['amount_cumsum'].max()#消费金额总值
data['prop'] = data.apply(lambda x:x['amount_cumsum']/amount_total,axis=1)
#前xx名用户的总贡献率
data.tail()

plt.style.use('ggplot')
data['prop'].plot()
#由图分析可知,前2000名用户贡献总金额的43%,3000名用户贡献总金额的63%,剩余1418名用户贡献总金额的27%

 

#首购时间
data.groupby(by='用户id')['下单日期'].min().value_counts().plot()
#由图可知,首次购买的用户量在8.21日购买力逐渐下降,猜测本产品价格,味道有变化,或者选择了其他产品

 

 

#最后一次购买时间
data.groupby(by='用户id')['下单日期'].max().value_counts().plot()
#大多数用户最后一次购买时间集中在两个月,说明缺少忠诚用户
#购买商品用户呈下降趋势

 

#RFM模型
rfm = data.pivot_table(index = '用户id',
                      values=['订单金额','订单数','下单日期'],
                      aggfunc={
                          '下单日期':'max',
                          '订单数':'sum',
                          '订单金额':'sum'
                      })
rfm.head()
#每个用户最后一次购买时间-日期列中的最大值,转换成天数
rfm['下单日期'] = pd.to_datetime(rfm['下单日期'],format='%Y/%m/%d')
rfm['R'] = - (rfm['下单日期'] - rfm['下单日期'].max())/np.timedelta64(1,'D')#取相差的天数,保留一位小数
rfm.rename(columns={'订单数':'F','订单金额':'M'},inplace=True)
rfm

 

 

#RFM计算方式,每一列数据减去数据所在列的平均值,有正有负,根据结果值与1作比较,如果>=1,设置为1,否则0
def rfm_func(x):
    level = x.apply(lambda x:'1' if x>=1 else'0')
    label = level['R']+level['F']+level['M']
    d={
        '111':'重要价值客户',
        '011':'重要保持客户',
        '101':'重要发展客户',
        '001':'重要挽留客户',
        '110':'一般价值客户',
        '010':'一般保持客户',
        '100':'一般发展客户',
        '000':'一般挽留客户'
    }
    result = d[label]
    return result
    
rfm['label']= rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm.head()
#R:最近一次消费
#F:消费频率
#M:消费金额

#客户分层可视化
for label,grouped in rfm.groupby(by='label'):
    x = grouped['F']
    y = grouped['R']
    plt.scatter(x,y,label=label)
plt.legend()
plt.xlabel('F')
plt.ylabel('R')

pivoted_counts = data.pivot_table(
                     index ='用户id',
                     columns ='下单日期',
                     values = '订单数',
   
                   # aggfunc =  '订单金额'
).fillna(0)
pivoted_counts

 

 

data_purchase = pivoted_counts.applymap(lambda x:1 if x>0 else 0)
data_purchase.info()
#apply:作用与dataframe数据中的一行或者一列数据
#applymap:作用与dataframe数据中的每一个元素
#map:本身是一个series的函数,在data结构中无法使用map函数,map函数作用于series中每一个元素
def active_status(data):#data 整行数据,共45列
    status = [] #负责存储45个月的状态:unreg|new|acitive|unactive|return
    for i in range(45):
        #本月没有消费
        if data[i] ==0:
            if len(status) ==0: #前面没有任何记录
                status.append('unreg')
            else:#开始判断上一个状态
                if status[i-1]=='unreg':#一直未消费过
                    status.append('unreg')
                else:#new|acitive|unactive|return
                    status.append('unactive')#不管上个月是否消费过,本月都是不活跃用户
            pass
        #本月有消费==1
        else:
            if len(status)==0:#前面没有任何记录
                status.append('new')#第一次消费
            else:
                if status[i-1]=='unactive':
                    status.append('return')
                elif status[i-1]=='unreg':
                    status.append('new')
                else:#new|acitive|return
                    status.append('active')
    return pd.Series(status,data_purchase.columns)#值status,列名data_purchase中的列名

purchase_states = data_purchase.apply(active_status,axis=1)#得到用户分层结果
purchase_states.head()

 

#用Nan替换unreg
purchase_states_ct = purchase_states.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_states_ct.head(60)
purchase_states_ct.T.fillna(0).plot.area()#行列变换
#新用户占比较大
#8月开始新用户和活跃客户开始下降,并且呈现稳定趋势
#回流客户比较稳定,是餐厅的重要客户

#回流用户的占比
rate = purchase_states_ct.T.fillna(0).apply(lambda x:x/x.sum() ,axis = 1)
plt.plot(rate['return'],label='return')
plt.plot(rate['active'],label='active')
plt.legend()
#回流用户下降且不稳定
#活跃用户持续下降
#外卖运营平稳后,回流用户占比>活跃用户

 

 

#用户的购买周期
data['下单日期'] = pd.to_datetime(data['下单日期'],format='%Y/%m/%d')
order_diff = data.groupby(by ='用户id').apply(lambda x:x['下单日期']-x['下单日期'].shift())
order_diff.describe()
count                         1516
mean     4 days 02:08:13.931398416
std      9 days 17:37:25.671726657
min             -30 days +00:00:00
25%                1 days 00:00:00
50%                2 days 00:00:00
75%                7 days 00:00:00
max               48 days 00:00:00
Name: 下单日期, dtype: object
(order_diff/np.timedelta64(1,'D')).hist(bins = 20)#影响柱子的宽度,每个柱子的宽度 = (最大值-最小值)/bins 
#用户购买周期太低,赠送消费券,增大消费频率

 

#用户生命周期
#用户最后一次购买日期(max)-第一次购买的日期(min)。如果差值==0,说明用户仅仅购买了一次
user_life = data.groupby('用户id')['下单日期'].agg(['min','max'])
(user_life['max'] == user_life['min']).value_counts().plot.pie(autopct='%1.1f%%')#格式化成1为小数
plt.legend(['仅消费一次','多次消费'])#多次消费用户占比低,说明运营不利,或者产品问题。留存率不好

(user_life['max']-user_life['min']).describe()#生命周期描述
#用户平均生命周期为2天,但是中位数==0,再次验证大多数用户消费了一次,属于低质量用户
#75%分位数以后的用户,属于核心用户,需要重点维护
#std标准差
count                         2903
mean     2 days 14:35:00.723389596
std      6 days 22:37:00.475735135
min                0 days 00:00:00
25%                0 days 00:00:00
50%                0 days 00:00:00
75%                0 days 00:00:00
max               48 days 00:00:00
dtype: object
#绘制所有用户生命周期直方图
plt.figure(figsize=(12,6))
plt.subplot(121)
((user_life['max']-user_life['min'])/np.timedelta64(1,'D')).hist(bins=15)
plt.title('所有用户生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')

plt.subplot(122)
u_1 = (user_life['max']-user_life['min']).reset_index()[0]/np.timedelta64(1,'D')
u_1[u_1>0].hist(bins=15)
plt.title('用户多次消费生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')
#对比可知,第二幅图过滤掉了生命周期==0的用户,但是留存率十分低
#第二张图还有一部分用户的生命周期趋于0天,但比第一幅图好了一些,进行了多次消费,但不长期
#普通用户可针对性进行营销推广活动
#少部分用户生命周期集中在10-20天,属于忠诚用户,需要大力度维护

#回购率分析:相邻两个月重复购买
def purchase_back(data):
    status = []
    #当前月份消费了
    for i in range(44):
        if data[i] ==1:
            if data [i+1]==1:
                status.append(1)#回购用户
            elif data[i+1]==0:#下个月未消费
                status.append(0)
        else:#当前月份未进行消费
            status.append(np.NaN)
    status.append(np.NaN)#填充最后一列数据
    return pd.Series(status,data_purchase.columns)
purchase_b = data_purchase.apply(purchase_back,axis=1)
purchase_b.head()
#回购率可视化
plt.figure(figsize=(20,10))
plt.subplot(211)
#回购率
(purchase_b.sum()/purchase_b.count()).plot(label='回购率')
(purchase_r.sum()/purchase_r.count()).plot(label='复购率')
plt.legend()
plt.ylabel('百分比%')
plt.title('用户回购率和复购率对比图')
#回购率波动较大,复购率低于回购率
#新用户需要时间,新用户忠诚度低于老客户

#回购人数与购物总人数
plt.subplot(212)
plt.plot(purchase_b.sum(),label='回购人数')
plt.plot(purchase_b.count(),label='购物总人数')
plt.xlabel('month')
plt.ylabel('人数')
plt.legend()
#购物总人数远远大于回购人数,因为很多用户在1月份进行了首购

 Tableau

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_70084548/article/details/125527717

智能推荐

Linux分区还是sdb而不是sdb1,Linux 磁盘分区-程序员宅基地

文章浏览阅读2.5k次。第一步:对系统当前各个磁盘的状态要有一定的了解。这个就要用到fdisk -l 这个命令了。fdisk -l由上图可以看出/dev/sdb/并没有被分区、下面的例子我们就要为/dev/sdb进行分区而且还要把它挂载到linux目录树上去。第二步:fdisk /dev/sdb -- 这个的意思就是对/dev/sdb这个盘进行操作,如上图所示,说明已经进入了fdisk这个程序了;它的一些重要的操作项有..._sdb和sdb1

启动类启动找不到或无法加载主类_找不到或无法加载主类 com.eurekaserverapplication-程序员宅基地

文章浏览阅读524次。错误: 找不到或无法加载主类 com.itan.EurekaServer01Application点击install后org.apache.maven.plugins:maven-install-plugin:2.5.2:install (default-cli) on project eureka-server01: The packaging for this project did not assign a file to the build artifact不行后来点击lifecyle_找不到或无法加载主类 com.eurekaserverapplication

【OpenCV学习笔记】我的OpenCV学习之路-程序员宅基地

文章浏览阅读269次。图像平滑从信号处理的角度看就是去除其中的高频信息,保留低频信息。因此我们可以对图像实施低通滤波。低通滤波可以去除图像中的噪声,对图像进行平滑。根据滤波器的不同可分为均值滤波,高斯滤波,中值滤波,双边滤波。1.模板匹配原理:在给定的图片中查找和模板最相似的区域API:利用cv.matchTemplate0进行模板匹配,然后使用cv.minMaxLoc0搜索最匹配的位置。_opencv学习之路

七十六、Fluent初始化patch和UDF详解_fluent中patch-程序员宅基地

文章浏览阅读1.3k次,点赞19次,收藏19次。这可以包括对初始化的速度场、温度场、体积分数场等进行调整,也可以是对经过计算后的各物理量场进行修正。Patch操作的目的是在流场中选择性地改变某些部分的特定物理量数值。_fluent中patch

Java课程设计-基于Java Swing的职工信息管理系统_基于java swing管理系统-程序员宅基地

文章浏览阅读2.4w次,点赞10次,收藏76次。基于Java Swing的职工信息管理系统职工信息管理系统1.介绍2.相关技术3.项目地址4.所需环境5.安装教程6.运行截图7.相关博客1.介绍在职工信息管理系统中,主要是对职工信息管理,功能分下如下:1.职工信息的添加2.职工信息的查询(按工号进行查询)3.职工信息的删除(按工号进行删除)4.职工信息的修改(按工号进行修改)2.相关技术Java的Swing编程Java的JDBC编程3.项目地址https://gitee.com/jack0240/staff-manage.git_基于java swing管理系统

2023年全国职业院校技能大赛 模块B:Windows+Linux服务部署——完整版、视频配置+赛题解析_全国职业院校技能大赛网络建设与运维windows-程序员宅基地

文章浏览阅读1k次,点赞35次,收藏10次。基于2023年全国职业院校技能大赛——网络系统管理赛项——Windows+Linux服务部署——完整版(包含 视频配置+赛题解析)内容完整~负责售后服务。_全国职业院校技能大赛网络建设与运维windows

随便推点

matlab 图片显示出现中文乱码_matlab2018a图片显示中文-程序员宅基地

文章浏览阅读9.6k次。解决matlab 2016 图片标题显示乱码的问题。_matlab2018a图片显示中文

python爬考研_Python爬取考研必备单词-程序员宅基地

文章浏览阅读158次。参考链接:(https://blog.csdn.net/OnlyloveCuracao/art0icle/details/80768334)原博主的代码可能因为单词发音的音频爬取有问题,导致无法将单词存入数据库,不过也非常感谢原博主,我根据源码做了一定删减和更改,下次可能会根据用户的需求,输入需求,爬取相应的单词。主要步骤:1.连接数据库2.创建word单词表3.获取网页主界面HTML代码4.获取..._python爬取英语考试重点

vue + vue-video-player添加多个视频案例,包含更改视频播放插件样式CSS_vue video更改样式-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏13次。vue + vue-video-player添加多个视频案例,包含更改视频播放插件样式CSS<!--城市动态面板--><template> <div> <div class="player" v-for="(item,index) in videoConfig" :key="index"> <video-player class="video-player vjs-custom_vue video更改样式

江科大-stm32-B站系统初识笔记P2_江科大stm32用户手册-程序员宅基地

文章浏览阅读1.2k次,点赞20次,收藏23次。①:②:系统结构AHB系统总线:Advanced High Performance Bus(一般是72M)cpu总裁,DMA秘书,外设小弟③:引脚定义:标红:电源相关引脚标蓝:最小系统相关引脚标绿:IO口、功能口有FT的,能容忍5v电压,没有FT的,只能容忍3.3v电压,如果需要接5v电平,需要加装电平转换电路主功能和默认复用功能:这句话的意思是如果你想用一个引脚的默认复用的两个功能,可以映射到其他引脚用。_江科大stm32用户手册

FPGA拾忆_(1):FPGA设计总流程-程序员宅基地

文章浏览阅读454次,点赞12次,收藏10次。FPGA设计流程如下图所示: 设计定义:确定要实现的功能,如用按键来控制LED灯的亮灭,本质是设计一个 多路选择器(由一个按键控制两路信号),即 将功能抽象成基本电路的组合设计。 设计输入:确定输入信号与输出信号,使用硬件描述语音来描述功能。 分析和综合:综合是将抽象的RTL语言转变成具体的电路的过程,编译(Compile)与纠错过程。 功能仿真:RTL级别仿真,通过quartus II软件可以直接调用modelsim-altera进行,初下载的配置操作:(1)quartus-tool

面试官| 聊聊Spring Batch批处理框架_spring batch面试-程序员宅基地

文章浏览阅读190次。Spring Batch 是 Spring 提供的一个数据处理框架。企业域中的许多应用程序需要批量处理才能在关键任务环境中执行业务操作。这些业务运营包括:无需用户交互即可最有效地处理大量信息的自动化,复杂处理。这些操作通常包括基于时间的事件(例如月末计算,通知或通信)。在非常大的数据集中重复处理复杂业务规则的定期应用(例如,保险利益确定或费率调整)。集成从内部和外部系统接收的信息,这些信息通常需要以事务方式格式化,验证和处理到记录系统中。批处理用于每天为企业处理数十亿的交易。Spring Batch 是一个_spring batch面试

推荐文章

热门文章

相关标签