1

使用四分位数boxplotmatplotlib.pyplot值是通过包括中位数来计算的。可以将其更改为不包括中位数吗?

例如,考虑有序数据集

2、3、4、5、6、7、8

如果不包括中位数,则 Q1=3 和 Q3=7。但是,boxplot包括中间值,即5,并生成下图

箱形图

是否可以改变这种行为,并且在四分位数的计算中不包括中位数?这应该对应于维基百科页面Quartile上描述的方法 1 。下面列出了生成图形的代码

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator

data = [2, 3, 4,  5,    6, 7, 8]

fig = plt.figure(figsize=(6,1))
ax = fig.add_axes([0.1,0.25,0.8,0.8])
bp = ax.boxplot(data, '', 
                vert=False,
                positions=[0.4],
                widths=[0.3])

ax.set_xlim([0,9])
ax.set_ylim([0,1])

ax.xaxis.set_major_locator(MultipleLocator(1))

ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["top"].set_visible(False)

ax.yaxis.set_ticks([])

ax.grid(which='major',axis='x',lw=0.1)

plt.show()
4

1 回答 1

1

这个问题的动机是互联网上的一些教育资源没有将四分位数计算为 matplotlib 箱线图使用的默认设置。例如,在可汗学院的在线课程“统计和概率”中,四分位数的计算方法如维基百科页面四分位数中的方法 1 所述,而箱线图采用方法 2。

考虑可汗学院课程“统计和概率”部分“比较范围和四分位距 (IQR)”中的一个示例。每日高温记录在密歇根州的天堂。7天,发现是16、24、26、26、26、27和28摄氏度。用箱线图描述数据并计算 IQR。

使用boxplot默认设置的结果和Khan教授给出的结果有很大的不同,见下图。

根据方法 1 和 2 计算的四分位数箱线图

matplotlib找到的IQR是1.5,Khan教授计算的是3。正如@JohanC评论中指出的,boxplot不能直接配置为遵循方法1,而是需要自定义函数。因此,忽略了异常值的计算,我更新了代码,按照方法1计算四分位数,从而与可汗学院的课程相媲美。下面列出了代码,不是很pythonic,欢迎提出建议。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
from matplotlib.ticker import MultipleLocator


def median(x):
    """
    x - input a list of numbers
    Returns the midpoint number, for example
    in a list with oddnumbers 
    [1,2, 3, 4,5] returns 3
    for a list with even numbers the algebraic mean is returned, e.g
    [1,2,3,4]   returns 2.5
    """
    if len(x)&1:
        # Odd number of elements in list, e.g. x = [1,2,3] returns 2
        index_middle = int((len(x)-1)/2)
        median = x[index_middle]
    else:
        # Even number of elements in list, e.g. x = [-1,2] returns 0.5
        index_lower = int(len(x)/2-1)
        index_upper = int(len(x)/2)
        median = (x[index_lower]+x[index_upper])/2

    return median


def method_1_quartiles(x):
    """
    x - list of numbers
    """
    x.sort()
    N = len(x)
    if N&1:
        # Odd number of elements
        index_middle = int((N-1)/2)
        lower = x[0:index_middle] # Up to but not including
        upper = x[index_middle+1:N+1]
        Q1= median(lower)
        Q2 = x[index_middle]
        Q3 = median(upper)
    else:
        # Even number of elements
        index_lower = int(N/2)
        lower = x[0:index_lower]
        upper = x[index_lower:N]

        Q1= median(lower)
        Q2 = (x[index_lower-1]+x[index_lower])/2
        Q3 = median(upper)
    
    return Q1,Q2,Q3


data = [16,24,26,   26,   26,27,28]

fig = plt.figure(figsize=(6,1))
ax = fig.add_axes([0.1,0.25,0.8,0.8])



stats = cbook.boxplot_stats(data,)[0]

Q1_default = stats['q1']
Q3_default = stats['q3']

stats['whislo']=min(data)
stats['whishi']=max(data)

IQR_default = Q3_default - Q1_default  

Q1, Q2, Q3 = method_1_quartiles(data)
IQR = Q3-Q1
stats['q1'] = Q1
stats['q3'] = Q3
print(f"IQR: {IQR}")



ax.bxp([stats],vert=False,manage_ticks=False,widths=[0.3],positions=[0.4],showfliers=False)

ax.set_xlim([15,30])
ax.set_ylim([0,1])

ax.xaxis.set_major_locator(MultipleLocator(1))

ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["top"].set_visible(False)

ax.yaxis.set_ticks([])

ax.grid(which='major',axis='x',lw=0.1)


plt.show()

生成的图是

根据方法 1 带有四分位数的箱线图

于 2021-03-25T11:07:55.270 回答