数据处理——拉伊达法则去除异常值(Python实现)

数据处理——拉伊达法则去除异常值(Python实现)

背景:

题目出自2020年中国研究生数学建模竞赛B题

代码及附件
上传时间:2020.12.24

1 数据采集

原始数据采集来自于中石化高桥石化实时数据库(霍尼韦尔PHD)及LIMS实验数据库。其中操作变量数据来自于实时数据库,采集时间为2017年4月至2020年5月,采集操作位点数共354个。2017年4月至2019年9月,数据采集频次为3分钟/次;2019年10月至2020年5月,数据采集频次为6分钟/次。原料、产品和催化剂数据来自于LIMS实验数据库,数据时间范围为2017年4月至2020年5月。其中原料及产品的辛烷值是重要的建模变量,该数据采集频次为每周2次。

2 数据整定

原始数据中,大部分变量数据正常,但每套装置的数据均有部分位点存在问题:部分变量只含有部分时间段的数据,部分变量的数据全部为空值或部分数据为空值。因此对原始数据进行处理后才可以使用。数据处理方法如下:

(1)对于只含有部分时间点的位点,如果其残缺数据较多,无法补充,将此类位点删除;

(2)删除325个样本中数据全部为空值的位点;

(3)对于部分数据为空值的位点,空值处用其前后两个小时数据的平均值代替;

(4)根据工艺要求与操作经验,总结出原始数据变量的操作范围,然后采用最大最小的限幅方法剔除一部分不在此范围的样本;

(5)根据拉依达准则(3σ准则)去除异常值。

3σ准则:设对被测量变量进行等精度测量,得到x1,x2,……,xn,算出其算术平均值x及剩余误差vi=xi-x(i=1,2,…,n),并按贝塞尔公式算出标准误差σ,若某个测量值xb的剩余误差vb(1<=b<=n),满足|vb|=|xb-x|>3σ,则认为xb是含有粗大误差值的坏值,应予剔除。贝塞尔公式如下:

img

3 样本确定

本题目标为降低S Zorb装置产品辛烷值损失,故确定样本的主要依据为样品的辛烷值数据。由于辛烷值的测定数据相对于操作变量数据而言相对较少,而且辛烷值的测定往往滞后,因此确定某个样本的方法为:以辛烷值数据测定的时间点为基准时间,取其前2个小时的操作变量数据的平均值作为对应辛烷值的操作变量数据。

4 Python代码

  • 读取 excel表格数据
  • 去除空值、残缺数据过多(大于10个)的位点
  • 残缺数据少的位点 数据用同位点平均值替代
  • 根据 拉伊达准则 去除异常值
import numpy as np
import pandas as pd
import xlrd
import xlwt
import openpyxl
import math
import matplotlib.pyplot as plt
from datetime import date, datetime

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False


# 定义正态分布函数
def pdf(x, ave, std):
    y = (1.0 / math.sqrt(2 * math.pi * std)) * np.exp(-(x - ave) ** 2 / (2 * std ** 2))
    return y


# 通过路径,打开文件工作簿
#313
# ExcelFile = xlrd.open_workbook(r'F:\0.0\竞赛\2020数学建模\1111\附件三:285号和313号样本原始数据 1.xlsx')
#285
ExcelFile = xlrd.open_workbook(r'F:\0.0\竞赛\2020数学建模\1111\313.xlsx')


# 通过sheet名查找打开工作表
sheet = ExcelFile.sheet_by_name('操作变量')
# 通过sheet索引查找打开工作表
# sheet=ExcelFile.sheet_by_index(4)

# 打印输出当前工作表名,行数以及列数
print('当前工作表名,行数以及列数' + sheet.name, sheet.nrows, sheet.ncols)

# 获取行或者列中部分元素的值sheet.row_values(a,b,c) sheet.col_values(a,b,c)
# a,b,c均从0开始计数,从b号元素开始到c-1号元素,不包含c号元素

# 确定 各列输出列表
outX1 = []  #均值输出
list_1 = []  #空 列表
Z1 = 0  # 计算多少列剔除异常值


for a in range(1,355):

    # rowsData=sheet.row_values(3,1,355)   #取第4列,第2号元素到第355号元素

    # 285 号
    colsData = sheet.col_values(a, 3, 43)  #取第2列,第4号元素到第43号元素


    #313 号
    #colsData = sheet.col_values(a, 43, 84)  # 取第2列,第4号元素到第43号元素

    # 打印输出取出的数据元素,默认数据类型为list
    # print("取出的数据元素:")
    # print(colsData)
    # 将list类型的数据转换成array类型
    data = np.array(colsData)

    # 计算数据的算术平均值、标准误差以及剩余误差
    arithmeticMean = data.mean()
    standardDeviation = data.std()
    residualError = abs(data - arithmeticMean)

    # 打印输出均值和标准差
    # print('列算数平均值、标准误差以及剩余误差:')
    # print(arithmeticMean, standardDeviation, residualError)

    # 未测位点判断
    if standardDeviation == 0:
        X1 = 0

        print('第' + str(a) + '列数据全为0,算术平均值:' + str(X1))
        #print('注:本列全为0,缺损值过多')
        continue

    else :

        #解决缺损值问题
        Y1 = 0         #计算缺损数据量个数
        for i in range(0,40):
            if colsData[i] == 0:
                #求缺损值的个数
                Y1 = Y1 + 1
        #缺损值不多时,用平均值替代
        if 0 < Y1 <= 10:
            arithmeticMean = arithmeticMean * 40 / (40 - Y1)
            print('第' + str(a) + '列有少量缺损值,已替换')
        elif  Y1 > 10:
            arithmeticMean = arithmeticMean * 40 / (40 - Y1)
            print('第' + str(a) + '列缺损值过多,已删除此为点')



        if __name__ == '__main__':

            # plot histgram of its distribution
            x = np.sort(data)
            y = pdf(x, arithmeticMean, standardDeviation)


            # 去除异常值
            good_x = []
            outliers = []


            for i, j in zip(residualError, x):
                if i < 3 * standardDeviation:
                    good_x.append(j)
                else:
                    outliers.append(j)

            good_x = np.array(good_x)
            goodArithmeticMean = good_x.mean()

            goodStandardDeviation = good_x.std()
            good_y = pdf(good_x, goodArithmeticMean, goodStandardDeviation)





            #求解 剔除异常值之后的算术平均值
            if outliers != list_1:
                X1 = (arithmeticMean * 40 - sum(outliers))/(40 - len(outliers))
                print('第' + str(a) + '列剔除的异常值为:', outliers)
                print('第' + str(a) + '列数据的移除异常值之后的算数平均值:' + str(X1))

                Z1 = Z1 + 1
            else:
                X1 = arithmeticMean
                print('第' + str(a) + '列数据的算术平均值:' + str(X1))

# 将处理之后的数据打印输出

    outX1.append(X1)

    # # 数据可视化
    # plt.plot(x, y, c='b', label=u'原始值')
    # plt.plot(good_x, good_y, c='r', label=u'去除异常值后数据')
    # plt.title('Normalization distribution curve  313号第' + str(a) + '列元素')
    #
    # if outliers != list_1:
    #     plt.ylabel(u"标准差太小")
    #     plt.xlabel(u'原始数据  剔除的异常值为:'+ str(outliers))
    # else :
    #     plt.xlabel(u"原始数据")
    #
    # plt.legend()
    # plt.show()

    # 数据可视化


    if outliers != list_1:
        plt.ylabel(u"标准差太小")
        plt.xlabel(u'原始数据  剔除的异常值为:'+ str(outliers))
        plt.plot(x, y, c='b', label=u'原始值')
        plt.plot(good_x, good_y, c='r', label=u'去除异常值后数据')
        plt.title('Normalization distribution curve  313号第' + str(a) + '列元素')
        plt.legend()
        plt.show()

print('处理之后的数据:' )
print(outX1)
print('共计' + str(Z1) + '列剔除了 异常值')



# #将数据写入新文件
# #打开工作簿,进行操作
# wb = openpyxl.Workbook()
# sheet = wb.active
#
# # sheet['A1'] = 'hello'
# # print(sheet['A1'].value)
#
# #新创建页
# ws1 = wb.create_sheet('285')
# #ws2 = wb.create_sheet('313')
# row = 0
#
# ws1.append(outX1)
# #ws2.append(outX1)
# #313号样本给入 313  #285号样本给入 285
#
# wb.save(filename='date.xlsx')
# print('写入成功')


你可能感兴趣的:(数学建模,Python,python,数据分析,数据挖掘,数学建模)