使用Python和matplotlib获取箱型图中的值
我可以用数据画一个箱线图:
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 个回答
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倍的四分位距。
你可以从数据框的系列中找到值。比如说,可以在图表中显示中位数的值作为注释。
举个例子,假设有一个数据框,它有两列,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()
最近我遇到了这个问题,于是写了一个函数,用来从箱线图中提取数据,并把这些数据放到一个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
你为什么想这么做呢?你现在的做法已经很直接了。
是的,如果你是想获取图表的数据,而图表已经画好了,那就直接用 get_ydata()
这个方法就行了。
B = plt.boxplot(data)
[item.get_ydata() for item in B['whiskers']]
这个方法会返回一个形状为 (2,) 的数组,对于每根须条来说,第二个元素就是我们想要的值:
[item.get_ydata()[1] for item in B['whiskers']]