# -*- coding：utf-8 -*-
# &Author  AnFany

# 两层的Blending回归

#  第一层7个模型：随机森林，AdaBoost，GBDT，LightGBM，XGBoost，CatBoost，BPNN回归
#  第二层模型：线性回归

# 引入数据文件
import pm25_Blending_data as pm25

# 引入绘图库包
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['FangSong']  # 显示中文
mpl.rcParams['axes.unicode_minus'] = False  # 显示负号

# 引入需要用到的模型的库包
# 随机森林
from sklearn.ensemble import RandomForestRegressor as RF
# AdaBoost
from sklearn.ensemble import AdaBoostRegressor
from sklearn.tree import DecisionTreeRegressor
# GBDT
from sklearn.ensemble import GradientBoostingRegressor
# XGBoost
import xgboost as xgb
# LightGBM
import lightgbm as lgbm
# CatBoost
import catboost as cb
# BP神经网络回归
import BP_Regression as bp

# 线性回归
import Linear_Regression as linear

# 其他库包
import tensorflow as tf
import numpy as np
import pandas as pd
from collections import OrderedDict  # python字典是无序的，此包是有序的
import os
os.chdir(r'E:\tensorflow_Learn\Blending\pm25')
'''
第一部分：数据处理模型
'''

class DATA:

    def __init__(self, datadict=pm25.data_dict, mubiao='pm2.5'):
        self.data = datadict
        self.k = 0.3  # 在Blending中验证数据所占的比例

        # 训练数据
        self.chidata = self.data['train']

        # 预测数据
        self.nodata = self.data['predict']

        # 类别型数据的编号
        self.catsign = self.Sign()

        # 目标字段
        self.ziduan = mubiao

        # 对于归一化化和标准化的处理，要记录转化的值，在这里将2者统一。反处理时，预测值需要乘以self.fenmu 加上self.cha
        self.fenmu = 1  # 相当于标准化的标准差, 归一化的最大值减去最小值
        self.cha = 0   # 相当于标准化的平均值, 归一化的最小值

    # 因为对于CatBoost而言，不需要进行类别型特征的处理，但是需要类别型特征的标号
    def Sign(self):
        sign = []
        numlist = self.chidata.values[0][: -1]  # 不包括最后的目标字段
        for jj in range(len(numlist)):
            try:
                numlist[jj] + 9
            except TypeError:
                sign.append(jj)
        return sign

    # 类别型特征数字标签化函数，
    def CAtoDI(self):
        # 如果特征值不能执行数字加法运算，则视为类别型特征
        for tezheng in self.chidata:
            try:
                self.chidata[tezheng].values[0] + 1
            except TypeError:
                numlist = sorted(list(set(list(self.chidata[tezheng]))))
                self.chidata[tezheng] = [numlist.index(hh) for hh in self.chidata[tezheng]]
                try:
                    self.nodata[tezheng] = [numlist.index(ss) for ss in self.nodata[tezheng]]
                except ValueError:
                    print('特征%s：预测比训练的多了值' % (tezheng))
        return print('数字化处理完毕')

    # 对于归一化和标准化的函数，要记录目标字段的转化值，因为要进行反归一化

    # 归一化函数
    def Normal(self):
        # 在此之前需要把类别型标签去掉,否则会报错
        for tezheng in self.chidata:
            maxnum = max(list(self.chidata[tezheng]))
            minum = min(list(self.chidata[tezheng]))
            if maxnum == minum:
                self.chidata[tezheng] = [1 for hh in self.chidata[tezheng]]
                self.nodata[tezheng] = [1 for ss in self.nodata[tezheng]]
            else:
                self.chidata[tezheng] = [(hh - minum) / (maxnum - minum) for hh in self.chidata[tezheng]]
                self.nodata[tezheng] = [(ss - minum) / (maxnum - minum) for ss in self.nodata[tezheng]]
                if tezheng == self.ziduan:
                    self.fenmu = maxnum - minum
                    self.cha = minum
        return print('归一化处理完毕')

    # 标准化函数
    def Stand(self):
        # 在此之前需要把类别型标签去掉,否则会报错
        for tezheng in self.chidata:
            standnum = np.std(np.array(list(self.chidata[tezheng])), ddof=1)  # 计算有偏的标准差
            meanum = np.mean(np.array(list(self.chidata[tezheng])))

            if meanum == 0:
                self.chidata[tezheng] = [1 for hh in self.chidata[tezheng]]
                self.nodata[tezheng] = [1 for ss in self.nodata[tezheng]]
            else:
                self.chidata[tezheng] = [(hh - standnum) / meanum for hh in self.chidata[tezheng]]
                self.nodata[tezheng] = [(ss - standnum) / meanum for ss in self.nodata[tezheng]]
                if tezheng == self.ziduan:
                    self.fenmu = standnum
                    self.cha = meanum
        return print('标准化处理完毕')

    # 按照验证数据集的比例,将训练数据集分为验证和训练数据集
    def Kfold(self):
        # 因为要保证每个模型的验证数据是一样的，
        datanum = self.chidata.values
        # 数据集总长度
        length = len(datanum)
        alist = np.arange(length)
        np.random.seed(1990)
        np.random.shuffle(alist)  # 随机打乱数据对BPNN,SVM而言是有益处的，而对于决策树之类的模型而言没有影响

        # 验证数据的长度
        yanlem = int(length * self.k)

        # 存储数据集的字典
        datai = {}
        datai['predict'] = self.nodata.values

        # 选中的作为验证数据集的索引序列
        np.random.seed(2000)
        yanlis = np.random.choice(alist, yanlem, replace=False)

        # 没有被选中的作为训练数据集的索引序列
        trainlis = [ki for ki in alist if ki not in yanlis]

        # 储存训练数据集
        datai['train'] = datanum[trainlis]

        # 储存验证数据集
        datai['test'] = datanum[yanlis]

        # 返回的数据集形式{'train':data, 'test':data, 'predict':data}
        print('数据集分割处理完毕')
        return datai


'''
第二部分：第一层的模型运行阶段
'''
# 可以任意添加模型
class MODELONE:

    def __init__(self, fenmu, cha, zidan='pm2.5'):

        # 验证数据集的预测结果
        self.yanzhneg_pr = []

        # 预测数据集的预测结果
        self.predi = []

        # 目标字段名称
        self.zi = zidan

        # 数据结构和数据处理类的保持一致，要把验证数据集的输入和真实的输出合二为一
        self.datai = {}

        # 记录每个模型最终误差的字典
        self.error_dict = OrderedDict()

        # 验证数据集的真实输出结果
        self.yanzhneg_real = []

        # 预测数据集的真实输出结果
        self.preal = []

        #  针对BPnn，得到的结果数据要反归一化
        self.fen = fenmu
        self.cha = cha

    # 将第一层的结果转换为何数据结构处理类中一样的数据结构的函数
    #  也就是{'train':dataframe, 'predict':dataframe}样式的字典
    def DataStru(self):
        self.datai['train'] = np.row_stack((np.array(self.yanzhneg_pr), np.array(self.yanzhneg_real)))  # 此处添加行
        self.datai['predict'] = np.row_stack((np.array(self.predi), np.array(self.preal)))
        # 将训练数据转置
        datapst = self.datai['train'].T
        # 为训练数据定义DataFrame的列名
        mingcheng = ['第%s个模型列' % str(dd) for dd in list(range(len(self.datai['train']) - 1))] + [self.zi]
        self.datai['train'] = pd.DataFrame(datapst, columns=mingcheng)

        # 将预测数据转置
        dapst = self.datai['predict'].T
        # 为训练数据定义DataFrame的列名
        mingche= ['第%s个模型列' % str(dd) for dd in list(range(len(self.datai['predict']) - 1))] + [self.zi]
        self.datai['predict'] = pd.DataFrame(dapst, columns=mingche)
        return print('二层的数据准备完毕')

    # 定义均方误差的函数
    def RMSE(self, data1, data2):
        data1, data2 = np.array(data1), np.array(data2)
        subdata = np.power(data1 - data2, 2)
        return np.sqrt(np.sum(subdata) / len(subdata - 1))

    # 随机森林
    def RF_First(self, data, n_estimators=400, max_features='auto'):
        # 对训练数据进行训练，返回模验证数据，预测数据的预测结果
        model = RF(n_estimators=n_estimators, max_features=max_features)
        model.fit(data['train'][:, :-1], data['train'][:, -1])
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        #预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['随机森林'] = [xx, yy, pp]

        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]

        return print('1层中的随机森林运行完毕')

    # AdaBoost
    def Adaboost_First(self, data, max_depth=5, n_estimators=320):
        model = AdaBoostRegressor(DecisionTreeRegressor(max_depth=max_depth),
                                  n_estimators=n_estimators, learning_rate=0.8)
        model.fit(data['train'][:, :-1], data['train'][:, -1])
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        # 预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['AdaBoost'] = [xx, yy, pp]
        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]
        return print('1层中的AdaBoost运行完毕')

    # GBDT
    def GBDT_First(self, data, max_depth=17, n_estimators=57):
        model = GradientBoostingRegressor(loss='ls', n_estimators=n_estimators, max_depth=max_depth,
                                          learning_rate=0.12, subsample=0.8)
        model.fit(data['train'][:, :-1], data['train'][:, -1])
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        # 预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['GBDT'] = [xx, yy, pp]
        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]
        return print('1层中的GBDT运行完毕')

    # LightGBM
    def LightGBM_First(self, data, max_depth=9, n_estimators=380):
        model = lgbm.LGBMRegressor(boosting_type='gbdt', objective='regression', num_leaves=1200,
                                   learning_rate=0.17, n_estimators=n_estimators, max_depth=max_depth,
                                   metric='rmse', bagging_fraction=0.8, feature_fraction=0.8, reg_lambda=0.9)
        model.fit(data['train'][:, :-1], data['train'][:, -1])
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        # 预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['LightGBM'] = [xx, yy, pp]
        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]
        return print('1层中的LightGBM运行完毕')

    # XGBoost
    def XGBoost_First(self, data, max_depth=5, n_estimators=320):
        model = xgb.XGBRegressor(max_depth=max_depth, learning_rate=0.1, n_estimators=n_estimators,
                                 silent=True, objective='reg:gamma')
        model.fit(data['train'][:, :-1], data['train'][:, -1])
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        # 预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['XGBoost'] = [xx, yy, pp]
        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]
        return print('1层中的XGBoost运行完毕')

    # CatBoost
    def CatBoost_First(self, data, catsign, depth=8, iterations=80000):
        model = cb.CatBoostRegressor(iterations=iterations, depth=depth, learning_rate=0.8, loss_function='RMSE')
        model.fit(data['train'][:, :-1], data['train'][:, -1], cat_features=catsign)
        # 注意存储验证数据集结果和预测数据集结果的不同
        # 训练数据集的预测结果
        xul = model.predict(data['train'][:, :-1])
        # 验证的预测结果
        yanre = model.predict(data['test'][:, :-1])
        # 预测的预测结果
        prer = model.predict(data['predict'][:, :-1])
        # 储存
        self.yanzhneg_pr.append(yanre)
        self.predi.append(prer)
        # 分别计算训练、验证、预测的误差
        # 每计算一折后，要计算训练、验证、预测数据的误差
        xx = self.RMSE(xul, data['train'][:, -1])
        yy = self.RMSE(yanre, data['test'][:, -1])
        pp = self.RMSE(prer, data['predict'][:, -1])
        # 储存误差
        self.error_dict['CatBoost'] = [xx, yy, pp]
        # 验证数据集的真实输出结果
        self.yanzhneg_real = data['test'][:, -1]

        # 预测数据集的真实输出结果
        self.preal = data['predict'][:, -1]
        return print('1层中的CatBoost运行完毕')


    # BPNN回归
    def BPnn(self, data, hiddenlayers=3, hiddennodes=100, learn_rate=0.05, itertimes=10000,
             batch_size=200, activate_func='sigmoid', break_error=0.00000043):
        sign, fir, trer, ader = bp.Ten_train(data['train'][:, :-1], np.array([data['train'][:, -1]]).T,
                                 data['test'][:, :-1], np.array([data['test'][:, -1]]).T,
                                 hiddenlayers=hiddenlayers, hiddennodes=hiddennodes,
                                 learn_rate=learn_rate, itertimes=itertimes,
                                 batch_size=batch_size, activate_func=activate_func,
                                 break_error=break_error)

        # 这个模型的误差在后文添加上
        return sign, fir, trer, ader

'''
第三部分：第二层的模型运行阶段 可以任意更换模型
'''
class MODETWO:

    def __init__(self, in_tr_data, out_tr_data, in_pre_data):
        self.xdata = in_tr_data
        self.ydata = out_tr_data

        self.in_pre_data = in_pre_data


    # 梯度下降线性回归
    def Lin_Gr(self, learn_rate=0.00000003, iter_times=80000, error=1e-9):
        model = linear.LinearRegression(learn_rate=learn_rate, iter_times=iter_times, error=error)
        # 两种方法
        model.Gradient(self.xdata, self.ydata)
        outpre = model.predict(self.in_pre_data)
        return outpre

    # 公式法线性回归
    def Lin_Fo(self):
        model = linear.LinearRegression()
        # 两种方法
        model.Formula(self.xdata, self.ydata)
        outpre = model.predict(self.in_pre_data)
        return outpre



'''
第四部分：绘制图，绘制第一层各个模型中训练，验证数据的误差，
以及最终的预测数据的真实值和误差值的对比
'''
# 定义绘制第一层模型训练、验证、预测数据的误差的函数
# 根据字典绘制不同参数下评分的对比柱状图
def Plot_RMSE_ONE_Blending(exdict, kaudu=0.2):
    '''
    :param exdict: 不同模型的RMSE 最小二乘回归误差的平方根
    :return: 柱状图
    '''
    # 参数组合列表
    palist = exdict.keys()
    # 对应的训练数据的评分
    trsore = [exdict[hh][0] for hh in palist]
    # 对应的测试数据的评分
    tesore = [exdict[hh][1] for hh in palist]
    # 对应的预测数据的评分
    presore = [exdict[hh][2] for hh in palist]

    # 开始绘制柱状图
    fig, ax = plt.subplots()
    # 柱的个数
    ind = np.array(list(range(len(trsore))))
    # 绘制柱状
    ax.bar(ind - kaudu, trsore, kaudu, color='SkyBlue', label='训练')
    ax.bar(ind, tesore, kaudu, color='IndianRed', label='测试')
    ax.bar(ind + kaudu, presore, kaudu, color='slateblue', label='预测')
    # xy轴的标签
    ax.set_ylabel('RMSE')
    ax.set_xlabel('Blending第一层中的模型')
    # 设置刻度
    ax.set_xticks(ind)
    ax.set_xticklabels(palist)

    ax.grid()

    leg = ax.legend(loc='best', ncol=3, shadow=True, fancybox=True)
    leg.get_frame().set_alpha(0.8)
    plt.title('Blending第一层中模型的RMSE')
    plt.savefig(r'C:\Users\GWT9\Desktop\Blending_pm25.jpg')
    return '一层不同模型对比'

# 最终的预测数据的真实值和误差值的对比
# 按照误差值从小到大排列的数据
def Pailie(realr, modelout, count=90):
    '''
    :param real: 预测数据集真实的数据
    :param modelout: 预测数据集的模型的输出值
    :param count: 进行数据对比的条数
    :return: 按照差值从小到大排列的数据
    '''
    relal_num = np.array(realr)
    modelout_num = np.array(modelout)
    # 随机选取
    np.random.seed(200)
    fu = np.random.choice(list(range(len(realr))), count, replace=False)
    show_real, show_model = relal_num[fu], modelout_num[fu]
    # 计算差值
    sunnum = show_real - show_model
    # 首先组合三个数据列表为字典
    zuhedict = {ii: [show_real[ii], show_model[ii], sunnum[ii]] for ii in range(len(show_model))}
    # 字典按着值排序
    zhenshi = []
    yucede = []
    chazhi = []
    # 按着差值从大到小
    for jj in sorted(zuhedict.items(), key=lambda gy: gy[1][2]):
        zhenshi.append(jj[1][0])
        yucede.append(jj[1][1])
        chazhi.append(jj[1][2])
    return zhenshi, yucede, chazhi

# 绘制预测值，真实值对比折线，以及与两值的误差柱状图
def recspre(yzhenshide, yyucede, title='公式法'):
    #  获得展示的数据
    yreal, ypre, cha = Pailie(yzhenshide, yyucede)
    plt.figure()
    ax = plt.subplot(111)
    plt.grid()
    dign = np.arange(len(yreal))
    #  绘制真实值
    ax.scatter(dign, yreal, label='真实值', lw=2, color='blue', marker='*')
    #  绘制预测值
    ax.plot(dign, ypre, label='预测值', lw=2, color='red', linestyle='--', marker='.')
    #  绘制误差柱状图
    ax.bar(dign, cha, 0.1, label='真实值减去预测值', color='k')
    # 绘制0线
    ax.plot(dign, [0] * len(dign), lw=2, color='k')

    ax.set_ylim((int(min(cha)) - 1, int(max([max(yreal), max(ypre)]))))
    ax.set_xlim((0, len(dign)))

    ax.legend(loc='best')
    ax.set_title('%s:北京市Pm2.5预测数据集结果对比' % title)
    plt.savefig(r'C:\Users\GWT9\Desktop\Blending_duibi_%s.jpg' % title)

    return '完毕'



'''
第五部分：Blending主函数
'''

if __name__ == "__main__":
    #  第一层7个模型：随机森林，AdaBoost，GBDT，LightGBM，XGBoost，CatBoost，BPNN

    # 下面依次为每个模型建立数据
    # 随机森林、AdaBoost，GBDT，LIghtGNM，XGBoost都是一样的
    rf_data = DATA()
    rf_data.CAtoDI()  # 标签数字化
    data_rf = rf_data.Kfold()  # 分割数据

    # CatBoost
    cat_data = DATA()  # 不用处理
    data_cat = cat_data.Kfold()  # 分割数据


    # BPnn数据处理
    bp_data = DATA()
    bp_data.CAtoDI()  # 标签数字化
    bp_data.Stand()   # 标准化
    data_bp = bp_data.Kfold()  # 分割数据

    # 开始建立Blending第一层的模型
    one_stacking = MODELONE(bp_data.fenmu, bp_data.cha)
    # 随机森林
    one_stacking.RF_First(data_rf)
    # AdaBoost
    one_stacking.Adaboost_First(data_rf)
    # GBDT
    one_stacking.GBDT_First(data_rf)
    # LightGBM
    one_stacking.LightGBM_First(data_rf)
    # XGBoost
    one_stacking.XGBoost_First(data_rf)
    # CatBoost
    one_stacking.CatBoost_First(data_cat, cat_data.catsign)

    # BPnn
    signi, gir, trarm, adder = one_stacking.BPnn(data_bp)
    # 此处要下载最优的BPnn的模型，计算成本函数值，验证、预测数据集的预测结果
    # 训练完成后读取最优的参数，在计算最终的预测结果
    graph = tf.train.import_meta_graph("./pm25-%s.meta" % signi)
    ses = tf.Session()
    graph.restore(ses, tf.train.latest_checkpoint('./'))
    op_to_restore = tf.get_default_graph().get_tensor_by_name("Sigmoid_%s:0" % gir)  # 这个tensor的名称和激活函数有关系，需要去BP的程序中获得
    w1 = tf.get_default_graph().get_tensor_by_name("x_data:0")
    feed_dict = {w1: data_bp['predict'][:, :-1]}
    dgsio = ses.run(op_to_restore, feed_dict)

    preout = bp_data.fenmu * dgsio.T[0] + bp_data.cha

    # 在这里需要计算预测数据集的误差
    prerror = one_stacking.RMSE(preout, data_bp['predict'][:, -1] * bp_data.fenmu + bp_data.cha)

    one_stacking.error_dict['BPNN'] = [trarm * bp_data.fenmu, adder * bp_data.fenmu, prerror]  # 因为BPNN的误差是数据处理后的误差

    # 绘制第一层中各个模型的误差图
    Plot_RMSE_ONE_Blending(one_stacking.error_dict)

    feed_dict_add = {w1: data_bp['test'][:, :-1]}
    dgsio_add = ses.run(op_to_restore, feed_dict_add)
    adderout = bp_data.fenmu * dgsio_add.T[0] + bp_data.cha

    # 添加BPNN模型的验证、预测数据集的输出结果
    one_stacking.yanzhneg_pr.append(adderout)
    one_stacking.predi.append(preout)


    # 第二层的数据准备
    one_stacking.DataStru()
    data_two = one_stacking.datai

    # 第二层的数据处理
    erce_data = DATA(datadict=data_two)
    #  因为线性回归是可以取到最优值的，因此在里不在设置验证数据集
    erce_data.k = 0
    redata = erce_data.Kfold()

    # 第二层建模
    stacking_two = MODETWO(np.array(redata['train'][:, :-1]),
                           np.array([redata['train'][:, -1]]).T,
                           np.array(redata['predict'][:, :-1]))

    # 两种不同方法的线性回归:公式法
    outlist_Fo = stacking_two.Lin_Fo()

    # 输出最终的对比曲线
    recspre(redata['predict'][:, -1], outlist_Fo.T[0], title='公式法')


    # 两种不同方法的线性回归:梯度下降法
    outlist_Gr = stacking_two.Lin_Gr()


    # 输出最终的对比曲线
    recspre(redata['predict'][:, -1], outlist_Gr.T[0], title='梯度下降法')
