特征工程之数据预处理

本文代码及数据集来自《Python大数据分析与机器学习商业案例实战》

一、非数值类型数据处理

Get_dummies哑变量处理

哑变量也叫虚拟变量,通常取值为0或1,上面提到的将性别中的“男”和“女”分别转换成数字1和0就是哑变量最经典的应用。在Python中,通常利用pandas库中的get_dummies()函数进行哑变量处理,它不仅可以处理“男”和“女”这种只有两个分类的简单问题,还可以处理含有多个分类的问题。

# 非数值类型数据处理
# Get_dummies哑变量处理
import pandas as pd
df = pd.DataFrame({'房屋编号': [1, 2, 3, 4, 5], '朝向': ['东', '南', '西', '北', '南']})
print(df)

运行结果:
在这里插入图片描述

df = pd.get_dummies(df, columns=['朝向'])
print(df)

运行结果:
在这里插入图片描述
上表存在多重共线性(即根据3个朝向的数字就能判断第4个朝向的数字是0还是1),因此需要从新构造出来的4个哑变量中删去1个,假设删去“朝向_西”列,代码如下。

df = df.drop(columns='朝向_西')
print(df)

运行结果:
在这里插入图片描述
举例:转换三个分类变量并添加至dataframe中。

a = pd.get_dummies(df_heart['cp'], prefix = "cp")
b = pd.get_dummies(df_heart['thal'], prefix = "thal")
c = pd.get_dummies(df_heart['slope'], prefix = "slope")

frames = [df, a, b, c]
df = pd.concat(frames, axis = 1)
df = df.drop(columns = ['cp', 'thal', 'slope'])
df.head()

Label Encoding编号处理

# Label Encoding编号处理
import pandas as pd
df = pd.DataFrame({'编号': [1, 2, 3, 4, 5], '城市': ['北京', '上海', '广州', '深圳', '北京']})
print(df)

运行结果:
在这里插入图片描述

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
label = le.fit_transform(df['城市'])
print(label)

运行结果:[1 0 2 3 1]
可以看到,“北京”被转换成数字1,“上海”被转换成数字0,“广州”被转换成数字2,“深圳”被转换成数字3。通过如下代码可以用转换结果替换原来的列内容。

df['城市'] = label
print(df)

运行结果:
在这里插入图片描述
LabelEncoder()函数生成的数字是随机的,如果想按特定内容进行替换,可以使用replace()函数。

df['城市'].value_counts()

在使用replace()函数之前,先利用value_counts()函数查看“城市”列有哪些内容需要替换(因为有时数据量很大,通过人眼判断可能会遗漏某些内容)。
运行结果:
在这里插入图片描述
从上述结果可知,需要替换的是“北京”“上海”“深圳”“广州”这4个词。这里用replace()函数按“北上广深”的顺序进行数字编号。

df['城市'] = df['城市'].replace({'北京': 0, '上海': 1, '广州': 2, '深圳':3})
print(df)

运行结果:
在这里插入图片描述

二、重复值、缺失值及异常值处理

重复值

# 重复值处理
import pandas as pd
data = pd.DataFrame([[1, 2, 3], [1, 2, 3], [4, 5, 6]], columns=['c1', 'c2', 'c3'])
print(data)

#用duplicated()函数来查询重复的内容
print(data[data.duplicated()])

#若要统计重复行的数量,可以用sum()函数,其结果为1
print(data.duplicated().sum())

#发现有重复行时,可以用drop_duplicates()函数删除重复行
data = data.drop_duplicates()
print(data)

注意,drop_duplicates()函数并不改变原表格结构,所以需要进行重新赋值,或者在其中设置inplace参数为True。运行结果如下,此时已删除重复行。
在这里插入图片描述
若要按列进行去重,例如,c1列出现重复的内容,就将重复内容所在的一整行删除,代码如下。

data = pd.DataFrame([[1, 2, 3], [1, 2, 3], [4, 5, 6]], columns=['c1', 'c2', 'c3'])
data = data.drop_duplicates('c1')
print(data)

缺失值

# 缺失值处理
import numpy as np
import pandas as pd
data = pd.DataFrame([[1, np.nan, 3], [np.nan, 2, np.nan], [1, np.nan, 0]], columns=['c1', 'c2', 'c3'])
print(data)
print(data.isnull())

isnull()函数的作用是判断是否是空值,若是空值就赋予True,否则赋予False。运行结果:
在这里插入图片描述

# 对单列查看空值
data['c1'].isnull()

# 如果数据量较大,可以通过如下代码筛选出某列中内容为空值的行
data[data['c1'].isnull()]

# 只要含有空值的行都会被删除
a = data.dropna()
print(a)

# 如果一行中的非空值少于2个则删除该行
a = data.dropna(thresh=2)
print(a)

# 均值填补法,data.median()中位数填补
b = data.fillna(data.mean())
print(b)

# 用空值上方的值替换空值,如果上方的值不存在或也为空值,则不替换
c = data.fillna(method='pad')
print(c)

# 用空值下方的值来替换空值,如果下方的值不存在或也为空值,则不替换
d = data.fillna(method='backfill')
# 或 e = data.fillna(method='bfill')
print(d)

异常值处理:

# 异常值处理
data = pd.DataFrame({'c1': [3, 10, 5, 7, 1, 9, 69], 'c2': [15, 16, 14, 100, 19, 11, 8], 'c3': [20, 15, 18, 21, 120, 27, 29]}, columns=['c1', 'c2', 'c3'])
print(data)

# 1.利用箱型图观察
data.boxplot()  # 画箱型图

运行结果:
在这里插入图片描述

# 2.利用标准差检测
a = pd.DataFrame()
for i in data.columns:
    z = (data[i] - data[i].mean()) / data[i].std()
    a[i] = abs(z) > 2
print(a)

用mean()函数(获取均值)和std()函数(获取标准差)将每列数据进行Z-score标准化,运行结果:
在这里插入图片描述

三、数据标准化

对于以特征距离为算法基础的机器学习算法(如K近邻算法),数据标准化尤为重要。数据标准化有两种方法——min-max标准化及Z-score标准化。

  • min-max标准化(Min-Max Normalization)也称离差标准化,它利用原始数据的最大值和最小值把原始数据转换到[0,1]区间内;
  • Z-score标准化(Mean Normaliztion)也称均值归一化,通过原始数据的均值(mean)和标准差(standard deviation)对数据进行标准化。标准化后的数据符合标准正态分布,即均值为0,标准差为1。
# 数据标准化
import pandas as pd
X = pd.DataFrame({'酒精含量(%)': [50, 60, 40, 80, 90], '苹果酸含量(%)': [2, 1, 1, 3, 2]})
y = [0, 0, 0, 1, 1]
print(X) # 查看X

# min-max标准化
from sklearn.preprocessing import MinMaxScaler
X_new = MinMaxScaler().fit_transform(X)
print(X_new)  # 查看X_new

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_new, y, test_size=0.2, random_state=123)

# Z-score标准化
from sklearn.preprocessing import StandardScaler
X_new = StandardScaler().fit_transform(X)
print(X_new)  # 查看X_new

对于树模型则无须做数据标准化处理,因为数值缩放不影响分裂点位置,对树模型的结构不造成影响。因此,决策树模型及基于决策树模型的随机森林模型、AdaBoost模型、GBDT模型、XGBoost模型、LightGBM模型通常都不需要进行数据标准化处理,因为它们不关心变量的值,而是关心变量的分布和变量之间的条件概率。

四、数据分箱与特征筛选

数据分箱就是将一个连续型变量离散化,可分为等宽分箱和等深分箱。

  • 等宽分箱是指每个分箱的差值相等,以“年龄”这一连续型特征变量为例,其取值范围为0~100的连续数值,可以将“年龄”分为0~20、20~40、40~60、60~80、80~100共5个分箱,这5个分箱就可以当成离散的分类变量,每个分箱的年龄差相等(都相差20岁)。
  • 等深分箱是指每个分箱中的样本数一致,同样按“年龄”这一特征变量进行分箱,例如,500个样本分成5箱,那么每个分箱中都是100人,此时对应的5个分箱可能就是0~20、20~25、25~30、30~50、50~100,确保每个分箱中的人数一致。
# 1.数据分箱
import pandas as pd
data = pd.DataFrame([[22,1],[25,1],[20,0],[35,0],[32,1],[38,0],[50,0],[46,1]], columns=['年龄', '是否违约'])
print(data)

# 对“年龄”这一特征变量进行等宽分箱
data_cut = pd.cut(data['年龄'], 3)
print(data_cut)

运行结果:
在这里插入图片描述

# 2.统计各个分箱样本总数、坏样本数和好样本数
cut_group_all = data['是否违约'].groupby(data_cut).count()
cut_y = data['是否违约'].groupby(data_cut).sum()
cut_n = cut_group_all - cut_y
print(cut_group_all)

df = pd.DataFrame()  # 创建一个空DataFrame用来汇总数据
df['总数'] = cut_group_all
df['坏样本'] = cut_y
df['好样本'] = cut_n
print(df)

运行结果:
在这里插入图片描述
挑选入模变量需要考虑很多因素,其中最主要的衡量标准是变量的预测能力,对分类模型来说,即希望变量具有较好的特征区分度,可以较准确地将样本进行分类。对于决策树等树模型来说,可以通过基尼系数或信息增益来衡量变量的特征区分度,而对于逻辑回归等没有基尼系数等指标的模型而言,可以通过WOE值和IV值进行变量选择。
在这里插入图片描述
在这里插入图片描述

# 3.统计各分箱中坏样本比率和好样本比率
df['坏样本%'] = df['坏样本'] / df['坏样本'].sum()
df['好样本%'] = df['好样本'] / df['好样本'].sum()
print(df)

# 4.计算WOE值
import numpy as np
df['WOE'] = np.log(df['坏样本%'] / df['好样本%'])
print(df)
df = df.replace({'WOE': {np.inf: 0, -np.inf: 0}}) #将无穷大替换为0

# 5.计算IV值
df['IV'] = df['WOE'] * (df['坏样本%'] - df['好样本%'])
print(df)
iv = df['IV'].sum()
print(iv)

运行结果:
在这里插入图片描述
实战案例:

# 案例实战:客户流失预警模型的IV值计算
import pandas as pd
import numpy as np
def cal_iv(data, cut_num, feature, target):
    # 1.数据分箱
    data_cut = pd.cut(data[feature], cut_num)
    # 2.统计各个分箱样本总数、坏样本数和好样本数
    cut_group_all = data[target].groupby(data_cut).count()  # 总客户数
    cut_y = data[target].groupby(data_cut).sum()  # 坏样本数
    cut_n = cut_group_all - cut_y  # 好样本数
    df = pd.DataFrame()  # 创建一个空DataFrame用来汇总数据
    df['总数'] = cut_group_all
    df['坏样本'] = cut_y
    df['好样本'] = cut_n
    # 3.统计坏样本%和好样本%
    df['坏样本%'] = df['坏样本'] / df['坏样本'].sum()
    df['好样本%'] = df['好样本'] / df['好样本'].sum()
    # 4.计算WOE值
    df['WOE'] = np.log(df['坏样本%'] / df['好样本%'])
    df = df.replace({'WOE': {np.inf: 0, -np.inf: 0}}) 
    # 5.计算各个分箱的IV值
    df['IV'] = df['WOE'] * (df['坏样本%'] - df['好样本%'])
    # 6.汇总各个分箱的IV值,获得特征变量的IV值
    iv = df['IV'].sum()
    print(iv)

data = pd.read_excel('股票客户流失.xlsx')
data.head()

cal_iv(data, 4, '账户资金(元)', '是否流失')

for i in data.columns[:-1]:
    print(i + '的IV值为:')
    cal_iv(data, 4, i, '是否流失')  # 调用函数

第1个参数data就是刚刚读取的股票客户流失数据;设置第2个参数cut_num为4,即将数据分为4箱;设置第3个参数feature为’账户资金(元)’,即要计算IV值的特征变量;设置第4个参数target为’是否流失’,即原始表格中的目标变量。通过for循环可以快速计算出所有特征变量的IV值,运行结果:
在这里插入图片描述
可得出结论:“本券商使用时长(年)”的信息量最大,而“账户资金(元)”的信息量最小,预测能力最低。

五、多重共线性的分析与处理

对多元线性回归模型Y=k0+k1X1+k2X2+…+knXn而言,如果特征变量X1、X2、X3…之间存在高度线性相关关系,则称为多重共线性(multicollinearity)。例如,X1=1-X2,此时X1与X2存在高度的线性相关关系,则认为该模型存在多重共线性,需要删去X1和X2中的一个变量。
在实际应用中,多重共线性会带来如下不利影响:线性回归估计式变得不确定或不精确;线性回归估计式方差变得很大,标准误差增大;当多重共线性严重时,甚至可能使估计的回归系数符号相反,得出错误的结论;削弱特征变量的特征重要性。
这里主要讲解两种判别方法——相关系数判断和方差膨胀系数法(VIF检验)。

# 多重共线性的分析与处理
import pandas as pd
df = pd.read_excel('数据.xlsx')
df.head()

X = df.drop(columns='Y')
Y = df['Y']

# 1.相关系数判断
print(X.corr())

运行结果:
在这里插入图片描述
第1行第2列的相关系数0.99,表示的就是特征变量X1和特征变量X2的相关系数,可以看到它们的相关性还是非常强的,有理由相信它们会导致多重共线性,因此需要删去其中一个特征变量。
相关系数判断使用起来非常简单,结论也比较清晰,不过它有一个缺点:简单相关系数只是多重共线性的充分条件,不是必要条件。在有多个特征变量时,相关系数较小的特征变量间也可能存在较严重的多重共线性。为了更加严谨,实战中还经常用到下面要讲解的方差膨胀系数法(VIF检验)。

# 2.方差膨胀因子法(VIF检验)
from statsmodels.stats.outliers_influence import variance_inflation_factor
vif = [variance_inflation_factor(X.values, X.columns.get_loc(i)) for i in X.columns]
print(vif)

vif = []
for i in X.columns:  # i对应的是每一列的列名
    vif.append(variance_inflation_factor(X.values, X.columns.get_loc(i)))
print(vif)

运行结果都为[259.6430487184967, 257.6315718292196, 1.302330632715429]。从上述计算结果也可以看出,前2个VIF值均大于100,暗示多重共线性十分严重,应该删掉X1或X2。下面删掉X2再进行一次回归和VIF检验,看看结果的变化。

X = df[['X1', 'X3']]
Y = df['Y']

from statsmodels.stats.outliers_influence import variance_inflation_factor
vif = [variance_inflation_factor(X.values, X.columns.get_loc(i)) for i in X.columns]
print(vif)

运行结果为[1.289349054516766, 1.289349054516766],不存在多重共线性。

六、过采样和欠采样

建立模型时,可能会遇到正负样本比例极度不均衡的情况。例如,建立信用违约模型时,违约样本的比例远小于不违约样本的比例,此时模型会花更多精力去拟合不违约样本,但实际上找出违约样本更为重要。这会导致模型可能在训练集上表现良好,但测试时表现不佳。为了改善样本比例不均衡的问题,可以使用过采样和欠采样的方法。

过采样

过采样的方法有随机过采样和SMOTE法过采样。

  • 随机过采样
    随机过采样是从100个违约样本中随机抽取旧样本作为一个新样本,共反复抽取900次,然后和原来的100个旧样本组合成新的1000个违约样本,和1000个不违约样本一起构成新的训练集。因为随机过采样重复地选取了违约样本,所以有可能造成对违约样本的过拟合。

  • SMOTE法过采样
    SMOTE法过采样即合成少数类过采样技术,它是一种针对随机过采样容易导致过拟合问题的改进方案。假设对少数类进行4倍过采样,通过下图来讲解SMOTE法的原理。
    在这里插入图片描述

# 过采样
import pandas as pd
data = pd.read_excel("信用卡数据.xlsx")
data.head()

X = data.drop(columns='分类')
y = data['分类']

from collections import Counter
Counter(y) #对目标变量进行计数

# 不违约样本数为1000,远远大于违约样本数100

# (1)随机过采样
from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(random_state=0)
X_oversampled, y_oversampled = ros.fit_resample(X, y)

print(Counter(y_oversampled))
# 违约样本数从100上升至1000,与不违约样本数相同,证明随机过采样有效。

# (2)SMOTE过采样
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=0)
X_smotesampled, y_smotesampled = smote.fit_resample(X, y)

print(Counter(y_smotesampled))

运行结果都为:Counter({0: 1000, 1: 1000})

欠采样

欠采样是从1000个不违约样本中随机选取100个样本,和100个违约样本一起构成新的训练集。欠采样抛弃了大部分不违约样本,在搭建模型时有可能产生欠拟合。

# 欠采样
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
X_undersampled, y_undersampled = rus.fit_resample(X, y)

print(Counter(y_undersampled))

运行结果:Counter({0: 100, 1: 100})
在实战中处理样本不均衡问题时,如果样本数据量不大,通常使用过采样,因为这样能更好地利用数据,不会像欠采样那样很多数据都没有使用到;如果数据量充足,则过采样和欠采样都可以考虑使用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值