绘制图表的重要性——基于Python从安斯库姆四重奏到等候排队理论的研究

Anscombe's quartert, variability and studying queues with Python - 从安斯库姆四重奏到等候排队理论的研究

Anscombe’s quartet - 安斯库姆四重奏 是四组基本的统计特性一致的数据,但它们的分布却非常不同

性质 数值
x平均数 9
x方差 11
y的平均数 7.50(精确到小数点后两位)
y的方差 4.122或4.127(精确到小数点后三位)
xy之间的相关系数 0.816(精确到小数点后三位)
线性回归线 y=3.00+0.500x(分别精确到小数点后两位和三位)

让我们使用Python和seaborn库、Ciw库 etc. 来探索一下吧


引入库

import pandas as pd  # Data manipulation
import ciw  # The discrete event simulation library we will use to study queues
import matplotlib.pyplot as plt  # Plots
import seaborn as sns  # Powerful plots
from scipy import stats  # Linear regression
import numpy as np  # Quick summary statistics
import tqdm  # A progress bar

缺少相应库使用pip install即可

加载Anscombe数据集
anscombe = sns.load_dataset("anscombe")

我们得到四组数据集,每组由11个(x, y)点构成:

   dataset     x      y
0        I  10.0   8.04
1        I   8.0   6.95
2        I  13.0   7.58
3        I   9.0   8.81
4        I  11.0   8.33
5        I  14.0   9.96
6        I   6.0   7.24
7        I   4.0   4.26
8        I  12.0  10.84
9        I   7.0   4.82
10       I   5.0   5.68
11      II  10.0   9.14
12      II   8.0   8.14
13      II  13.0   8.74
14      II   9.0   8.77
15      II  11.0   9.26
16      II  14.0   8.10
17      II   6.0   6.13
18      II   4.0   3.10
19      II  12.0   9.13
20      II   7.0   7.26
21      II   5.0   4.74
22     III  10.0   7.46
23     III   8.0   6.77
24     III  13.0  12.74
25     III   9.0   7.11
26     III  11.0   7.81
27     III  14.0   8.84
28     III   6.0   6.08
29     III   4.0   5.39
30     III  12.0   8.15
31     III   7.0   6.42
32     III   5.0   5.73
33      IV   8.0   6.58
34      IV   8.0   5.76
35      IV   8.0   7.71
36      IV   8.0   8.84
37      IV   8.0   8.47
38      IV   8.0   7.04
39      IV   8.0   5.25
40      IV  19.0  12.50
41      IV   8.0   5.56
42      IV   8.0   7.91
43      IV   8.0   6.89
查看统计数据
anscombe.groupby("dataset").describe()

得到非常相近的结果

                       x          y
dataset                            
I       count  11.000000  11.000000
        mean    9.000000   7.500909
        std     3.316625   2.031568
        min     4.000000   4.260000
        25%     6.500000   6.315000
        50%     9.000000   7.580000
        75%    11.500000   8.570000
        max    14.000000  10.840000
II      count  11.000000  11.000000
        mean    9.000000   7.500909
        std     3.316625   2.031657
        min     4.000000   3.100000
        25%     6.500000   6.695000
        50%     9.000000   8.140000
        75%    11.500000   8.950000
        max    14.000000   9.260000
III     count  11.000000  11.000000
        mean    9.000000   7.500000
        std     3.316625   2.030424
        min     4.000000   5.390000
        25%     6.500000   6.250000
        50%     9.000000   7.110000
        75%    11.500000   7.980000
        max    14.000000  12.740000
IV      count  11.000000  11.000000
        mean    9.000000   7.500909
        std     3.316625   2.030579
        min     8.000000   5.250000
        25%     8.000000   6.170000
        50%     8.000000   7.040000
        75%     8.000000   8.190000
        max    19.000000  12.500000
计算回归曲线
for data_set in anscombe.dataset.unique():  # array(['I', 'II', 'III', 'IV'], dtype=object)
    df = anscombe.query("dataset == '{}'".format(data_set))
    slope, intercept, r_val, p_val, slope_std_error = stats.linregress(x=df.x, y=df.y)
    sns.lmplot(x="x", y="y", data=df);
    plt.title("Data set {}: y={:.2f}x+{:.2f} (p: {:.2f}, R^2: {:.2f})".format(data_set, slope, intercept, p_val, r_val))

虽然数据分布十分不一样,但是它们的线性回归却惊人的一致。





分析

上面的事实经常被用来证明一个数据集的统计信息会固有地丢失数据信息,因此应该伴随着进一步的研究/理解(例如在这种情况下:查看数据的图)。
参考: Generating Data with Identical Statistics but Dissimilar Graphics: A Follow up to the Anscombe Dataset

引申

Queueing theory - 等候理論中,理解类似上文的变异性是非常必要的。
我之前的一篇博文calculating the expected wait in a tandem queue(计算串联队列中的预期等待)讲解了队列理论。其中,主要想法是假定在排队系统中个体到达的间隔时间的某一(随机)分布和为服务每个个体所花费的时间的另一(随机)分布。
在分析上这些会被当作Markov chain - 马尔可夫链研究。单个服务器队列的维基百科条目展示了相应链的图片。
队列的马尔科夫模型假定了(到达时间间隔和服务时间的)分布是马尔可夫的:即指数分布。这通常是一个很好地模拟现实情况的分布,它也常是不现实的。例如在医疗系统中,诸如医院病房(就是队列),服务时间分布通常遵循Log-normal distribution - 对数正态分布
当学习队列以及建立分析模型时,可以使用伪随机数发生器来模拟过程。一个用于这个研究的小软件就是ciw(主要开发者是Geraint Palmer-phd在读,Paul Harper和我)。
下面将使用ciw模拟一个单个服务器队列的模型,其中个体到达平均速率为0.5每时间单位(即到达之间有2个时间单位),并且平均以每时间单位1的速率提供服务(即每次服务为1个时间单位)。
我们需要在ciw中构建一个参数字典。Ciw被编写为便于处理大多数排队系统,但是特别考虑到排队网络,也考虑到了处理多个类别的个体的能力。在下面的系统中,我们只有一个个体类Class 0,转换矩阵会显得有点冗余,因为我们只处理单个队列。这里是我们的系统在假设指数分布时的字典:

parameters = {'Arrival_distributions': {'Class 0': [['Exponential', 0.5]]},
              'Service_distributions': {'Class 0': [['Exponential', 1]]},
              'Transition_matrices': {'Class 0': [[0.0]]},
              'Number_of_servers': [1]}

这里是运行单个模拟的函数:

def iteration(parameters, maxtime=250, warmup=50):
    """
    Run a single iteration of the simulation and
    return the times spent waiting for service
    as well as the service times
    """
    N = ciw.create_network(parameters)
    Q = ciw.Simulation(N)
    Q.simulate_until_max_time(maxtime)
    records = [r for r in  Q.get_all_records() if r.arrival_date > warmup]
    waits = [r.waiting_time for r in records]
    service_times = [r.service_time for r in records]
    n = len(waits)
    return waits, service_times

下面是多次运行模拟组合成的一个实验函数,它对于帮助平滑随机过程的随机性是很重要的。

def trials(parameters, repetitions=30, maxtime=2000, warmup=250):
    """Repeat out simulation over a number of trials"""
    waits = []
    service_times = []
    for seed in tqdm.trange(repetitions):  # tqdm gives us a nice progress bar
        ciw.seed(seed)
        wait, service_time = iteration(parameters, maxtime=maxtime, warmup=warmup)
        waits.extend(wait)
        service_times.extend(service_time)
    return waits, service_times

让我们以默认参数运行实验:

waits, service_times = trials(parameters)

首先,让我们验证预期的服务时间(回想下应该是1个时间单位):

np.mean(service_times)
# 输出 1.0024931747410142

我们还可以检查平均等待是否和理论值一致,理论值为ρ/[μ(1−ρ)],其中ρ=λ/μ,with λ,μ being the interarrival and service rates

lmbda = parameters['Arrival_distributions']['Class 0'][0][1]
mu = parameters['Service_distributions']['Class 0'][0][1]
rho = lmbda / mu
np.mean(waits), rho / (mu * (1 - rho))
# 输出 (1.0299693832838621, 1.0)

既然我们已经实现了一个简单的马尔科夫系统,让我们创建一些具有相同平均服务时间的分布并比较它们:

distributions = [
    ['Uniform', 0, 2],  # A uniform distribution with mean 1
    ['Deterministic', 1],  # A deterministic distribution with mean 1
    ['Triangular', 0, 2, 1],  # A triangular distribution with mean 1
    ['Exponential', 1],  # The Markovian distribution with mean 1
    ['Gamma', 0.91, 1.1], # A Gamma distribution with mean 1
    ['Lognormal', 0, .1], # A lognormal distribution with mean 1
    ['Weibull', 1.1, 3.9],  # A Weibull distribuion with mean 1
    ['Empirical', [0] * 19 + [20]]  # An empirical distribution with mean 1 (95% of the time: 0, 5% of the time: 20)
   ]

(更多信息从ciw关于分布的文档中获得)

利用trials函数模拟上述所有分布的排队过程:

columns = ["distribution", "waits", "service_times"]
df = pd.DataFrame(columns=columns)  # Create a dataframe that will keep all the data
for distribution in distributions:
    parameters['Service_distributions']['Class 0'] = [distribution]
    waits, service_times = trials(parameters)
    n = len(waits)
    df = df.append(pd.DataFrame(list(zip([distribution[0]] * n, waits, service_times)), columns=columns))

分析下面的统计数据,我们看到所有分布具有相同的平均服务时间,平均等待时间却截然不同:

bydistribution = df.groupby("distribution")  # Grouping the data
for name, df in sorted(bydistribution, key= lambda dist: dist[1].waits.max()):
    print("{}:\n\t Mean service time: {:.02f}\n\t Mean waiting time: {:.02f}\n\t 95% waiting time: {:.02f} \n\t Max waiting time: {:.02f}\n".format(
                                                        name,
                                                        df.service_times.mean(),
                                                        df.waits.mean(),
                                                        df.waits.quantile(0.95),
                                                        df.waits.max()))

输出统计数据

Deterministic:
         Mean service time: 1.00
         Mean waiting time: 0.51
         95% waiting time: 2.11 
         Max waiting time: 6.73

Weibull:
         Mean service time: 1.00
         Mean waiting time: 0.54
         95% waiting time: 2.24 
         Max waiting time: 8.16

Lognormal:
         Mean service time: 1.01
         Mean waiting time: 0.51
         95% waiting time: 2.09 
         Max waiting time: 8.53

Triangular:
         Mean service time: 1.00
         Mean waiting time: 0.59
         95% waiting time: 2.48 
         Max waiting time: 9.21

Uniform:
         Mean service time: 1.00
         Mean waiting time: 0.67
         95% waiting time: 2.84 
         Max waiting time: 10.42

Exponential:
         Mean service time: 1.00
         Mean waiting time: 1.03
         95% waiting time: 4.76 
         Max waiting time: 16.41

Gamma:
         Mean service time: 1.00
         Mean waiting time: 1.02
         95% waiting time: 4.72 
         Max waiting time: 20.65

Empirical:
         Mean service time: 0.99
         Mean waiting time: 9.30
         95% waiting time: 37.60 
         Max waiting time: 100.60

例如,经验分布中至少有一个个体的等待时间超过了100个时间单位!(要记得,平均每2个时间单位到达一个个体,并且个体平均被服务1个时间单位!)还有5%的个体在经验分布中等待了超过37个时间单位,以及更保守的分布中5%的个体等待时间超过:4.72个时间单位(Gamma),4.76个时间单位(Exponential),2.84时间单位(Uniform) ...
但是正如我们一开始提到的,就像看安斯库姆四重奏那样,重要的是不要仅看统计信息,所以让我们看图:

for name, df in sorted(bydistribution, key= lambda dist: dist[1].waits.max())[:-1]:
    plt.hist(df.waits, normed=True, bins=20, cumulative=True,
             histtype = 'step', label=name, linewidth=1.5)
plt.title("Cdf (excluding the empirical distribution)")
plt.xlabel("Waiting time")
plt.ylabel("Probability")
plt.ylim(0, 1)
plt.legend(loc=5)
for name, df in sorted(bydistribution, key= lambda dist: dist[1].waits.max()):
    plt.hist(df.waits, normed=True, bins=20, cumulative=True,
             histtype = 'step', label=name, linewidth=1.5)
plt.title("Cdf")
plt.xlabel("Waiting time")
plt.ylabel("Probability")
plt.ylim(0, 1)
plt.legend(loc=5)

我们看到,经验分布到目前为止是尾巴最长的,一些个体的等待时间超过了80个时间单位。虽然这种分布是相当人为的,但是假设某一个特定过程或服务几乎不需要多少时间而在很少的情况下却会花费很多的时间,这是相当现实的。这些例子包括了有时需要非常规程序的医疗保健操作。
所有这一切都是模拟一个相当简单的排队系统(单服务器)但是有了Ciw,研究更复杂的系统也变得简单。

此外

Ciw已经相当完善,并且100%测试覆盖,但是它仍需要你的贡献(任何级别的),如果有意,请看:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容