如何用xgboost模型填补缺失值?
演示用数据如下:
代码文件如下:
代码如下:
# ### 导入库 import pandas as pd import numpy as np import xgboost as xgb from sklearn.base import BaseEstimator, TransformerMixin #from sklearn.ensemble import BaggingClassifier, RandomForestClassifier #pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple pd.options.display.max_columns = None # 显示所有列 pd.set_option('display.float_format', lambda x: '%.3f' % x) # 取消科学计数法 #定义一个函数来判断一个变量到底是什么类型 def get_kind(x: pd.Series, diff_limit: int = 10): x = x.astype('str') x = x.str.extract(r'(^(\-|)(?=.*\d)\d*(?:\.\d*)?$)')[0] x.dropna(inplace=True) if x.nunique() > diff_limit: kind = 'numeric' else: kind = 'categorical' return kind #1.先将这个变量x转化为字符型 #2.对这个字符串变量x进行正则表达式的匹配,如果没有匹配成功,则返回缺失值,并且直接作用于x #3.删除变量x里面的缺失值,并且直接作用于x #4.数一数变量x里面有多少个唯一值, #5.将唯一值的个数和diff_limit进行比较 #6.如果大于diff_limit则将其判定为numeric类型,否则将其判定为categorical类型。 #7.也就是categorical类型变量的取值是不能太多的,不能超过我们设定的阀值10 class wrong_value_fillna(BaseEstimator, TransformerMixin): def __init__(self, num_list: list = None, cate_list: list = None, wrong_value: list = None, diff_num: int = 10): self.num_list = num_list self.cate_list = cate_list self.diff_num = diff_num self.wrong_value = wrong_value def fit(self, X, y=None): X = X.copy() if self.num_list is None: self.num_list = [] for col in X.columns: kind = get_kind(x=X[col], diff_limit=self.diff_num) if kind == 'numeric': self.num_list.append(col) if self.cate_list is None: self.cate_list = [] for col in X.columns: kind = get_kind(x=X[col], diff_limit=self.diff_num) if kind == 'categorical': self.cate_list.append(col) return self def transform(self, X): X = X.copy() X.replace(self.wrong_value, np.nan, inplace=True) for col in X.columns: if get_kind(X[col]) == 'numeric': X[col] = X[col].astype('float') else: X[col] = X[col].astype('object') return X class xgb_fill(BaseEstimator, TransformerMixin): def __init__(self, num_list: list = None, cate_list: list = None, diff_num: int = 10, #注意这个diff_num的设定非常重要,这个值最好与get_kind函数里面的设定值保持一致,否则可能出现漏洞 #比如这里设定为了8,而get_kind里面设定为了10,那么当判断一个变量是否要处理为object变量的时候 #就可能出现问题,比如这个变量的不重复值有9个,到了get_kind函数那里,将其处理为了object变量, #在这里就会被处理成float变量 random_state: int = 0): self.num_list = num_list self.cate_list = cate_list self.diff_num = diff_num self.random_state = random_state self.xgb_cla_dict = {} self.xgb_reg_dict = {} def fit(self, X, y=None): from tqdm import tqdm X = X.copy() #1.先找到numberic变量列表self.num_list #2.再找到categorical变量列表self.cate_list if self.num_list is None: self.num_list = [] for col in X.columns: kind = get_kind(x=X[col], diff_limit=self.diff_num) if kind == 'numeric': self.num_list.append(col) if self.cate_list is None: self.cate_list = [] for col in X.columns: kind = get_kind(x=X[col], diff_limit=self.diff_num) if kind == 'categorical': self.cate_list.append(col) #对categorical变量进行处理 for col in tqdm(self.cate_list): file = X.copy() if file[col].isnull().any(): #如果变量有缺失值的话,则用XGBClassifier分类器进行填补,填补后的变量放在了xgb_cla_dict里面 df = pd.get_dummies(file, columns=[i for i in self.cate_list if i != col], prefix=[i for i in self.cate_list if i != col], dummy_na=True) #找出col列没有缺失值的行,生成not_null数据框 not_null = df.dropna(subset=[col]) #将not_null中col以外的列设定为x_,将col列设定为y_ x_ = not_null.drop([col], axis=1) y_ = not_null[col] #实例化分类器 xgb_cla = xgb.XGBClassifier(random_state=self.random_state) #xgb_cla是一个针对col列预测的分类器 #用fit方法拟合这个分类器的参数 xgb_cla.fit(x_, y_) #将拟合好的分类器传给字典self.xgb_cla_dict[col] self.xgb_cla_dict[col] = xgb_cla #对numberic变量进行处理 for col in tqdm(self.num_list): file = X.copy() if file[col].isnull().any(): #如果变量有缺失值的话,则用XGBRegressor回归器进行填补,填补后的变量放在了xgb_reg_dict里面 df = pd.get_dummies(file, columns=self.cate_list, dummy_na=True, prefix=self.cate_list) not_null = df.dropna(subset=[col]) x_ = not_null.drop([col], axis=1) y_ = not_null[col] xgb_reg = xgb.XGBRegressor(random_state=self.random_state, objective='reg:squarederror') xgb_reg.fit(x_, y_) self.xgb_reg_dict[col] = xgb_reg print('fit xgb fill the Na success!') return self def transform(self, X): print("cate_list1",self.cate_list,sep="************************") X = X.copy() from tqdm import tqdm print("******************",self.cate_list) for col in tqdm(self.cate_list): #对object变量进行填补 print("cate_list",col,sep="************************") file = X.copy() if file[col].isnull().any(): print(col,"有缺失") #如果col列中有缺失值的话则进行如下处理 #1将数据框file里面的全部object变量都转化为虚拟变量,并且把缺失值也看成一个类别。 #2把数据框保存为df df = pd.get_dummies(file, columns=[i for i in self.cate_list if i != col], prefix=[i for i in self.cate_list if i != col], dummy_na=True) #删除col变量存在缺失值的行,保留col变量不存在缺失值的行,保存为not_null not_null = df.dropna(subset=[col]) #把col变量存在缺失值的行保存为数据框null null = df.drop(not_null.index) #根据数据框null的除col列的其他列的值对col列进行插补 #开始调用实例的predict方法 #如果调用predict方法就会先调用fit方法 #接下来可以转到上面的fit方法定义那里看一下 #完成fit之后数据就会插补成功了,并且将插补后的结果放到了self.xgb_cla_dict里面 #然后用self.xgb_cla_dict里面的值对数据框null里面的col列进行赋值。 print(null.drop([col],axis=1).dtypes) null[col] = self.xgb_cla_dict[col].predict(null.drop([col], axis=1)) #然后对X里面的col列进行赋值 X[col] = pd.concat([null, not_null], axis=0)[col] else: #如果col列中没有缺失值的话则进行如下处理 X[col] = file[col] #对float64变量进行填补 for col in tqdm(self.num_list): print("num_list",col,sep="************************") file = X.copy() if file[col].isnull().any(): df = pd.get_dummies(file, columns=self.cate_list, dummy_na=True, prefix=self.cate_list) not_null = df.dropna(subset=[col]) null = df.drop(not_null.index) null[col] = self.xgb_reg_dict[col].predict(null.drop([col], axis=1)) X[col] = pd.concat([null, not_null], axis=0)[col] else: X[col] = file[col] print('transform xgb fill the NA success!') return X wvf = wrong_value_fillna(wrong_value=['.', '?']) data = pd.read_excel(r"C:\Users\Administrator\Desktop\1614829496_756660.xlsx") #我们看一下现在data数据里面的变量类型 #其中类型为object的变量有age,sex,region, #float64:'B0003', 'B0004', 'B0005', 'B0006', 'B0008', 'B0010', 'B0011', 'B0014', 'B0015', 'B0016', 'B0018', #int64:'B0012','B0017', 'B0124', 'Target' data = wvf.transform(data) #我们看一下经过转化后的data数据里面的变量类型 #其中类型为object的变量有age,sex,region, #float64:'B0003', 'B0005', 'B0006', 'B0010', 'B0012','B0015', 'B0016', 'B0018', 'B0124', #object:'B0004','B0008','B0011','B0014','B0017','Target' #将数据转化好后进行数据拆分 x1_ = data.drop(['Target'], axis=1) y1_ = data['Target'].values #实例化 xgbf = xgb_fill() #调用fit_transform方法进行插补 x_=xgbf.fit_transform(x1_) #这个就是插补之后的结果
希望大家运行愉快!