Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同

1. 首先介绍Boxplot(箱形图)的定义,这里参考:Understanding Boxplots,非常精彩的一篇介绍boxplot的博文。

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同

该图片显示的即是一个boxplot的基本组成部分, 它由5个基本数值决定,即 最小值 (minimum); 下四分位数 (first quartile, Q1); 中值或中位数 (median), 或第二个四分位数 (second quartile, Q2); 上四分位数 (third quartile, Q3); 最大值 (maximum)。 

其它定义:四分位间距 (interquartile range, IQR), 表示下四分位数Q1和上四分位数Q3的间距; 盒须 (wiskers), 上图中蓝色的延伸线,长度为1.5*IQR, 分为两段,分别是Q1延伸至minimum, Q3延伸至maximum; 离群值 (outliers), 上图中绿色的点,表示小于minimum的值和大于maximum的值。

这里需要注意,boxplot是用于显示数据分散情况的图,其中的minimum和maximum很可能不表示数据集中的最小、最大点,而是由Q1、Q3、IQR决定, 而不分布于[minimum, maximum]的点视为outlier.



2. 接下来介绍三个四分位数Q1、Q2和Q3的具体取值情况,这里部分参考:Wikipedia: Quartile  维基百科。

Q2,即median的取值比较简单,设n表示数据集中点的数目a为该数据集的升序数组.

这里分为两种情况:

  • n = 2 * i + 1,  即数据点个数为奇数 (odd number), median (Q2) = a[i]
  • n = 2 * i, 即数据点个数为偶数 (even number), median (Q2) = a[i-1] * 0.5 + a[i] * 0.5

Q1和Q3的取值,根据Wikipedia: Quartile上的解释,还没有通用的取值。

上面介绍了3种主要的取值方法,需要先将点的数量n分为4种情况:

  • n = 4 * i, 即数据点个数为偶数,将其平分后,每一份的个数仍是偶数;
  • n = 4 * i + 2, 即数据点个数为偶数,  将其平分后,每一份的个数为奇数;
  • n = 4 * i + 1, 即数据点个数为奇数,先将其去除中值Q2, 再平分后,每一份的个数为偶数;
  • n = 4 * i +3, 即数据点个数为奇数,先将其去除中值Q2, 再平分后,每一份的个数为奇数。

3种方法对于Q1和Q3的具体取值为:

当n为偶数时,3种方法对于Q1和Q3的取值均相同,即先将a等分为下半 (lower half) 和上半 (upper half)(不去除用于计算中值的两个数),等分之后的数组再分别计算中值,Q1为下半的中值,Q3为上半的中值。

若 n = 4 * i, Q1 = a[i-1] * 0.5 + a[i] * 0.5; Q3 = a[3*i-1] * 0.5 + a[3*i] * 0.5

若 n = 4 * i + 2, Q1 = a[i]; Q3 = a[3*i+1]

3种方法对于Q1和Q3的取值不同体现在当n为奇数时:


Method 1:对其等分为下半和上半时,两个数组均不包含中间的那个中值,Q1为下半的中值,Q3为上半的中值:

若n = 4 * i + 1, Q1 = a[i-1] * 0.5 + a[i] * 0.5; Q3 = a[3*i] * 0.5 + a[3*i+1] * 0.5

若n = 4 * i + 3, Q1 = a[i]; Q3 = a[3*i+2]



Method 2: 对其等分为下半和上半时,两个数组均包含中间的那个中值,Q1为下半的中值,Q3为上半的中值: 

 若n = 4 * i + 1, Q1 = a[i]; Q3 = a[3*i]

 若n = 4 * i + 3, Q1 = a[i] * 0.5 + a[i+1] * 0.5; Q3 = a[3*i+1] * 0.5 + a[3*i+2] * 0.5

 



Method 3: 对其等分为下半和上半时,两种情况下,一种两个数组均包含中间的那个均值,另一种两个数组均不包含中间的那个中值,使两个半数组中数值个数为偶数,Q1、Q3分别为下半和上半中间值的权值,不是中值。

若n = 4 * i + 1, 两个数组不包含中间那个中值,此时method 3和method 1相似,不过权值分别为0.25和0.75,不是0.5和0.5

Q1 = a[i-1] * 0.25 + a[i] * 0.75; Q3 = a[3*i] * 0.75 + a[3*i+1] * 0.25

若n = 4 * i +3, 两个数组包含中间的那个中值,此时method 3和method 2相似,不过权值分别为0.25和0.75, 不是0.5和0.5

Q1 = a[i] * 0.75 + a[i+1] * 0.25; Q3 = a[3*i+1] * 0.25 + a[3*i+2] * 0.75


示例:

示例1: n = 8 (2*4), a = [6, 7, 15, 36, 39, 40, 41, 42]

Example 1: n=8
  Method 1 Method 2 Method 3
Q1 11 11 11
Q2 37.5 37.5 37.5
Q3 40.5 40.5 40.5

 

示例2: n = 10 (2*4+2), a = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47]

 

Example 2: n=10
  Method 1 Method 2 Method 3
Q1 15 15 15
Q2 39.5 39.5 39.5
Q3 42 42 42

 

示例3: n = 9 (2*4+1), a = [6, 7, 15, 36, 39, 40, 41, 42, 43]

Example 3: n = 9
  Method 1 Method 2 Method 3
Q1 11 15 13
Q2 39 39 39
Q3 41.5 41 41.25

 

示例4: n = 11 (2*4+3), a = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]

Example 4: n = 11
  Method 1 Method 2 Method 3
Q1 15 25.5 9
Q2 40 40 40
Q3 43 42.5 42.75

 



  3.  Python 中matplotlib.pyplot中包含的boxplot()函数可直接用于画boxplot箱形图。

定义,及参数的官方文档参考:matplotlib.pyplot.boxplot

不过,如第二节介绍Q1、Q2和Q3取值时说明,目前对于Q1和Q3,没有通用的取值标准,这里我用实际的示例进行实验,发现其对于Q1和Q3的取值,和上述3种方法均不同。 

假设定义如上,n表示数据点的数目,a表示升序数组:


若n = 4 * i,  Q1 = a[i-1] * 0.25 + a[i] * 0.75;  Q3 = a[3*i-1] * 0.75 + a[3*i] * 0.25

此时,Q1和Q3和3种Method的取的元素相同,但权值不是0.5和0.5。

示例:

import numpy as np
import matplotlib.pyplot as plt

#A2 = [6, 7, 15, 36, 39, 40, 41, 42]
A2 = [1, 2, 3, 4, 5, 6, 7, 8]
#n = 8

A2_sort = sorted(A2)

plt.boxplot(A2, whis = 1.5000)

median = np.median(A2)

plt.plot([1, 2], [median, median], lw = 0.40)

plt.plot([0, 1], [A2_sort[1] * 0.25 + A2_sort[2] * 0.75, A2_sort[1] * 0.25 + A2_sort[2] * 0.75], lw = 0.80, linestyle= ':')

plt.plot([0, 1], [A2_sort[5] * 0.75 + A2_sort[6] * 0.25, A2_sort[5] * 0.75 + A2_sort[6] * 0.25], lw = 0.80, linestyle= ':')

plt.plot([1, 2], [A2_sort[1] * 0.50 + A2_sort[2] * 0.50, A2_sort[1] * 0.50 + A2_sort[2] * 0.50], lw = 0.75, linestyle= '-')

plt.plot([1, 2], [A2_sort[5] * 0.50 + A2_sort[6] * 0.50, A2_sort[5] * 0.50 + A2_sort[6] * 0.50], lw = 0.75, linestyle= '-')

#plt.grid(True, linestyle = '--')

#plt.savefig('anita.png', dpi = 600, bbox_inches = 'tight')

plt.show()

运行结果:

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同 

明显,左边由自己实验验证的权值,即虚线求出的Q1和Q3和真正由boxplot()画出的Q1和Q3是吻合的 ,和上述3种方法的在n为偶数的相同情况,即右边实线画出的Q1和Q3和boxplot()画出的不同。



 

若 n = 4 * i + 2 , Q1 = a[i] * 0.75 + a[i+1] * 0.25; Q3 = a[3*i] * 0.25 + a[3*i+1] * 0.75

示例:

import numpy as np
import matplotlib.pyplot as plt

A2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#n = 10

A2_sort = sorted(A2)

plt.boxplot(A2, whis = 1.5000)

median = np.median(A2)

plt.plot([1, 2], [median, median], lw = 0.40)

plt.plot([0, 1], [A2_sort[2] * 0.75 + A2_sort[3] * 0.25, A2_sort[2] * 0.75 + A2_sort[3] * 0.25], lw = 0.80, linestyle= ':')

plt.plot([0, 1], [A2_sort[6] * 0.25 + A2_sort[7] * 0.75, A2_sort[6] * 0.25 + A2_sort[7] * 0.75], lw = 0.80, linestyle= ':')

plt.plot([1, 2], [A2_sort[2], A2_sort[2]], lw = 0.75, linestyle= '-')

plt.plot([1, 2], [A2_sort[7], A2_sort[7]], lw = 0.75, linestyle= '-')

#plt.grid(True, linestyle = '-.')

#plt.savefig('Bob.png', dpi = 600, bbox_inches = 'tight')

plt.show()

运行结果:

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同 

左边由虚线画出的线,即自己实验验证的权值,和boxplot()画出的Q1和Q3吻合;而右边,上述3种方法画出的实线,和实际结果有偏差。



 

若 n = 4 * i + 1, Q1 = a[i]; Q3 = a[3*i]

此时的Q1和Q3取值和Method 2相同。

示例:

import numpy as np
import matplotlib.pyplot as plt

A2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
#n = 9

A2_sort = sorted(A2)

plt.boxplot(A2, whis = 1.5000)

median = np.median(A2)

plt.plot([1, 2], [median, median], lw = 0.40)

plt.plot([0, 1], [A2_sort[2], A2_sort[2]], lw = 0.80, linestyle = ':')

plt.plot([0, 1], [A2_sort[6], A2_sort[6]], lw = 0.80, linestyle = ':')

plt.plot([1, 2], [A2_sort[1] * 0.50 + A2_sort[2] * 0.50, A2_sort[1] * 0.50 + A2_sort[2] * 0.50], lw = 0.75, linestyle= '-', color='r') #Method 1

plt.plot([1, 2], [A2_sort[6] * 0.50 + A2_sort[7] * 0.50, A2_sort[6] * 0.50 + A2_sort[7] * 0.50], lw = 0.75, linestyle= '-', color = 'r')

plt.plot([1, 2], [A2_sort[2], A2_sort[2]], lw = 0.75, linestyle= '-', color = 'k') #Method 2

plt.plot([1, 2], [A2_sort[6], A2_sort[6]], lw = 0.75, linestyle= '-', color = 'k')

plt.plot([1, 2], [A2_sort[1] * 0.25 + A2_sort[2] * 0.75, A2_sort[1] * 0.25 + A2_sort[2] * 0.75], lw = 0.75, linestyle= '-', color = 'b') #Method 3

plt.plot([1, 2], [A2_sort[6] * 0.75 + A2_sort[7] * 0.25, A2_sort[6] * 0.75 + A2_sort[7] * 0.25], lw = 0.75, linestyle= '-', color = 'b')

#plt.grid(True, linestyle = '--')

#plt.savefig('claire.png', dpi = 600, bbox_inches = 'tight')

plt.show()

运行结果:

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同 

右边画出的实线,红、黑蓝分别是Method 1、Method 2、Method 3画出的Q1和Q3的值;左边虚线是自己实验验证的结果, 由对比可知,黑色的实线以及虚线,和boxplot()画出的Q1和Q3吻合,此时Q1和Q3的取值是由Method 2决定的。



 

若 n = 4 * i +3,  Q1 = a[i] * 0.5 + a[i+1] * 0.5; Q3 = a[3*i+1] * 0.5 + a[3*i+2] * 0.5 

此时的Q1和Q3取值和Method 2相同。

示例:

import numpy as np
import matplotlib.pyplot as plt

A2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
#n = 11

A2_sort = sorted(A2)

plt.boxplot(A2, whis = 1.5000)

median = np.median(A2)

plt.plot([1, 2], [median, median], lw = 0.40)

plt.plot([0, 1], [A2_sort[2] * 0.50 + A2_sort[3] * 0.50, A2_sort[2] * 0.50 + A2_sort[3] * 0.50], lw = 0.80, linestyle= ':')

plt.plot([0, 1], [A2_sort[7] * 0.50 + A2_sort[8] * 0.50, A2_sort[7] * 0.50 + A2_sort[8] * 0.50], lw = 0.80, linestyle= ':')

plt.plot([1, 2], [A2_sort[2], A2_sort[2]], lw = 0.75, linestyle= '-', color = 'r') #Method 1

plt.plot([1, 2], [A2_sort[8], A2_sort[8]], lw = 0.75, linestyle= '-', color = 'r')

plt.plot([1, 2], [A2_sort[2] * 0.50 + A2_sort[3] * 0.50, A2_sort[2] * 0.50 + A2_sort[3] * 0.50], lw = 0.75, linestyle= '-', color='k') #Method 1

plt.plot([1, 2], [A2_sort[7] * 0.50 + A2_sort[8] * 0.50, A2_sort[7] * 0.50 + A2_sort[8] * 0.50], lw = 0.75, linestyle= '-', color = 'k')


plt.plot([1, 2], [A2_sort[2] * 0.75 + A2_sort[3] * 0.25, A2_sort[2] * 0.75 + A2_sort[3] * 0.25], lw = 0.75, linestyle= '-', color = 'b') #Method 3

plt.plot([1, 2], [A2_sort[7] * 0.25 + A2_sort[8] * 0.75, A2_sort[7] * 0.25 + A2_sort[8] * 0.75], lw = 0.75, linestyle= '-', color = 'b')

#plt.grid(True, linestyle = '--')

#plt.savefig('david.png', dpi = 600, bbox_inches = 'tight')

plt.show()

运行结果:

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同

右边画出的实线,红、黑蓝分别是Method 1、Method 2、Method 3画出的Q1和Q3的值;左边虚线是自己实验验证的结果, 由对比可知,黑色的实线以及虚线,和boxplot()画出的Q1和Q3吻合,此时Q1和Q3的取值是由Method 2决定的。


4. 对matplotlib.pyplot中boxplot()总结:

n的数量 Q1 Q3 和3种Method关系
4 * i

a[i-1] * 0.25 +

a[i] * 0.75

a[3*i-1] * 0.75 +

a[3*i] * 0.25

无,权值改变
4 * i + 2

a[i] * 0.75 +

a[i+1] * 0.25

a[3*i] * 0.25 +

a[3*i+1] * 0.75

无,权值改变
4* i +1 a[i] a[3*i] Method 2
4 * i +3

a[i] * 0.5 +

a[i+1] * 0.5

a[3*i+1] * 0.5 +

a[3*i+2] * 0.5

Method 2

 

5. 示例:

这里用的示例是基于官方文档给出的Boxplot Demo

n = 100, 50个数为(0, 100)的随机分布,30个数为(0,50)的随机分布,10个数为(0, 200)的随机分布,10个数为(-100, 0)的随机分布,画出其箱形图。

# n较大的示例

import numpy as np
import matplotlib.pyplot as plt
import time

np.random.seed(int(time.time()))

spread = np.random.rand(50) * 100
center = np.random.rand(30) * 50
filter_high = np.random.rand(10) * 100 + 100
filter_low = np.random.rand(10) * 100 - 100

data = np.concatenate((spread, center, filter_high, filter_low))

fig1, ax1 = plt.subplots()
ax1.set_title('Box Plot Demo')

plt.ylim(-101, 201)

ax1.boxplot(data, showfliers = True, whis = 1.50000)

####Verifing the data 

data_sort = sorted(data)

Q1 = data_sort[24] * 0.25 + data_sort[25] * 0.75
Q3 = data_sort[74] * 0.75 + data_sort[75] * 0.25

IQR = Q3 - Q1

Minimum = Q1 - 1.50000 * IQR
Maximum = Q3 + 1.5000 * IQR

median = np.median(data)

outlier = np.array([data_sort[0], data_sort[-1]])

for i in range(len(data)):           # 求出minimum, 即大于等于Q1-1.5*IQR最小的数值;
    if(data_sort[i] >= Minimum ):
        minimum = data_sort[i]
        break
for i in range(len(data)):           # 求出maximum, 即小于等于Q3+1.5*IQR最大的数值。
    if(data_sort[::-1][i] <= Maximum):
        maximum_num = i
        maximum = data_sort[::-1][i]
        break

        
plt.plot([0, 1], [median, median], lw = 0.50)

plt.plot([0, 1], [Q1, Q1], lw = 0.80, linestyle = ':')
plt.plot([0, 1], [Q3, Q3], lw = 0.80, linestyle = ':')

Q1_M = data_sort[24] * 0.50 + data_sort[25] * 0.50 # value adopted in 3 methods
Q3_M = data_sort[74] * 0.50 + data_sort[75] * 0.50
plt.plot([1, 2], [Q1_M, Q1_M], lw = 0.75, linestyle = '-')
plt.plot([1, 2], [Q3_M, Q3_M], lw = 0.75, linestyle = '-')

plt.plot([0, 2], [minimum, minimum], lw = 0.50)
plt.plot([0, 2], [maximum, maximum], lw = 0.50)

#plt.grid(True, linestyle = '-.')
#plt.savefig("plot_100.png", dpi = 600, bbox_inches = 'tight')

plt.show()

一次的运行结果:

Box plot (箱形图) 中 quartile (四分位数)原理,及python_matplotlib中Q1和Q3定义的不同 

需要注意,由于这样的随机取值下,权值是(0.5, 0.5)(右实线)或(0.25, 0.75)(左虚线)相差不大, 但如果采用错误的权值,有可能造成求minimum和maximum错误。