独家 | 使用python马尔科夫链方法建模星巴克等待时长
off999 2024-11-24 20:11 32 浏览 0 评论
作者:Piero Paialunga
翻译:陈超
校对:和中华
本文约4200字,建议阅读11分钟
本文使用马尔科夫链的方法对星巴克购买咖啡的等待时长进行建模。
以下内容关于如何使用马尔科夫链计算你在星巴克咖啡的等待时长。
图片来自Unplash, Jon Tyson摄
我来自意大利,可以肯定的说,咖啡就是信仰。我们喝咖啡来社交,在早上喝来唤醒我们,在午餐、晚餐后也会喝咖啡。如果好久未见某个朋友,我们会说
“Vieni a prenderti un caffè”
意思是
“来喝杯咖啡吧”
我住在美国,美国人喝咖啡的方式完全不同。我上班的时候会选择将咖啡带走喝。在工作的时候喝咖啡。看电影的时候喝咖啡。美国人不喝“意式浓缩”,但是他们很享受花很长的时间喝一大整杯。此外:他们有很多种咖啡!
走进星巴克你可能会看到上百种咖啡。有黑咖啡,黑塔玛奇朵,拿铁或者星冰乐,或者是其他很多我叫不上名字的品类。
这里有很多容易制作的,也有很多复杂的咖啡。假设你在星巴克排队点咖啡,如果前面有三个人都点了黑咖啡,那你可能需要3分钟拿到你的咖啡。
然而,如果他们点了“额外鲜奶油加糖屑肉桂豆浆的焦糖玛奇朵”……那你的等待时长可能会加倍,或者至少你得多等几分钟。
所以问题是……
“我要等多久才能拿到咖啡,在此期间我可以写篇关于我要等多久才能拿到咖啡的文章吗?”
当然,我们无法获知其他人要点什么,所以这是一个概率问题(或者如果你想要一个随机过程)。所以我们该如何解决它呢?
一个可行的方法是建立马尔科夫链。或者说,我们需要一个时间依赖的马尔科夫链。
让我们来解决问题吧!
1. 理论介绍
让我们从问题的理论背景介绍和纠正错误开始。
先从最简单的例子开始。我们进入星巴克开始点单。用数学语言来说,我们可能有三种状态。
第一种状态(O)是我们点咖啡。第二种状态(M)我们已经点完了咖啡并在等待。M状态导致他们帮你做咖啡。然后你拿到咖啡并转移到离开状态(L)。这意味着你的咖啡已经做好了你也可以走了。
图片来自作者
好的。现在,可能的转移状态有哪些?
1) 你从点单到制作(O到M)
2) 也能从M到M(持续等待)
3) 最后你从M到L
图片来自作者
我们如何将其形式化呢?
1.1 关于马尔科夫链
马尔科夫链的假设是什么?具体如下:
“处入下一状态的概率仅依赖于当前状态”
例如:
在t=5的时候处入离开状态L的概率仅仅依赖于你在t=4时处于制作状态M中。
让我们将其形式化:
图片来自作者
在上述符号系统当中,我们在时间t时状态的概率,在空间s_t (O, M, L) 里仅依赖于我们在t-1时的状态。
在实验中我们还需要记住的是,t时刻的概率也是时间依赖的。因为,如果你已经等了5分钟,在下一分钟离开的概率就比只等待1分钟后离开的概率更大。
这意味着:
图片来自作者
这就是文章开头提到过的概念。
当然,星巴克不仅有我们在,也有很多其他顾客在!所以我们马上会把这个设置复杂化。但上述是一切的起点。
接下来我们正式开始!
2. 一顾客一饮品的例子
让我们从最简单的情况开始。我们知道要喝什么并且我们是咖啡店唯一的顾客。
假设我们想要一杯焦糖玛奇朵。allrecipes食谱网站说做一杯需要5分钟。假设我们需要30秒来点单和支付。所以总共等待时间可能有5分30秒。但是让我们更进一步,5分钟只是平均制作时间,一般要用4-6分钟:
图片来自作者
好的。我们假设时间刻度是30秒(0.5分钟)。我们在30秒后有可能拿到我们的咖啡,也有可能8分钟之后我们仍然在等待。
2.1 动手实现
让我们来执行一下这个过程。首先导入库:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import signal
import sklearn
import seaborn as sns
Hosted on Jovian接下来定义状态:
customer_state = ['Ordering the coffee','Waiting for your coffee','Leaving']
states = {'O':customer_state[0],'M':customer_state[1],'L':customer_state[2]}接下来我们使用下面这个函数运行上述过程:
mu, sigma = 5,1
def one_drink_one_cust():
start = states['O']
print(start+'\n')
ordering_time = 0.5
first_state = states['M']
print(first_state+'\n')
waiting_time = 0
k = 0
while k == 0:
p = stats.norm.cdf(waiting_time, loc=mu, scale=sigma)
k = np.random.choice([0,1],p = [1-p,p])
waiting_time = waiting_time+0.5
if k == 0:
print('Coffee is brewing... \n')
print('Your coffee is ready! \n')
print(states['L']+'\n')
print('Waiting time is = %.2f'%(waiting_time))
return waiting_time这里是一次运行例子:
运行 one_drink_one_cust()3. 一顾客多饮品
现在,让我们增加模型复杂性让情况更真实一些。
我们并不知道特定的顾客想要啥。而这种情况更真实的原因在于星巴克有多达150种饮品,且他们可能需要不同的等待时长。
现在马尔科夫链看起来像这样
图片来自作者
与之前的一个例子不同之处在于,这里有一个概率分布,我们叫做:
图片来自作者
特别地,所有可能的饮品有一个概率分布,我们叫做:
图片来自作者
例如:
图片来自作者
所以我们可以说:
图片来自作者
3.1实际操作
我们可以从kaggle数据集
(https://www.kaggle.com/datasets/ashishpatel26/starbucks-menu-nutrition-drinks)导入所有的
星巴克饮品:
pd.read_csv('starbucks-menu-nutrition-drinks.csv')177 rows × 7 columns
Hosted on Jovian我们不需要其他列。
我们不知道最热门的单品是哪一个,所以我们做一个假的分布:
kind_of_coffee = np.array(pd.read_csv('starbucks-menu-nutrition-drinks.csv')['Unnamed: 0'])
p_start = []
for i in range(len(kind_of_coffee)):
p_start.append(np.random.choice(np.arange(50,100)))
p_start = np.array(np.array(list(np.array(p_start)/sum(p_start))))
Hosted on Jovian 我们从50-100提取值,然后我们用和进行归一化,得到一个概率向量
coffe_picked = []
for i in range(10000):
coffe_picked.append(np.random.choice(range(0,len(kind_of_coffee)),p=p_start))
sns.displot(coffe_picked)我们有了自己的概率分布。我们接下来要做什么?概率分布将会为我们作出选择:我们将从分布中取样。然后假设每种咖啡需要不同的制作时间。
现在,客观来说,没有太大的差异。假设需要3分钟(平均时长)来完成一杯最“简单”的咖啡(可能是黑咖啡),6分钟(平均时长)来完成一杯最复杂的咖啡(额外鲜奶油加糖屑肉桂豆浆的焦糖玛奇朵)
我们把方差变化范围取到0.1-1。
coffee_data = pd.DataFrame(kind_of_coffee,columns=['State 1'])
mu_list = []
var_list = []
for i in range(len(coffee_data)):
mu_list.append(np.random.choice(np.linspace(3,6,1000)))
var_list.append(np.random.choice(np.linspace(0.1,1.5,1000)))
coffee_data[r'$\mu#39;]=mu_list
coffee_data[r'$\sigma#39;]=var_list
coffee_data[r'$p#39;] = p_start
coffee_data.head()我们的均值可以从3-6取值,平均等待时长也可以从3-6分钟,有时不确定性更大。例如,有可能当你喝咖啡A的时候,等待的时间更长。
用代码来实现这一情形:
def random_drink_one_cust():
start = states['O']
print(start+'\n')
ordering_time = 0.5
first_state = states['M']
chosen_i = np.random.choice(range(0,len(kind_of_coffee)),p=p_start)
print('Ordering coffee %s'%(kind_of_coffee[chosen_i]))
print(first_state+'\n')
mu_i, var_i = coffee_data[r'$\mu#39;].loc[chosen_i], coffee_data[r'$\sigma#39;].loc[chosen_i]
waiting_time = 0
k = 0
while k == 0:
p = stats.norm.cdf(waiting_time, loc=mu_i, scale=var_i)
k = np.random.choice([0,1],p = [1-p,p])
waiting_time = waiting_time+0.5
if k == 0:
print('Coffee is brewing... \n')
print('Your coffee is ready! \n')
print(states['L']+'\n')
print('Waiting time is = %.2f'%(waiting_time))
return waiting_time
random_drink_one_cust()输出:
Ordering the coffee
Ordering coffee Teavana? Shaken Iced Black Tea
Waiting for your coffee
Coffee is brewing...
Coffee is brewing...
Coffee is brewing...
Coffee is brewing...
Coffee is brewing...
Coffee is brewing...
Coffee is brewing...
Your coffee is ready!
Leaving
Waiting time is = 4.00
4.0
现在,我相信我们可以提取一些额外的有价值的信息。例如……
“不管你在星巴克点什么咖啡,你通常需要等多久呢?”
这一个问题非常宽泛,所以我们也可以用一些统计数据来回答:
waiting_time_list = []
for i in range(10000):
waiting_time_list.append(random_drink_one_cust())
plt.figure(figsize=(20,10))
plt.subplot(2,1,1)
sns.histplot(waiting_time_list,fill=True)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.xlabel('Waiting time (minutes)',fontsize=14)
plt.ylabel('Count',fontsize=14)
plt.subplot(2,1,2)
sns.kdeplot(waiting_time_list,fill=True)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.xlabel('Waiting time (minutes)',fontsize=14)
plt.ylabel('Probability Density Function (PDF)',fontsize=14)
Text(0, 0.5, 'Probability Density Function (PDF)')我改变了函数,去掉了所有的“print”,以便于我的电脑不会爆炸。
4. 多顾客多饮品
好的。现在我们有多名顾客,我们怎么办呢?现在的情形看起来像这样:
图片来自作者
现在去思考每次只能等前一杯咖啡做完才能做下一杯的情况已经不现实了。星巴克通常都有3,4个咖啡师。
这会让代码变得有点复杂,因为我们需要追踪所有咖啡师是否忙碌的事实。由于这个原因,该函数我们将设计为顾客数和咖啡师数量的函数。
函数如下:
def random_drink_multiple_cust(cust=2,num_baristas =5):
time_of_process = []
baristas = np.zeros(num_baristas)
q = 0
ordering_time = 0
for c in range(cust):
start = states['O']
print('Customer number %i is ordering'%(c))
ordering_time+=0.5
if sum(baristas)!=num_baristas:
print('There is at least one free baristas! :)')
waiting_time = random_drink_one_cust()
time_of_process.append(waiting_time+ordering_time)
baristas[q] = 1
q = q + 1
if len(time_of_process)==cust:
break
if sum(baristas)==num_baristas:
print('All the baristas are busy :(')
print('You have to wait an additional %i minutes until they can start making your coffee' %(min(time_of_process)))
waiting_time = min(time_of_process)+random_drink_one_cust()+ordering_time
baristas[num_baristas-1]=0
time_of_process.append(waiting_time)
q = q-1
if len(time_of_process)==cust:
break
print('The waiting time for each customer is:')
print(time_of_process)
return time_of_process好的,让我们来测试一下4个顾客和2个咖啡师的情况。
random_drink_multiple_cust(4,2)
Customer number 0 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Iced Caffè Americano
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 1 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Iced Skinny Mocha
Waiting for your coffee
Your coffee is ready!
Leaving
All the baristas are busy :(
You have to wait an additional 6 minutes until they can start making your coffee
Ordering the coffee
Ordering coffee Tazo? Bottled Organic Iced Black Tea
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 2 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Starbucks? Bottled Caramel Frappuccino? Coffee Drink
Waiting for your coffee
Your coffee is ready!
Leaving
The waiting time for each customer is:
[7.0, 6.5, 14.0, 5.5]
[7.0, 6.5, 14.0, 5.5]在前两个例子中,咖啡师是有空的情况下,他们分别要等待7分钟和6.5分钟。
注意?这不是错误!这只意味着第二杯咖啡快于第一杯咖啡+点单的时间和。你仔细想想就可以想得通。如果你点了一杯复杂的咖啡,我只点了一杯拿铁,我可以比你等的时间更短,即使你在我之前点单。
然后是咖啡师忙的情况。我们不得不在点单之前额外等6分钟。(我可以让情况更真实一点,并减去1分钟点单时间,但是你的想法是对的吗?)
到第四个顾客点单时,有一个空闲的咖啡师,所以他不用再等那么长时间,只需要等5.5分钟。
注意到这个模型的复杂度以及情况的有趣性。以下是10个顾客和5个咖啡师:
random_drink_multiple_cust(10,5)
Customer number 0 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Iced Coconutmilk Mocha Macchiato
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 1 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Coffee Frappuccino? Blended Coffee
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 2 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Strawberry Acai Starbucks Refreshers? Beverage
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 3 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Shaken Sweet Tea
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 4 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Coffee Traveler
Waiting for your coffee
Your coffee is ready!
Leaving
All the baristas are busy :(
You have to wait an additional 5 minutes until they can start making your coffee
Ordering the coffee
Ordering coffee Starbucks? Doubleshot Protein Vanilla
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 5 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Coconutmilk Mocha Macchiato
Waiting for your coffee
Your coffee is ready!
Leaving
All the baristas are busy :(
You have to wait an additional 5 minutes until they can start making your coffee
Ordering the coffee
Ordering coffee Nitro Cold Brew with Sweet Cream
Waiting for your coffee
Your coffee is ready!
Leaving
Customer number 6 is ordering
There is at least one free baristas! :)
Ordering the coffee
Ordering coffee Starbucks? Low Calorie Iced Coffee + Milk
Waiting for your coffee
Your coffee is ready!
Leaving
All the baristas are busy :(
You have to wait an additional 5 minutes until they can start making your coffee
Ordering the coffee
Ordering coffee Tazo? Bottled Lemon Ginger
Waiting for your coffee
Your coffee is ready!
Leaving
The waiting time for each customer is:
[5.5, 5.5, 7.0, 8.0, 6.5, 12.5, 8.5, 12.5, 7.5, 16.0]
[5.5, 5.5, 7.0, 8.0, 6.5, 12.5, 8.5, 12.5, 7.5, 16.0]5. 注意事项
在本文中,我们讨论一个排队等待时长的一般性问题。有不同的场景:
1) 我们可能是商店唯一顾客且明确知道自己想要买什么
2) 我们可能是商店唯一顾客但不知道自己想要买什么
3) 我们不是商店唯一顾客且不知道自己想要买什么
这三个场景依次变得复杂。由此,马尔科夫链也会变得越来越复杂。
1) 在第一个例子里,唯一的概率分布是煮咖啡的过程。例如,需要花费平均时长5分钟来做焦糖玛奇朵,制作时长是方差为1的高斯分布
2) 在第二个例子里,我们得到了所选咖啡的概率分布加上制作咖啡的概率分布,也就是之前那个
3) 第三个例子里,我们用上述两种情况,并且咖啡师变成了另外一个需要考虑的因素
我们可以让那个这个模型更复杂一点。我们可以考虑这样一个事实:机器是可以坏的。我们也可以考虑咖啡师人手不够的情况。可以考虑咖啡师人数随时间变化,也可以考虑一些咖啡师比另外一些人操作更快。以及更多可能性。
这个起点可以用来以几乎所有你想要的方式使模型复杂化。只要记住,我们使用的是时间相关马尔可夫链工具,并以此为基础进行构建。
原文标题:
Modeling Starbucks Waiting Time Using Markov Chains, with Python
原文链接:
https://towardsdatascience.com/modeling-starbucks-waiting-time-using-markov-chains-with-python-ab267670d02c
相关推荐
- windows server 2008激活
-
CAD不注册激活的话,一般只能试用30天,而且每次打开都会弹出激活窗口。为了能够永久使用,我们需要一个注册机来帮我们生成一个激活码激活它。下载CAD2008注册机(注册机必须与所要激活的CAD版本相对...
- 邮件服务器(hmailserver搭建邮件服务器)
-
电子邮件服务器名称:比如添加的是网易邮箱帐号在“接收邮件(pop、IMAP或HTTP)服务器:”字段中输入pop.163.com。在“发送邮件服务器(SMTP):”字段中输入smtp.163.com...
- win7蓝屏0x0000000a(win7蓝屏代码0x000000f4)
-
电脑蓝屏代码0x0000000a的原因以及解决办法如下:1、在BIOS界面内,进入“Intogratedperipherals”选项里把“SATAConfiguration”项的值改成IDE。&...
- 手机看nwd格式的3d图软件(手机看3d图片)
-
NWD只有具备NavisWorksPublisher许可才能保存NWD文件。这种文件格式存储NWF文件格式存储的所有NavisWorks特定数据,外加模型的几何图形。NWD文件一般比原始的CAD文件...
- windows系统正版(win10正版系统)
-
WINDOWS激活了不一定是正版,可以使用以下方法验证:"开始"菜单——"运行"中输入:1、slmgr.vbs-dli显示:操作系统版本、部分产品密钥、许可证状态...
- 创新声卡驱动安装教程(创新声卡7.1驱动安装)
-
1、准备工作:准备好声卡驱动安装文件,根据自己计算机操作系统的不同而选择不同的版本2、双击安装文件,根据弹出的提示进行操作,进行安装,可以选择安装至默认位置,安装过程中会出现若干设置,最好按照制造厂商...
- win10进入安全模式黑屏(win10进安全模式黑屏什么都不显示)
-
正常情况下进入“安全模式”屏幕是黑色的,没有壁纸。并且,与显卡,显示器没有任何关系。正常模式比安全模式多加载了很多启动,视频模式有所改变。如果出现异常,则有可能是以下问题引起:1、正常模式下...
- 问7升级win10(win 7升win 10)
-
Windows7系统如何升级为win10。嗯,这个是需要重新安装操作系统的。请在安装前先预备好备份好windows7系统下面你自己的个人数据备份到自己的移动硬盘上面。然后通过USB导的方式进行安装,...
- windows中文叫什么(win的中文是什么)
-
windows是窗口的意思,翻译到中文可以是窗户的意思。现在windows的意思大多用在PC领域,泛指微软出品的电脑、手机操作系统。windows操作系统业可以叫做视窗操作系统、可视化图形界面操作系统...
- win7系统激活工具下载免费(win7激活工具免费版)
-
KMSpico是一个非常好用的Win7系统激活工具。它简单易用,只需一步操作即可激活系统,无需复杂的设置和操作。同时,它支持离线激活和在线激活两种方式,让用户可以根据自己的需求选择使用。此外,KMSp...
- tenda路由器怎么设置网速快(tendawifi怎么设置网速快)
-
tenda设置网速最快方法如下:1.登入无线路由器;在浏览器地址输入192.168.0.1;无线路由器进入无线路由器控制界面几乎都是这个地址;原始密码admin。2.接着点击“高级设置”或者页面右上角...
- 功能最全的pe系统(pe系统功能介绍)
-
1、Windows预安装环境,是带有有限服务的最小Win32子系统,基于以保护模式运行的WindowsXPProfessional及以上内核。它包括运行Windows安装程序及脚本、连接网络共享、...
- 华为鸿蒙操作系统下载(华为鸿蒙电脑操作系统下载)
-
鸿蒙系统可以下载拼多多,但需要注意的是,目前拼多多官方并未推出面向鸿蒙系统的专门版本,所以需要通过安装第三方应用市场或者通过APK安装包的方式才能在鸿蒙系统上安装使用。此外,由于鸿蒙系统和拼多多应用的...
- 系统iso怎么安装(系统iso怎么安装应用)
-
ISO系统安装详细教程步骤如下:1.准备所需材料:一台符合系统要求的计算机、ISO系统文件、可启动的USB驱动器或光盘、系统激活密钥(如果需要的话)。2.制作启动盘:如果你使用USB驱动器作为启动...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
Python 批量卸载关联包 pip-autoremove
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
