編者按:在數(shù)據(jù)科學(xué)領(lǐng)域,值得閱讀的好書(shū)有很多,尤其是O‘Reilly出版的一系列動(dòng)物封面英文書(shū)。其中一本“蜥蜴書(shū)”叫《Python數(shù)據(jù)科學(xué)指南》(Python Data Science Handbook),書(shū)中詳細(xì)介紹了Jupyter、Numpy、Pandas、數(shù)據(jù)可視化和scikit-learn模塊的具體使用方式,是新手入門(mén)機(jī)器學(xué)習(xí)的一條捷徑。近日,這本書(shū)的作者Jake VanderPlas寫(xiě)了一篇博文,深入淺出地介紹了一個(gè)有趣的概念:等待時(shí)間悖論。
圖像來(lái)源:維基百科 許可:CC-BY-SA 3.0
經(jīng)常乘坐公共交通工具的人可能都遇到過(guò)這種情況:
你到公交站臺(tái)等車,標(biāo)牌顯示公交車的發(fā)車間隔是10分鐘一輛。你瞥了眼手表,記下時(shí)間……11分鐘后,公交車終于來(lái)了,你不由開(kāi)始懊惱:為什么我老是這么倒霉!
已知公交車每隔10分鐘發(fā)一班,你到站臺(tái)是在某個(gè)隨機(jī)時(shí)間點(diǎn)(不借助實(shí)時(shí)公交APP),面對(duì)這種情況,很多人會(huì)想當(dāng)然地覺(jué)得自己的平均等待時(shí)間應(yīng)該是5分鐘。但實(shí)際上,5分鐘后,公交車沒(méi)來(lái),這時(shí)你只能繼續(xù)等;10分鐘后,車子可能還是沒(méi)有來(lái)……在一些合理的數(shù)學(xué)假設(shè)下,你可以得出一個(gè)驚人結(jié)論:
當(dāng)公交車的平均發(fā)車間隔是10分鐘時(shí),乘客平均等公交車的時(shí)間也會(huì)是10分鐘。
這就是等待時(shí)間悖論。
那么這個(gè)悖論真實(shí)存在嗎?這些“合理的假設(shè)”究竟是只流于理論,還是同樣適用于現(xiàn)實(shí)?本文會(huì)以美國(guó)西雅圖市的真實(shí)公交車到達(dá)時(shí)間數(shù)據(jù)為例,從模擬和概率論證的角度探討這個(gè)問(wèn)題。
檢驗(yàn)悖論(Inspection Paradox)
如果每隔10分鐘一定有一班車到這個(gè)站臺(tái),那我們的平均等待時(shí)間確實(shí)是這個(gè)間隔的一半:5分鐘。但是,如果10分鐘只是個(gè)平均值,乘客的平均等待時(shí)間其實(shí)會(huì)比5分鐘更長(zhǎng)一些,這點(diǎn)不難理解。
等待時(shí)間悖論其實(shí)是檢驗(yàn)悖論的一個(gè)特殊例子,后者在日常生活中更普遍。舉一個(gè)簡(jiǎn)單例子,假設(shè)你正在調(diào)查某大學(xué)的班級(jí)規(guī)模,調(diào)查方法是隨機(jī)選一些學(xué)生問(wèn)“你所在的班級(jí)有多少人”再計(jì)算平均值,最后你統(tǒng)計(jì)出的結(jié)果是平均56人。但是,全校的班級(jí)平均人數(shù)實(shí)際上只有36(以上數(shù)據(jù)來(lái)自普渡大學(xué)調(diào)查)。這不是說(shuō)有人撒了謊,而是10人班級(jí)和100人班級(jí)被抽樣的概率不一樣,隨機(jī)抽樣會(huì)導(dǎo)致對(duì)人數(shù)較多的班級(jí)過(guò)度抽樣,使結(jié)果向人多的一方傾斜。
同理,在平均每隔10分鐘就有一班車到站臺(tái)的情況下,有時(shí)前后兩輛公交車的到達(dá)間隔會(huì)超過(guò)10分鐘,有時(shí)候不到10分鐘,如果你到站臺(tái)是個(gè)隨機(jī)時(shí)間點(diǎn),你就有更大概率會(huì)遇到超過(guò)10分鐘的情況。所以乘客的平均等待時(shí)間更長(zhǎng)是有道理的,因?yàn)檩^長(zhǎng)間隔被過(guò)度采樣了。
但等待時(shí)間悖論提出了一個(gè)更令人“匪夷所思”的結(jié)論:當(dāng)前后兩輛車的平均到站間隔是N分鐘時(shí),乘客體驗(yàn)到的公交車平均到站間隔是2N分鐘。這會(huì)是真的嗎?
模擬等待時(shí)間
為了證明等待時(shí)間悖論的結(jié)論是正確的,首先我們可以模擬一些公交車,它們的平均到站時(shí)間是10分鐘。已知樣本數(shù)量越大,結(jié)果越準(zhǔn)確,我們?cè)O(shè)一共有100萬(wàn)輛公交車。:
import numpy as np
N = 1000000# 公交車數(shù)量
tau = 10# 平均到站間隔
rand = np.random.RandomState(42) # 隨機(jī)種子
bus_arrival_times = N * tau * np.sort(rand.rand(N))
接著,我們檢查一下它們的平均到站間隔是否接近τ=10:
intervals = np.diff(bus_arrival_times)
intervals.mean()
輸出:9.9999879601518398
模擬好了公交車,之后是模擬大量在這個(gè)時(shí)間跨度內(nèi)到達(dá)公交站的乘客,并計(jì)算他們每個(gè)人的等待時(shí)間。如下所示,我們把它封裝進(jìn)一個(gè)函數(shù)以備后用:
def simulate_wait_times(arrival_times,
rseed=8675309, # Jenny的隨機(jī)種子
n_passengers=1000000):
rand = np.random.RandomState(rseed)
arrival_times = np.asarray(arrival_times)
passenger_times = arrival_times.max() * rand.rand(n_passengers)
# 為每個(gè)模擬乘客找到下一輛公交車
i = np.searchsorted(arrival_times, passenger_times, side='right')
return arrival_times[i] - passenger_times
然后我們可以模擬一些等待時(shí)間并計(jì)算平均值:
wait_times = simulate_wait_times(bus_arrival_times)
wait_times.mean()
輸出:10.001584206227317
正如等待時(shí)間悖論預(yù)測(cè)的那樣,乘客的平均等待時(shí)間也接近10分鐘。
深入挖掘:概率和泊松過(guò)程
所以上面的代碼到底是什么意思?
從本質(zhì)上看,等待時(shí)間悖論是檢驗(yàn)悖論的一個(gè)特例,觀察某個(gè)值的概率和這個(gè)值本身有關(guān)。讓我們用p(T)表示公交車到站時(shí)間間隔T的分布,這時(shí),對(duì)到達(dá)時(shí)間的期望值是:
在上面的例子中,我們已經(jīng)設(shè)E[T]=τ=10分鐘。
當(dāng)乘客在隨機(jī)時(shí)間點(diǎn)到達(dá)公交站時(shí),他們經(jīng)歷的等待時(shí)間的概率既會(huì)受p(T)影響,又會(huì)受T本身影響:汽車到達(dá)間隔越長(zhǎng),乘客遇到較長(zhǎng)等待時(shí)間的概率也會(huì)相應(yīng)變大。
所以我們可以寫(xiě)出乘客感受到的汽車到站間隔分布:
它們的比例常數(shù)是:
也就是:
已知乘客的期望等待時(shí)間E[W]是他們體驗(yàn)到的公交車到站間隔的一半,我們可以把它寫(xiě)成:
改寫(xiě)上式可得:
現(xiàn)在剩下的就是為p(T)選擇一個(gè)列表,并計(jì)算積分。
選擇p(T)
我們可以通過(guò)繪制公交車到站間隔直方圖來(lái)模擬p(T)的分布:
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('seaborn')
plt.hist(intervals, bins=np.arange(80), density=True)
plt.axvline(intervals.mean(), color='black', linestyle='dotted')
plt.xlabel('Interval between arrivals (minutes)')
plt.ylabel('Probability density');
圖中虛線表示平均到站時(shí)間為10分鐘??梢园l(fā)現(xiàn),上圖分布形狀很像指數(shù)分布,這并不是偶然:我們把公交車到站間隔模擬成均勻隨機(jī)數(shù)的做法十分類似泊松過(guò)程,而如果構(gòu)成泊松過(guò)程,到站間隔的分布一定符合指數(shù)分布。
注:在我們的例子里,到站間隔分布只是近似指數(shù)分布。
如果到站間隔符合指數(shù)分布,它就遵循泊松過(guò)程——為了驗(yàn)證這個(gè)推理,我們可以用另一個(gè)泊松過(guò)程的屬性來(lái)檢查:在固定時(shí)間范圍內(nèi),公交車到站次數(shù)的分布滿足泊松分布。我們可以在之前的模擬中查看每小時(shí)公交車的到站次數(shù):
from scipy.stats import poisson
# 計(jì)算1小時(shí)內(nèi)公交車到站次數(shù)
binsize = 60
binned_arrivals = np.bincount((bus_arrival_times // binsize).astype(int))
x = np.arange(20)
# 繪制結(jié)果
plt.hist(binned_arrivals, bins=x - 0.5, density=True, alpha=0.5, label='simulation')
plt.plot(x, poisson(binsize / tau).pmf(x), 'ok', label='Poisson prediction')
plt.xlabel('Number of arrivals per hour')
plt.ylabel('frequency')
plt.legend();
如上圖所示,模擬次數(shù)分布(方柱)和泊松分布(黑點(diǎn))幾乎一致?,F(xiàn)在理論、模擬實(shí)踐都支持這么一個(gè)事實(shí):對(duì)于一個(gè)足夠大的N,公交車的到站間隔可以用泊松過(guò)程描述,到站間隔分布滿足指數(shù)分布。
這意味著我們可以把概率分布寫(xiě)成:
把上式帶入之前的等式,可得每名乘客的平均等待時(shí)間是:
因此,如果公交車的到站間隔符合泊松過(guò)程,乘客的平均期望等待時(shí)間和公交車的平均到站間隔相同。
推斷這個(gè)結(jié)論的另一種補(bǔ)充方法是:泊松過(guò)程是一個(gè)無(wú)記憶過(guò)程,這意味事歷史事件與下一事件發(fā)生的預(yù)期時(shí)間無(wú)關(guān)。所以當(dāng)你到達(dá)公交站時(shí),你對(duì)下一班車的平均等待時(shí)間始終是一樣的:不管前一班車是什么時(shí)候來(lái)的,你平均都得等10分鐘。同理,無(wú)論你之前已經(jīng)等了多久,乘客對(duì)下一班車的預(yù)期等待時(shí)間還是10分鐘。
現(xiàn)實(shí)中的等待時(shí)間
那么泊松過(guò)程能描述現(xiàn)實(shí)生活中的公交車到站時(shí)間嗎?
為了探討等待時(shí)間悖論和現(xiàn)實(shí)情況是否存在矛盾,我們可以用一些數(shù)據(jù)進(jìn)行更深入的研究(arrival_times.csv,3MB CSV文件)。這個(gè)數(shù)據(jù)集包含2016年第二季度美國(guó)西雅圖3rd & Pike公交站的記錄,它一共有3條快速線:C、D和E,給出了每輛公交車的預(yù)定和實(shí)際到達(dá)時(shí)間。
import pandas as pd
df = pd.read_csv('arrival_times.csv')
df = df.dropna(axis=0, how='any')
df.head()
之所以選擇快速線,是因?yàn)樵谝惶斓拇蟛糠謺r(shí)間里,這幾路公交車的到站間隔都穩(wěn)定在10-15分鐘之間。
數(shù)據(jù)清理
首先,讓我們簡(jiǎn)單做一些數(shù)據(jù)清理,把數(shù)據(jù)集里的表格轉(zhuǎn)成更易于使用的形式:
# 把日期和時(shí)間組合成單個(gè)時(shí)間戳
df['scheduled'] = pd.to_datetime(df['OPD_DATE'] + ' ' + df['SCH_STOP_TM'])
df['actual'] = pd.to_datetime(df['OPD_DATE'] + ' ' + df['ACT_STOP_TM'])
# 如果公交車的預(yù)計(jì)到點(diǎn)和實(shí)際到點(diǎn)過(guò)了半夜,需要調(diào)整日期
minute = np.timedelta64(1, 'm')
hour = 60 * minute
diff_hrs = (df['actual'] - df['scheduled']) / hour
df.loc[diff_hrs > 20, 'actual'] -= 24 * hour
df.loc[diff_hrs < -20, 'actual'] += 24 * hour
df['minutes_late'] = (df['actual'] - df['scheduled']) / minute
# 內(nèi)外部路徑映射
df['route'] = df['RTE'].replace({673: 'C', 674: 'D', 675: 'E'}).astype('category')
df['direction'] = df['DIR'].replace({'N': 'northbound', 'S': 'southbound'}).astype('category')
# 抓取有用的列
df = df[['route', 'direction', 'scheduled', 'actual', 'minutes_late']].copy()
df.head()
公交車晚點(diǎn)情況
數(shù)據(jù)集中有6組不同數(shù)據(jù):南向行駛和北向行駛的3路公交車。為了感受它們的早到/遲到特點(diǎn),我們可以用實(shí)際到達(dá)時(shí)間減去預(yù)期到達(dá)時(shí)間,繪制6幅公交車“晚點(diǎn)”情況圖:
import seaborn as sns
g = sns.FacetGrid(df, row="direction", col="route")
g.map(plt.hist, "minutes_late", bins=np.arange(-10, 20))
g.set_titles('{col_name} {row_name}')
g.set_axis_labels('minutes late', 'number of buses');
事實(shí)上,很多人僅憑經(jīng)驗(yàn)就知道公交車在剛發(fā)車后的一段時(shí)間內(nèi)更不容易晚點(diǎn),公交站越靠后,車子晚點(diǎn)的可能性就越大,晚點(diǎn)時(shí)間也越長(zhǎng)。這一點(diǎn)在上圖中得到了證實(shí),南向行駛的C路公交車(圖四)、北向行駛的D路公交車(圖二)和北向行駛的E路公交車在剛開(kāi)出時(shí)還很準(zhǔn)時(shí),到最后卻出現(xiàn)了晚點(diǎn)超過(guò)十幾分鐘的情況。
預(yù)計(jì)到點(diǎn)和實(shí)際到點(diǎn)
接著,我們來(lái)看看這6條線路的實(shí)際到站間隔,這可以用Pandas的groupby函數(shù)計(jì)算:
def compute_headway(scheduled):
minute = np.timedelta64(1, 'm')
return scheduled.sort_values().diff() / minute
grouped = df.groupby(['route', 'direction'])
df['actual_interval'] = grouped['actual'].transform(compute_headway)
df['scheduled_interval'] = grouped['scheduled'].transform(compute_headway)
g = sns.FacetGrid(df.dropna(), row="direction", col="route")
g.map(plt.hist, "actual_interval", bins=np.arange(50) + 0.5)
g.set_titles('{col_name} {row_name}')
g.set_axis_labels('actual interval (minutes)', 'number of buses');
很明顯,上述分布和指數(shù)分布差距比較大,但它存在一個(gè)潛在影響因素,就是影響實(shí)際到站間隔的預(yù)期到站間隔可能本身就是不恒定的。
所以我們得再去看看預(yù)期到站間隔的情況:
g = sns.FacetGrid(df.dropna(), row="direction", col="route")
g.map(plt.hist, "scheduled_interval", bins=np.arange(20) - 0.5)
g.set_titles('{col_name} {row_name}')
g.set_axis_labels('scheduled interval (minutes)', 'frequency');
很顯然,預(yù)期到站間隔不是一個(gè)固定值,而且它的變化范圍還很大。所以在這個(gè)數(shù)據(jù)集里,我們沒(méi)法用實(shí)際到站間隔的分布來(lái)評(píng)估等待時(shí)間悖論是否準(zhǔn)確。
構(gòu)建同一時(shí)間表
雖然預(yù)期到站間隔不均勻,但它們中也存在一些常見(jiàn)的特定間隔,比如數(shù)據(jù)集中有近2000輛北向行駛的E路車的預(yù)期間隔是10分鐘。為了探究等待時(shí)間悖論是否,我們可以按公交路線、行駛方向和預(yù)期到站間隔對(duì)數(shù)據(jù)集進(jìn)行分類,篩選出相似的數(shù)據(jù)重新進(jìn)行堆疊分析,假設(shè)它們是連續(xù)發(fā)車的。
def stack_sequence(data):
# first, sort by scheduled time
data = data.sort_values('scheduled')
# re-stack data & recompute relevant quantities
data['scheduled'] = data['scheduled_interval'].cumsum()
data['actual'] = data['scheduled'] + data['minutes_late']
data['actual_interval'] = data['actual'].sort_values().diff()
return data
subset = df[df.scheduled_interval.isin([10, 12, 15])]
grouped = subset.groupby(['route', 'direction', 'scheduled_interval'])
sequenced = grouped.apply(stack_sequence).reset_index(drop=True)
sequenced.head()
利用這些清理過(guò)的數(shù)據(jù),我們可以繪制每個(gè)公交路線、行駛方向和到站頻率的公交車“實(shí)際”到站間隔分布:
for route in ['C', 'D', 'E']:
g = sns.FacetGrid(sequenced.query(f"route == '{route}'"),
row="direction", col="scheduled_interval")
g.map(plt.hist, "actual_interval", bins=np.arange(40) + 0.5)
g.set_titles('{row_name} ({col_name:.0f} min)')
g.set_axis_labels('actual interval (min)', 'count')
g.fig.set_size_inches(8, 4)
g.fig.suptitle(f'{route} line', y=1.05, fontsize=14)
如上圖所示,這三路公交車的到站間隔分布近似高斯分布:在預(yù)期到站間隔附近達(dá)到峰值,一開(kāi)始標(biāo)準(zhǔn)偏差較小,越往后越大。所以很顯然,這和等待時(shí)間悖論的基石——指數(shù)分布相違背。
我們?cè)儆蒙厦娴臄?shù)據(jù)計(jì)算每個(gè)公交路線、行駛方向和到站頻率的公交車的乘客平均等待時(shí)間:
grouped = sequenced.groupby(['route', 'direction', 'scheduled_interval'])
sims = grouped['actual'].apply(simulate_wait_times)
輸出:
route direction scheduled_interval
C northbound 10.0 7.8 +/- 12.5
12.0 7.4 +/- 5.7
15.0 8.8 +/- 6.4
southbound 10.0 6.2 +/- 6.3
12.0 6.8 +/- 5.2
15.0 8.4 +/- 7.3
D northbound 10.0 6.1 +/- 7.1
12.0 6.5 +/- 4.6
15.0 7.9 +/- 5.3
southbound 10.0 6.7 +/- 5.3
12.0 7.5 +/- 5.9
15.0 8.8 +/- 6.5
E northbound 10.0 5.5 +/- 3.7
12.0 6.5 +/- 4.3
15.0 7.9 +/- 4.9
southbound 10.0 6.8 +/- 5.6
12.0 7.3 +/- 5.2
15.0 8.7 +/- 6.0
Name: actual, dtype: object
平均等待時(shí)間可能比預(yù)期到站間隔的一半長(zhǎng)一兩分鐘,但不是等待時(shí)間悖論所暗示的結(jié)果。換句話說(shuō),這個(gè)結(jié)果證實(shí)了檢驗(yàn)悖論,而等待時(shí)間悖論似乎與現(xiàn)實(shí)不符。
最后的想法
等待時(shí)間悖論一直是一個(gè)有趣的論題,它涵蓋模擬、概率統(tǒng)計(jì)假設(shè)與現(xiàn)實(shí)的比較。雖然我們現(xiàn)在已經(jīng)確認(rèn)現(xiàn)實(shí)世界的公交線路確實(shí)遵循了一些檢驗(yàn)悖論,但上述分析也非常明確地表明等待時(shí)間悖論背后的核心假設(shè)——公交車到站間隔遵循泊松過(guò)程有很大問(wèn)題。
回想起來(lái),這可能并不令人驚訝:泊松過(guò)程是一個(gè)無(wú)記憶過(guò)程,它假設(shè)公交車到站概率完全獨(dú)立于自上次到站以來(lái)的時(shí)間。但在現(xiàn)實(shí)中,一個(gè)運(yùn)行良好的公交系統(tǒng)會(huì)設(shè)計(jì)合理的行車時(shí)間表,每輛公交車的出發(fā)時(shí)間都不是隨機(jī)的,它們要考慮乘客多少。
而由這個(gè)問(wèn)題引出的更大教訓(xùn),是我們應(yīng)該謹(jǐn)慎對(duì)待任何數(shù)據(jù)分析任務(wù)的假設(shè)。雖然泊松過(guò)程有時(shí)是對(duì)到站時(shí)間數(shù)據(jù)的良好描述,但我們不能僅僅因?yàn)橐环N類型的數(shù)據(jù)看起來(lái)和另一種類型的數(shù)據(jù)很像,就直接想當(dāng)然地認(rèn)為對(duì)這種數(shù)據(jù)有效的假設(shè)必然對(duì)另一種同樣有效??此普_的假設(shè)可能導(dǎo)致與現(xiàn)實(shí)不符的結(jié)論。
-
數(shù)據(jù)集
+關(guān)注
關(guān)注
4文章
1222瀏覽量
25253 -
數(shù)據(jù)可視化
+關(guān)注
關(guān)注
0文章
475瀏覽量
10692
原文標(biāo)題:等待時(shí)間悖論:為什么我的公交車總是遲到?
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
深入淺出matlab

深入淺出Android—Android開(kāi)發(fā)經(jīng)典教材

評(píng)論