使用Python和matplotlib获取箱型图中的值

36 投票
4 回答
68624 浏览
提问于 2025-04-18 05:21

我可以用数据画一个箱线图:

import numpy as np
import matplotlib.pyplot as plt

data = np.random.rand(100)
plt.boxplot(data)

在这个箱线图中,箱子的范围是从第25百分位数到第75百分位数,而“胡须”的范围是从最小值到最大值,这个最大值和最小值是在(25th-percentile - 1.5*IQR, 75th-percentile + 1.5*IQR)之间,其中IQR代表四分位间距。(当然,1.5这个值是可以调整的)。

现在我想知道箱线图中用到的数值,比如中位数、上四分位数和下四分位数,以及胡须的上端点和下端点。前面三个数值可以通过使用np.median()np.percentile()轻松获得,但要得到胡须的端点就需要写一些比较复杂的代码:

median = np.median(data)
upper_quartile = np.percentile(data, 75)
lower_quartile = np.percentile(data, 25)

iqr = upper_quartile - lower_quartile
upper_whisker = data[data<=upper_quartile+1.5*iqr].max()
lower_whisker = data[data>=lower_quartile-1.5*iqr].min()

我在想,虽然这样做可以,但有没有更简单的方法呢?看起来这些数值应该可以直接从已经画好的箱线图中提取出来。

4 个回答

-1
upper_whisker = data[data<=upper_quartile+1.5*iqr].max()
lower_whisker = data[data>=lower_quartile-1.5*iqr].min()

等于

upper_whisker = data.max()
lower_whisker = data.min()

如果你只是想获取数据集中真实的数据点。但是从统计学的角度来看,须状线的值是上四分位数加上1.5倍的四分位距,下四分位数减去1.5倍的四分位距。

0

你可以从数据框的系列中找到值。比如说,可以在图表中显示中位数的值作为注释。

举个例子,假设有一个数据框,它有两列,col1(分类数据)和col2(连续数据)。我们想要根据col1的值来绘制col2的箱线图:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

d = {'col1': ['B','A','A','B','B','A'], 'col2':[1,20,30,40,60,70]}
df = pd.DataFrame(data=d)
df['col1']= df['col1'].astype("category")

fig, axes = plt.subplots(figsize=(10, 10),nrows=1, ncols=1, sharey=True)
i='col2'
j='col1'
df.boxplot(ax=axes,column=[i], by=j, grid=True)

for value,cat in enumerate(df[j].cat.categories):
    series=df[df[j]==cat]
    median=series[i].describe()['50%']
    median=np.round(median,1)
    axes.annotate(median,(value+1+0.25,median),fontsize=24, color='blue')

plt.show()
17

最近我遇到了这个问题,于是写了一个函数,用来从箱线图中提取数据,并把这些数据放到一个pandas的数据框里。

这个函数是:

def get_box_plot_data(labels, bp):
    rows_list = []

    for i in range(len(labels)):
        dict1 = {}
        dict1['label'] = labels[i]
        dict1['lower_whisker'] = bp['whiskers'][i*2].get_ydata()[1]
        dict1['lower_quartile'] = bp['boxes'][i].get_ydata()[1]
        dict1['median'] = bp['medians'][i].get_ydata()[1]
        dict1['upper_quartile'] = bp['boxes'][i].get_ydata()[2]
        dict1['upper_whisker'] = bp['whiskers'][(i*2)+1].get_ydata()[1]
        rows_list.append(dict1)

    return pd.DataFrame(rows_list)

你可以通过传入一个标签数组(也就是你在绘制箱线图时用到的那些标签)和箱线图函数返回的数据来调用这个函数。

举个例子:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

def get_box_plot_data(labels, bp):
    rows_list = []

    for i in range(len(labels)):
        dict1 = {}
        dict1['label'] = labels[i]
        dict1['lower_whisker'] = bp['whiskers'][i*2].get_ydata()[1]
        dict1['lower_quartile'] = bp['boxes'][i].get_ydata()[1]
        dict1['median'] = bp['medians'][i].get_ydata()[1]
        dict1['upper_quartile'] = bp['boxes'][i].get_ydata()[2]
        dict1['upper_whisker'] = bp['whiskers'][(i*2)+1].get_ydata()[1]
        rows_list.append(dict1)

    return pd.DataFrame(rows_list)

data1 = np.random.normal(loc = 0, scale = 1, size = 1000)
data2 = np.random.normal(loc = 5, scale = 1, size = 1000)
data3 = np.random.normal(loc = 10, scale = 1, size = 1000)

labels = ['data1', 'data2', 'data3']
bp = plt.boxplot([data1, data2, data3], labels=labels)
print(get_box_plot_data(labels, bp))
plt.show()

get_box_plot_data 得到的输出是:

   label  lower_whisker  lower_quartile    median  upper_quartile  upper_whisker
0  data1      -2.491652       -0.587869  0.047543        0.696750       2.559301
1  data2       2.351567        4.310068  4.984103        5.665910       7.489808
2  data3       7.227794        9.278931  9.947674       10.661581      12.733275

并生成了如下的图表: 这里输入图片描述

35

你为什么想这么做呢?你现在的做法已经很直接了。

是的,如果你是想获取图表的数据,而图表已经画好了,那就直接用 get_ydata() 这个方法就行了。

B = plt.boxplot(data)
[item.get_ydata() for item in B['whiskers']]

这个方法会返回一个形状为 (2,) 的数组,对于每根须条来说,第二个元素就是我们想要的值:

[item.get_ydata()[1] for item in B['whiskers']]

撰写回答