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

技术标签: 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

智能推荐

stl map遍历和删除的方法_vs2008 stl map查询equal_range(),并删除-程序员宅基地

文章浏览阅读6.1k次。for(;iter!=mapStudent.end();){ if((iter->second)>=aa) { //满足删除条件,删除当前结点,并指向下面一个结点 mapStudent.erase(iter++); } else { //_vs2008 stl map查询equal_range(),并删除

rank函数python_Python pandas.DataFrame.rank函数方法的使用-程序员宅基地

文章浏览阅读1.4k次。DataFrame.rank(self: ~FrameOrSeries,axis=0,method: str = 'average',numeric_only: Union[bool,NoneType] = None,na_option: str = 'keep',ascending: bool = True,pct: bool = False)→ ~FrameOrSeries计算..._python dataframe rank

彻底扒光 通过智能路由器拆解看其本质-程序员宅基地

文章浏览阅读1.7k次。可以看到很多联发科的MT芯片摘自:https://net.zol.com.cn/531/5312999.html彻底扒光 通过智能路由器拆解看其本质2015-07-23 00:40:00[中关村在线 原创] 作者:陈赫|责编:白宁收藏文章 分享到 评论(24)关注智能路由器拆解的朋友们注意啦!我们已经将这五款产品彻底扒开,将主板的真容展现在了大家的眼前。网友们可以看见这些智能路由器主板的做工和用料,我们还为网友们展示了主要的电子元器件,供大家品评观赏。..._路由器拆解

Java--深入JDK和hotspot底层源码剖析Thread的run()、start()方法执行过程_jdk的源码hotspot跟jdk是分开的-程序员宅基地

文章浏览阅读2.1k次,点赞101次,收藏78次。【学习背景】今天主要是来了解Java线程Thread中的run()、start()两个方法的执行有哪些区别,会给出一个简单的测试代码样例,快速理解两者的区别,再从源码层面去追溯start()底层是如何最终调用Thread#run()方法的,个人觉得这样的学习不论对面试,还是实际编程来说都是比较有帮助的。进入正文~学习目录一、代码测试二、源码分析2.1 run()方法2.2 start()方法三、使用总结一、代码测试执行Thread的run()、start()方法的测试代码如下:public_jdk的源码hotspot跟jdk是分开的

透视俄乌网络战之一:数据擦除软件_俄乌网络战观察(一)-程序员宅基地

文章浏览阅读4.4k次,点赞90次,收藏85次。俄乌冲突中,各方势力通过数据擦除恶意软件破坏关键信息基础设施计算机的数据,达到深度致瘫的效果,同时窃取重要敏感信息。_俄乌网络战观察(一)

Maven私服仓库配置-Nexus详解_nexus maven-程序员宅基地

文章浏览阅读1.7w次,点赞23次,收藏139次。Maven 私服是一种特殊的Maven远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。当然也并不是说私服只能建立在局域网,也有很多公司会直接把私服部署到公网,具体还是得看公司业务的性质是否是保密的等等,因为局域网的话只能在公司用,部署到公网的话员工在家里也可以办公使用。_nexus maven

随便推点

软件体系结构_采用结构化技术开发的软件是否具有体系结构?-程序员宅基地

文章浏览阅读2k次。 软件体系结构是具有一定形式的结构化元素,即构件的集合,包括处理构件、数据构件和连接构件。处理构件负责对数据进行加工,数据构件是被加工的信息,连接构件把体系结构的不同部分组组合连接起来。这一定义注重区分处理构件、数据构件和连接构件,这一方法在其他的定义和方法中基本上得到保持。 一、软件体系结构的定义  虽然软件体系结构已经在软件工程领域中有着广泛的应用,但迄今为止还没有一个被大家所公认的_采用结构化技术开发的软件是否具有体系结构?

springbbot运行无法编译成功,找不到jar包报错:Error:(3, 46) java: 程序包org.springframework.context.annotation不存在-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏2次。文章目录问题描述:解决方案:问题描述:提示:idea springbbot运行无法编译成功,找不到jar包报错E:\ideaProject\demokkkk\src\main\java\com\example\demo\config\WebSocketConfig.javaError:(3, 46) java: 程序包org.springframework.context.annotation不存在Error:(4, 46) java: 程序包org.springframework.conte_error:(3, 46) java: 程序包org.springframework.context.annotation不存在

react常见面试题_recate面试-程序员宅基地

文章浏览阅读6.4k次,点赞6次,收藏36次。1、redux中间件中间件提供第三方插件的模式,自定义拦截 action -&gt; reducer 的过程。变为 action -&gt; middlewares -&gt; reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。常见的中间件:redux-logger:提供日志输出redux-thunk:处理异步操作..._recate面试

交叉编译jpeglib遇到的问题-程序员宅基地

文章浏览阅读405次。由于要在开发板中加载libjpeg,不能使用gcc编译的库文件给以使用,需要自己配置使用另外的编译器编译该库文件。/usr/bin/ld:.libs/jaricom.o:RelocationsingenericELF(EM:40)/usr/bin/ld:.libs/jaricom.o:RelocationsingenericELF(EM:40)...._jpeg_utils.lo: relocations in generic elf (em: 8) error adding symbols: file

【办公类-22-06】周计划系列(1)“信息窗” (2024年调整版本)-程序员宅基地

文章浏览阅读578次,点赞10次,收藏17次。【办公类-22-06】周计划系列(1)“信息窗” (2024年调整版本)

SEO优化_百度seo resetful-程序员宅基地

文章浏览阅读309次。SEO全称为Search Engine Optimization,中文解释为搜索引擎优化。一般指通过对网站内部调整优化及站外优化,使网站满足搜索引擎收录排名需求,在搜索引擎中提高关键词排名,从而把精准..._百度seo resetful

推荐文章

热门文章

相关标签