利用箱线图过滤数据

Posted _WILLPOWER_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用箱线图过滤数据相关的知识,希望对你有一定的参考价值。

箱线图

维基百科:
箱形图(英文:Box plot),又称为盒须图、盒式图、盒状图或箱线图,是一种用作显示一组数据分散情况资料的统计图。
因型状如箱子而得名。此图中之盒子之外,也常会有线条在上下四分位数之外延伸出去,像是胡须,因此也称为盒须图。离群值会有时会画成是个别的点。箱型图是无母数的,他显示样品的特性,对于母体分布并无任何假设。在各种领域也经常被使用,常见于品质管理。不过作法相对较繁琐。

箱形图于1977年由美国著名统计学家约翰·图基(John Tukey)发明。它能显示出一组数据的最大值最小值中位数、及上下四分位数


这组数据显示出:

  • 最小值(minimum)=5
  • 第一四分位数(Q1)=7
  • 中位数(Med --也就是Q2)=8.5
  • 第三四分位数(Q3)=9
  • 最大值(maximum )=10
  • 平均值=8
  • 四分位间距(interquartile range)={\\displaystyle (Q3-Q1)}{\\displaystyle (Q3-Q1)}=2 (即ΔQ)

在区间 Q3+1.5ΔQ, Q1-1.5ΔQ 之外的值被视为应忽略(farout)。

  • farout: 在图上不予显示,仅标注一个符号∇。
  • 最大值区间: Q3+1.5ΔQ
  • 最小值区间: Q1-1.5ΔQ

最大值与最小值产生于这个区间。区间外的值被视为outlier显示在图上.

  • mild outlier(离群值) = 3.5
  • extreme outlier (极端值) = 0.5


总之我们要知道的是,我们可以利用箱线图来处理一组数据中的异常数据
而这个数据处于箱线图的最大值以上,以及最小值以下!

箱线图分析和过滤数据

单组数据的箱线图绘制

利用matplotlib 库 pyplot 模块里的 boxplot() 函数

import matplotlib.pyplot as plt
# 生成数据
#x = pd.Series([1,2,3,3,4,5,10,12,2,2,3,1,4,3,-10,-1])同样可以使用series来绘制
x = [1,2,3,3,4,5,10,12,2,2,3,1,4,3,-10,-1]
plt.boxplot(x,patch_artist=True,labels=['test data'])
plt.show()


其中可选参数为
图片参考链接-百里希文

多组数据的箱线图绘制

多组数据的箱线图可以利用pandas中dataframe的boxplot方法

testData = pd.DataFrame({"a":[1,2,3,4,9,5,4,3,4,-1,-1-2],
                         "b":[2,3,3,3,4,5,5,6,25,-2,-2]})
testData.boxplot()

数据过滤

单组数据

对于单组数据的处理,我们可以通过查看图来进行数据的过滤;

import matplotlib.pyplot as plt
x = pd.Series([1,2,3,3,4,5,10,12,2,2,3,1,4,3,-10,-1])
plt.boxplot(x,patch_artist=True,labels=['test data'])
plt.show()

这里我们可以看到数据为10和12以及-10是异常数据,因此我们将其剔除

#将小于5,并且大于-5的数据保留
plt.boxplot(x[(x<5) & (x>-5)],patch_artist=True,labels=['test data'])
plt.show()

多组数据

当数据很多的时候,我们肉眼来进行查看显然是不现实的
比如以下图

那么我们如何进行数据异常的过滤呢?我们可以利用pandas中dataframe的boxplot方法返回的值进行数据过滤。通过设定return_typedict我们就可以获取箱线图的每个组件映射到Line2D创建的实例列表的字典。
该字典具有以下键(假设垂直箱线图):

  • boxes:箱线图的主体显示四分位数和中位数的置信区间(如果启用)。
  • medians:每个框中间的水平线。
  • whiskers:垂直线延伸到最极端的非异常数据点。
  • caps:胡须末端的水平线。
  • fliers:代表超出胡须(传单)的数据的点。
  • means:代表均值的点或线。

这里我们需要的即为fliers的值,而这个fliers的获取值是一个列表,保存的是每一列(每个属性)的异常值的信息,该信息为matplotlib.lines.Line2D的类型,我们就可以获取异常点的相关信息
举个例子

testData = pd.DataFrame({"a":[1,2,3,4,9,5,4,3,4,-1,-1-2],
                         "b":[2,3,3,3,4,5,5,6,25,-2,-2]})
allbox = testData.boxplot(return_type='dict')
print(type(allbox["fliers"]))
print(type(allbox["fliers"][0]))


我们通过matplotlib.lines.Line2D中的 get_ydata来获取异常点的纵坐标

allbox["fliers"][0].get_ydata()

可以看到这里异常值为 -3以及9

从而我们可以通过判断数据的值来将数据剔除。

这里给出一个函数,将异常值剔除

def boxfilter(data, proportion:float):
    allbox = data.boxplot(return_type='dict')
    zipped = zip(allbox['fliers'], testData.columns)
    i = 0
    for flier, index_name in zipped:
        # 获取一个列的异常y值
        y = flier.get_ydata()
        # 获取数据长度
        datalenth = data.shape[0]
        # 如果有异常值才处理
        if(len(y) >= 1):
            #对于每一个异常值做处理
            for i in y:          
                #因为你不知道异常值是高于还是低于,因此这里做两次判断(异常值毕竟是少数)
                #如果留下来的数据规模是大于以前数据规模*proportion的,则进行处理,这个取决于你来定
                if(data[data[index_name] < i].shape[0] >=datalenth*proportion):
                    data = data[data[index_name] < i]
                elif(data[data[index_name] > i].shape[0] >= datalenth*proportion):
                    data = data[data[index_name] > i] 
    return data

使用例子

def boxfilter(data, proportion:float):
    allbox = data.boxplot(return_type='dict')
    zipped = zip(allbox['fliers'], testData.columns)
    i = 0
    for flier, index_name in zipped:
        # 获取一个列的异常y值
        y = flier.get_ydata()
        # 获取数据长度
        datalenth = data.shape[0]
        # 如果有异常值才处理
        if(len(y) >= 1):
            #对于每一个异常值做处理
            for i in y:          
                #因为你不知道异常值是高于还是低于,因此这里做两次判断(异常值毕竟是少数)
                #如果留下来的数据规模是大于以前数据规模*proportion的,则进行处理,这个取决于你来定
                if(data[data[index_name] < i].shape[0] >=datalenth*proportion):
                    data = data[data[index_name] < i]
                elif(data[data[index_name] > i].shape[0] >= datalenth*proportion):
                    data = data[data[index_name] > i] 
    return data
testData = pd.DataFrame({"a":[1,2,3,4,9,5,4,3,4,-1,-1-2],
                         "b":[2,3,3,3,4,5,5,6,25,-2,-2]})
newdata = boxfilter(testData,0.7)
newdata.boxplot(return_type='dict')

一些滤出图

以上是关于利用箱线图过滤数据的主要内容,如果未能解决你的问题,请参考以下文章

可视化实验十二:利用Python绘制箱线图折线图

可视化实验十二:利用Python绘制箱线图折线图

通过箱线图判断偏向

Python去除异常数据——利用numpy求箱线图特征值

Fig4-a ggplot2绘制箱线图叠加散点图2020-12-14

向熊猫数据框箱线图添加标签?