任何对机器学习 ( ML )感兴趣的人肯定都会听说,一个数据科学家或者机器学习工程师 80%的时间都花在了准备数据上,剩下的 20%都花在了构建和评估模型上。准备数据花费的大量时间被认为是构建良好模型的投资。这是一个简单的模型,使用优秀的数据集构建的模型超过了使用糟糕的数据集开发的复杂模型。在现实生活中,找到一个可靠的数据集是非常困难的。我们必须创造和培育好的数据。你一定在想,如何创造好的数据?这是我们将在本章中发现的东西。我们将研究创建优秀且可行的数据集所需的一切。理论上,好与我们手头的任务以及我们如何感知和消费数据有关。在本章中,我们将引导您完成以下主题:
对于每一个主题,我们将讨论在数据集中遇到的不同类型的数据上可以做的各种事情。我们还将考虑一些自动化的开源特性准备工具,这些工具在用 Python 准备数据时会派上用场。
让我们从数据转换的第一个主题开始。
所有代码示例都可以在 GitHub 的Chapter 03
文件夹中找到。
让我们假设我们正在研究一个 ML 模型,其任务是预测员工流失。基于我们对业务的理解,我们可能会包含一些创建一个好模型所必需的相关变量。另一方面,我们可能会选择丢弃一些没有相关信息的功能,比如EmployeeID
。
Identifying the ID
columns is known as identifier detection. Identifier
columns don't add any information to a model in pattern detection and prediction. So, identifier
column detection functionality can be a part of the AutoML
package and we use it based on the algorithm or a task dependency.
一旦我们决定了要使用的领域,我们就可以探索数据来转换某些有助于学习过程的特征。转换为数据增加了一些经验,这有利于 ML 模型。例如,员工起始日期为 2018 年 2 月 11 日,该日期不提供任何信息。但是,如果我们将此功能转换为四个属性—日期、日、月和年,它将为模型构建练习增加价值。
特征变换也很大程度上取决于所使用的最大似然算法的类型。大体上,我们可以将监督模型分为两类——基于树的模型和非基于树的模型。
Tree-based models handle the abnormality in most features by themselves. Non-tree-based models, such as nearest neighbors and linear regression, improve their predictive power when we do feature transformations.
理论解释已经够多了。现在,让我们直接跳到一些可以对我们经常遇到的各种数据类型执行的功能转换。我们将首先从数字特征开始。
以下是一些最广泛使用的数值数据转换方法:
这里显示的技术可以嵌入到函数中,这些函数可以直接应用于在 AutoML 流水线中转换数值数据。
标准化和标准化是行业内使用的缩放技术的两个术语。这两种技术都确保模型中使用的数字特征在其表示中具有同等的权重。大多数时候,人们交替使用标准化和规范化。虽然两者都是缩放技术,但两者之间只有一线之差。
Standardization assumes the data to be normally distributed. It rescales the data to mean as zero and standard deviation as one. Normalization is a scaling technique that assumes no prior distribution of the data. In this technique, the numerical data is rescaled to a fixed range either: 0 to 1, -1 to +1, and so on.
以下是一些广泛使用的标准化或规范化数据的技术:
Scikit-learn 提供了各种方法来标准化和规范化数据。让我们首先使用以下代码片段加载HR
损耗数据集:
%matplotlib inline
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
前面代码的输出显示了数据集的各种属性以及一些数据点:
让我们使用以下代码来分析数据集的分布:
hr_data[hr_data.dtypes[(hr_data.dtypes=="float64")|(hr_data.dtypes=="int64")].index.values].hist(figsize=[11,11])
前面代码的输出是几个不同数值属性的直方图:
例如,让我们使用sklearn.preprocessing
模块中的StandardScaler
来标准化satisfaction_level
列的值。一旦我们导入了方法,我们首先需要创建一个StandardScaler
类的实例。接下来,我们需要使用fit_transform
方法来拟合和转换我们需要标准化的列。在下面的例子中,我们使用satisfaction_level
属性进行标准化:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
hr_data_scaler=scaler.fit_transform(hr_data[['satisfaction_level']])
hr_data_scaler_df = pd.DataFrame(hr_data_scaler)
hr_data_scaler_df.max()
hr_data_scaler_df[hr_data_scaler_df.dtypes[(hr_data_scaler_df.dtypes=="float64")|(hr_data_scaler_df.dtypes=="int64")].index.values].hist(figsize=[11,11])
一旦我们执行了前面的代码,我们就可以再次查看satisfication_level
实例直方图,并观察到值在 -2 到 1.5 之间是标准化的:
MinMaxScaler
方法可在 scikit-learn 的preprocessing
模块中获得。在本例中,我们对HR
损耗数据集的四个属性进行了规范化— average_monthly_hours
、last_evaluation
、number_project
和satisfaction_level
。我们遵循类似于StandardScaler
的流程。我们首先需要从sklearn.preprocessing
模块导入MinMaxScaler
,并创建一个MinMaxScaler
类的实例。
接下来,我们需要使用fit_transform
方法来拟合和变换列:
from sklearn.preprocessing import MinMaxScaler
minmax=MinMaxScaler()
hr_data_minmax=minmax.fit_transform(hr_data[[ 'average_montly_hours',
'last_evaluation', 'number_project', 'satisfaction_level']])
hr_data_minmax_df = pd.DataFrame(hr_data_minmax)
hr_data_minmax_df.min()
hr_data_minmax_df.max()
hr_data_minmax_df[hr_data_minmax_df.dtypes[(hr_data_minmax_df.dtypes=="float64")|(hr_data_minmax_df.dtypes=="int64")].index.values].hist(figsize=[11,11])
以下直方图描述了转换后的四个属性的值分布在 0 和 1 之间:
我们经常遇到数据集,其中并非所有的值都适用于特定的变量/属性。出现这种情况有几个原因,例如调查中被忽略的问题、打字错误、设备故障等等。在数据挖掘项目中会遇到这些缺失的值,处理这些值是必不可少的。
缺失值插补占据了数据科学家的大部分时间。我们可以通过各种方法来估算缺失值。决定性的因素是当归因于这些不可用的值时使用什么。决定什么时候用什么来输入缺失值的过程是一种天赋,来自于处理数据的经验。有时直接移除这些值更好,对于某些赋值,最好使用高级挖掘技术来估算这些值。
因此,出现了两个最重要的问题:
我们认为它来自于处理缺失值的经验;最好的开始方法是进行比较研究,对数据应用不同的插补方法,然后选择适当的技术,用偏差最小的估计值分配空值。
一般来说,当我们遇到一个丢失的值时,我们会首先尝试检查一个值为什么会丢失。是因为收集数据时的一些问题,还是罪魁祸首是数据源本身?最好是从根源上解决问题,而不是直接将价值强加于人。这是一个理想的情况,而且,大多数时候,这是不可能的。例如,如果我们正在处理一个调查数据集,而一些受访者选择不透露具体信息,在这种情况下,输入值可能是不可避免的。
因此,在我们开始输入值之前,我们可以使用以下准则:
我们可以将此记为缺失值插补的 IAD 规则(即调查、分析和决定)。以下是我们处理缺失值的一些可用方法:
我们将再次使用HR
磨损数据集来演示缺失值处理。让我们首先加载数据集,并查看数据集中的空值数量:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
print('Nulls in the data set' ,hr_data.isnull().sum())
从下面的输出中我们可以看到,数据集相对干净,只是promotion_last_5years
有一些缺失值。因此,我们将把一些缺失的值合成到一些列中:
我们使用以下代码片段将列promotion_last_5years
、average_montly_hours
和number_project
中的一些值替换为空值:
#As there are no null introduce some nulls by replacing 0 in promotion_last_5years with NaN
hr_data[['promotion_last_5years']] = hr_data[[ 'promotion_last_5years']].replace(0, np.NaN)
#As there are no null introduce some nulls by replacing 262 in promotion_last_5years with NaN
hr_data[['average_montly_hours']] = hr_data[[ 'average_montly_hours']].replace(262, np.NaN)
#Replace 2 in number_project with NaN
hr_data[['number_project']] = hr_data[[ 'number_project']].replace(2, np.NaN)
print('Nulls in the data set', hr_data.isnull().sum())
在本练习之后,为这三列插入了一些空值,我们可以从以下结果中看到:
在我们删除行之前,让我们首先创建一个hr_data
的副本,这样我们就不会替换原始数据集,该数据集将用于演示其他缺失值插补方法。接下来,我们使用dropna
方法删除缺少值的行:
#Remove rows
hr_data_1 = hr_data.copy()
print('Shape of the data set before removing nulls ', hr_data_1.shape)
# drop rows with missing values
hr_data_1.dropna(inplace=True)
# summarize the number of rows and columns in the dataset
print('Shape of the data set after removing nulls ',hr_data_1.shape)
我们可以观察到这个练习后的行数从14999
减少到278
。删除行必须小心使用。由于promotion_last_5years
有大约 14,680 个缺失值,14,680 个记录被完全删除:
我们可以使用fillna
方法将缺失值替换为常量值,如-999
。下面的代码片段演示了该方法的使用:
#Mark global constant for missing values
hr_data_3 = hr_data.copy()
# fill missing values with -999
hr_data_3.fillna(-999, inplace=True)
# count the number of NaN values in each column
print(hr_data_3.isnull().sum())
print(hr_data_3.head())
我们可以从以下结果中看到,所有缺失的值都被-999
值替换:
我们可以使用相同的fillna
方法,以均值函数为参数,用均值替换缺失值。下面的代码演示了它的用法:
#Replace mean for missing values
hr_data_2 = hr_data.copy()
# fill missing values with mean column values
hr_data_2.fillna(hr_data_2.mean(), inplace=True)
# count the number of NaN values in each column
print(hr_data_2.isnull().sum())
print(hr_data_2.head())
我们可以从下面的输出中看到,丢失的值被替换为每个属性的平均值:
正如我们演示其他插补方法一样,让我们首先复制原始数据,并创建新的列来指示被插补的属性和值。下面的代码首先为那些缺少值的属性创建新的列,并将_was_missing
追加到原始列名中。
接下来,丢失的值被替换为全局常数-999
。虽然我们使用了全局常数插补方法,但您可以使用任何插补方法来插补缺失的值:
# make copy to avoid changing original data (when Imputing)
hr_data_4 = hr_data.copy()
# make new columns indicating what is imputed
cols_with_missing = (col for col in hr_data_4.columns
if hr_data_4[col].isnull().any())
for col in cols_with_missing:
hr_data_4[col + '_was_missing'] = hr_data_4[col].isnull()
hr_data_4.fillna(-999, inplace=True)
hr_data_4.head()
我们可以从以下结果中看到,创建了新的列,表明属性中是否存在缺失值:
在 Python 中,fancyimpute
是一个提供高级数据挖掘选项来估算缺失值的库。这是我们最常使用的东西,所以我们想演示一下这个包。Python 中可能还有其他一些库也可以完成类似的任务。首先,我们需要使用以下命令安装fancyimpute
库。这必须在命令提示符下执行:
pip install fancyimpute
安装完成后,我们可以返回 Jupyter 笔记本,从fancyimpute
库中导入KNN
方法。KNN 插补方法只适用于数值。因此,我们首先从hr_data
集合中只选择数字列。接下来,创建一个 k 等于3
的 KNN 模型,并为数字属性替换缺失的值:
from fancyimpute import KNN
hr_data_5 = hr_data.copy()
hr_numeric = hr_data_5.select_dtypes(include=[np.float])
hr_numeric = pd.DataFrame(KNN(3).complete(hr_numeric))
hr_numeric.columns = hr_numeric.columns
hr_numeric.index = hr_numeric.index
hr_numeric.head()
fancyimpute
库使用 TensorFlow 后端,执行起来需要一些时间。一旦执行完成,我们可以向下滚动查看插补结果,如以下结果截图所示:
异常值是不符合整体数据模式的极端值。它们通常远离其他观测结果,扭曲了数据的总体分布。在模型构建过程中包含它们可能会导致错误的结果。适当地对待他们是非常重要的。异常值有两种类型——单变量和多变量。
顾名思义,单变量异常值基于数据集中的单个属性。单变量异常值是通过使用箱线图和查看属性值的分布来发现的。然而,当我们构建 AutoML 流水线时,我们没有权限可视化数据分布。相反,AutoML 系统应该能够检测异常值并自行处理它们。
因此,我们可以部署以下三种方法中的任何一种来自动进行单变量异常检测和处理:
让我们创建一个虚拟异常数据集来演示异常检测和处理方法:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
number_of_samples = 200
outlier_perc = 0.1
number_of_outliers = number_of_samples - int ( (1-outlier_perc) * number_of_samples )
# Normal Data
normal_data = np.random.randn(int ( (1-outlier_perc) * number_of_samples ),1)
# Inject Outlier data
outliers = np.random.uniform(low=-9,high=9,size=(number_of_outliers,1))
# Final data set
final_data = np.r_[normal_data,outliers]
让我们使用以下代码绘制新创建的数据集:
#Check data
plt.cla()
plt.figure(1)
plt.title("Dummy Data set")
plt.scatter(range(len(final_data)),final_data,c='b')
从下图中我们可以看出,在数据集的末尾有一些异常值:
我们还可以使用下面的代码生成一个方框图来观察异常值。箱线图,也称为箱线图和触须图,是一种基于五个数字汇总来表示数据分布的方法:最小值、第一个四分位数、中值、第三个四分位数和最大值。任何低于最小值和高于最大值的都被认为是异常值:
## Detect Outlier###
plt.boxplot(final_data)
从得到的方框图中,我们可以观察到存在一些超出最大和最小标记的值。因此,我们可以假设我们成功地创建了一些异常值:
去除异常值的一种方法是过滤高于最大值和低于最小值的值。要完成这个任务,首先需要计算四分位数区间 ( IQR )。
四分位数之间的范围是数据集中可变性或分布的度量。它是通过将数据集分成四分位数来计算的。四分位数根据我们之前研究的五个数字的汇总将数据集分成四份,即最小值、第一个四分位数、第二个四分位数、第三个四分位数和最大值。第二个四分位数是排序数据集的中值;第一个四分位数是排序数据集前半部分的中间值,第三个四分位数是排序数据集后半部分的中间值。
四分位数范围是第三个四分位数(quartile75
或Q3
)与第一个四分位数(quartile25
或Q1
)之间的差值。
我们使用以下代码计算 Python 中的IQR
:
## IQR Method Outlier Detection and Removal(filter) ##
quartile75, quartile25 = np.percentile(final_data, [75 ,25])
## Inter Quartile Range ##
IQR = quartile75 - quartile25
print("IQR",IQR)
从下面的代码中我们可以看到数据集的IQR
是1.49
:
我们可以过滤高于最大值和低于最小值的值。最小值可用公式计算:quartile25 - (IQR * 1.5)
最大值为quartile75 + (IQR*1.5)
。
The method to calculate maximum and minimum values is based on Turkey Fences, which was developed by John Turkey. The value 1.5
indicates about 1% of measurements as outliers and is synonymous with the 3σ principle, which is practiced as a bound in many statistical tests. We can use any value other than 1.5
, which is at our discretion. However, the bound may increase or decrease the number of outliers in the dataset.
我们使用下面的 Python 代码来计算数据集的Max
和Min
值:
## Calculate Min and Max values ##
min_value = quartile25 - (IQR*1.5)
max_value = quartile75 + (IQR*1.5)
print("Max", max_value)
print("Min", min_value)
在执行前面的代码后,我们注意到以下输出。最大值和最小值分别为2.94
和-3.03
:
接下来,我们使用以下代码过滤低于min_value
和高于max_value
的值:
filtered_values = final_data.copy()
filtered_values[ filtered_values< min_value] = np.nan
filtered_values[ filtered_values > max_value] = np.nan
#Check filtered data
plt.cla()
plt.figure(1)
plt.title("IQR Filtered Dummy Data set")
plt.scatter(range(len(filtered_values)),filtered_values,c='b')
代码执行成功完成后,我们可以看到离群值被消除了,数据集看起来远比之前的数据集好:
Winsorizing 是用较小的绝对值代替极值的方法。它对数值列中的非空值进行排序,计算尾值,然后用定义的参数替换尾值。
我们可以使用 SciPy 包中的winsorize
方法来处理异常值。SciPy 是一个 Python 库,是科学和技术计算领域开源 Python 贡献的集合。它拥有大量的统计计算模块、线性代数、优化、信号和图像处理模块以及更多模块。
一旦导入winsorize
方法,我们需要将data
和limit
参数传递给函数。尾值的计算和替换是通过这种方法进行的,并生成结果无异常值数据:
##### Winsorization ####
from scipy.stats.mstats import winsorize
import statsmodels.api as sm
limit = 0.15
winsorized_data = winsorize(final_data,limits=limit)
#Check winsorized data
plt.cla()
plt.figure(1)
plt.title("Winsorized Dummy Data set")
plt.scatter(range(len(winsorized_data)),winsorized_data,c='b')
从下面的图中我们可以观察到,极值已经被消除,数据看起来没有异常值:
修剪与 winsorizing 相同,只是尾部值被裁剪掉了。
stats
库中的trimboth
方法从数据的两端分割数据集。final_data
和0.1
的极限作为参数传递给函数,从两端修剪 10%的数据:
### Trimming Outliers ###
from scipy import stats
trimmed_data = stats.trimboth(final_data, 0.1)
#Check trimmed data
plt.cla()
plt.figure(1)
plt.title("Trimmed Dummy Data set")
plt.scatter(range(len(trimmed_data)),trimmed_data,c='b')
我们可以从下面的结果图中观察到,极值被截断,不再存在于数据集中:
多元异常值是至少两个变量的极端分数的混合。单变量离群点检测方法非常适合处理一维数据,但是当我们越过一维时,使用这些方法检测离群点就变得具有挑战性。多元异常检测方法也是异常检测方法的一种形式。一类 SVM、局部异常因子 ( LOF )和IsolationForest
等技术是检测多元异常值的有用方法。
我们使用以下IsolationForest
代码描述HR
磨损数据集上的多元异常检测。我们需要从sklearn.ensemble
包装中进口IsolationForest
。接下来,我们加载数据,将分类变量转换为一个热编码变量,并使用估计量的数量调用IsolationForest
方法:
##Isolation forest
import numpy as np
import pandas as pd
from sklearn.ensemble import IsolationForest
hr_data = pd.read_csv('data/hr.csv', header=0)
print('Total number of records ',hr_data.shape)
hr_data = hr_data.dropna()
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
clf = IsolationForest(n_estimators=100)
然后,我们将IsolationForest
实例(clf
)拟合到数据,并使用predict
方法预测异常值。异常值由-1
表示,非异常值(也称为新数据)由1
表示:
clf.fit(data_trnsf)
y_pred_train = clf.predict(data_trnsf)
data_trnsf['outlier'] = y_pred_train
print('Number of outliers ',data_trnsf.loc[data_trnsf['outlier'] == -1].shape)
print('Number of non outliers ',data_trnsf.loc[data_trnsf['outlier'] == 1].shape)
从下面的输出中我们可以看到,模型能够从14999
记录的数据集中识别出大约1500
个异常值:
宁滨是一个将连续数值分组到更小数量的桶或箱中的过程。这是离散化连续数据值的重要技术。许多算法如朴素贝叶斯和 Apriori 在离散数据集上运行良好,因此有必要将连续数据转换为离散值。
有各种类型的宁滨方法:
其中 w 为箱的宽度, maxval 为数据中的最大值, minval 为数据中的最小值, k 为所需的箱数
区间边界形成如下:
在这两种方法中, k 的值是根据我们的要求以及试错过程确定的。
除了这两种方法之外,我们还可以明确提到创建面元的切割点。当我们知道数据并希望它以某种格式入库时,这非常有帮助。以下代码是基于预定义切割点执行宁滨的函数:
#Bin Values:
def bins(column, cutpoints, labels=None):
#Define min and max values:
min_val = column.min()
max_val = column.max()
print('Minimum value ',min_val)
print(' Maximum Value ',max_val)
break_points = [min_val] + cut_points + [max_val]
if not labels:
labels = range(len(cut_points)+1)
#Create bins using the cut function in pandas
column_bin = pd.cut(column,bins=break_points,labels=labels,include_lowest=True)
return column_bin
以下代码将员工满意度分为三类:low
、medium
和high
。低于0.3
的被认为是low
满意度,高于0.6
的分数被认为是高度满意的员工分数,介于这两个值之间的被认为是medium
:
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
#Binning satisfaction level:
cut_points = [0.3,0.6]
labels = ["low","medium","high"]
hr_data["satisfaction_level"] = bins(hr_data["satisfaction_level"], cut_points, labels)
print('\n####The number of values in each bin are:###\n\n',pd.value_counts(hr_data["satisfaction_level"], sort=False))
一旦我们执行了前面的代码,我们可以从下面的结果中观察到为satisfaction_level
属性创建了三个箱,其中low
箱中有1941
值,medium
箱中有4788
,而high
箱中有8270
:
对数和幂变换通常有助于非基于树的模型,使高度偏斜的分布不那么偏斜。这种预处理技术有助于满足线性回归模型的假设和推断统计的假设。这种类型转换的一些示例包括对数转换、平方根转换和对数-对数转换。
以下是使用虚拟数据集进行平方根转换的演示:
import numpy as np
values = np.array([-4, 6, 68, 46, 89, -25])
# Square root transformation #
sqrt_trnsf_values = np.sqrt(np.abs(values)) * np.sign(values)
print(sqrt_trnsf_values)
以下是前面平方根转换的输出:
接下来,让我们使用另一个虚拟数据集尝试一个日志转换:
values = np.array([10, 60, 80, 200])
#log transformation #
log_trnsf_values = np.log(1+values)
print(log_trnsf_values)
虚拟数据集上的日志转换产生以下结果:
现在,我们已经对数值数据的不同预处理方法有了一个合理的想法,让我们看看分类数据有什么储备。
分类数据本质上是非参数的。这意味着它不遵循任何数据分布。然而,为了在参数模型中使用这些变量,它们需要使用各种编码方法进行转换,缺失的值将被替换,并且我们可以使用宁滨技术减少类别的数量。
在许多实际的 ML 活动中,数据集将包含分类变量。它在企业环境中更为合适,在企业环境中,大多数属性都是分类的。这些变量有不同的离散值。例如,组织的规模可以是Small
、Medium
或Large
,或者地理区域可以是Americas
、Asia Pacific
和Europe
。许多 ML 算法,尤其是基于树的模型,可以直接处理这种类型的数据。
然而,许多算法并不直接接受数据。因此,需要将这些属性编码成数值,以便进一步处理。有各种方法来编码分类数据。以下部分描述了一些广泛使用的方法:
这些技术中的大多数,以及许多其他技术,也在 Python 中实现,并且在包category_encoders
中可用。您可以使用以下命令安装category_encoders
库:
pip install category_encoders
接下来,我们将category_encoders
库导入为ce
(支持在代码中轻松使用的短代码)。我们加载HR
损耗数据集,并对salary
属性进行一次热编码:
import pandas as pd
import category_encoders as ce
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
print(hr_data.shape)
print(list(hr_data.columns))
onehot_encoder = ce.OneHotEncoder(cols=['salary'])
onehot_df = onehot_encoder.fit_transform(hr_data)
onehot_df.head()
我们可以观察到使用category_encoders
库将分类属性转换为其对应的单热编码属性是多么容易:
同样,我们使用OrdinalEncoder
对salary
数据进行标签编码:
ordinal_encoder = ce.OrdinalEncoder(cols=['salary'])
ordinal_df = ordinal_encoder.fit_transform(hr_data)
ordinal_df.head(10)
ordinal_df['salary'].value_counts()
上述代码将低、中、高工资等级映射为三个数值:0
、1
和2
:
同样,你可以从CategoryEncoders
开始尝试其他分类编码方法,使用以下代码片段,观察结果:
binary_encoder = ce.BinaryEncoder(cols=['salary'])
df_binary = binary_encoder.fit_transform(hr_data)
df_binary.head()
poly_encoder = ce.PolynomialEncoder(cols=['salary'])
df_poly = poly_encoder.fit_transform(hr_data)
df_poly.head()
helmert_encoder = ce.HelmertEncoder(cols=['salary'])
helmert_df = helmert_encoder.fit_transform(hr_data)
helmert_df.head()
下一个讨论的主题是处理分类属性缺失值的方法。
对于分类变量,评估缺失值的技术也保持不变。然而,一些插补技术是不同的,一些方法类似于所讨论的数值缺失值处理方法。我们将演示 Python 代码中专门针对分类缺失值处理的技术:
我们将再次利用HR
损耗数据集来解释分类属性的缺失值处理。让我们首先加载数据集,并观察数据集中的空值数量:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print('Nulls in the data set' ,hr_data.isnull().sum())
我们从以下输出中了解到,数据集没有缺失分类属性sales
和salary
的数据。因此,我们将综合考虑这些特征的一些缺失值:
我们使用下面的代码片段来替换sales
属性中的sales
值为空和salary
属性为低的空值:
#As there are no null introduce some nulls by replacing sales in sales column with NaN
hr_data[['sales']] = hr_data[[ 'sales']].replace('sales', np.NaN)
#As there are no null introduce some nulls by replacing low in salary column with NaN
hr_data[['salary']] = hr_data[[ 'salary']].replace('low', np.NaN)
print('New nulls in the data set' ,hr_data.isnull().sum())
执行完代码后,我们可以在salary
和sales
属性中找到一些空值,如下图所示:
现在,我们可以用每个列的模式来替换这些空值。正如我们对数值缺失值插补所做的那样,即使在这里,我们也首先创建hr_data
的副本,这样我们就不会替换原始数据集。接下来,我们使用fillna
方法用模式值填充行,如以下代码片段所述:
#Replace mode for missing values
hr_data_1 = hr_data.copy()
# fill missing values with mode column values
for column in hr_data_1.columns:
hr_data_1[column].fillna(hr_data_1[column].mode()[0], inplace=True)
# count the number of NaN values in each column
print(hr_data_1.isnull().sum())
print(hr_data_1.head())
从下面的输出中我们可以看到sales
列中缺失的值被technical
和salary
列中的medium
替换:
AAAA
或NA
来区分缺失值与数据集的其余部分:#Mark global constant for missing values
hr_data_2 = hr_data.copy()
# fill missing values with global constant values
hr_data_2.fillna('AAA', inplace=True)
# count the number of NaN values in each column
print(hr_data_2.isnull().sum())
print(hr_data_2.head())
前面代码的输出产生以下结果,缺失值用AAA
代替:
fancyimpute
库也可以用于这个任务。我们已经讨论了如何处理结构化数据的数据预处理。在这个数字时代,我们从各种来源捕获了大量非结构化数据。在下一节中,让我们了解预处理文本数据的方法,以便为模型使用做好准备。
有必要通过去除在分析期间给文本增加噪声的不必要的文本来减小文本数据的特征空间的大小。通常会执行一系列步骤来预处理文本数据。然而,并不是每个任务都需要所有步骤,只要有必要,就会使用这些步骤。例如,如果文本数据项中的每个单词都已经是小写的,就不需要修改文本的大小写来使其统一。
文本预处理任务有三个主要元素:
我们将使用nltk
库演示不同的文本预处理方法。通过在命令提示符下发出以下命令来安装nltk
库:
pip install nltk
安装完成后,在 Python 环境中运行以下代码片段:
##Run this cell only once##
import nltk
nltk.download()
你会得到一个 NLTK 下载器弹出窗口。从标识符部分选择全部,等待安装完成。
在本节中,我们将研究一些预处理步骤,这些步骤用于预处理文本以生成规范化的表单:
sent_tokenizer
把文字转换成句子。首先,我们使用以下代码片段从data
文件夹中读取文本文件:import pandas as pd
import category_encoders as ce
text_file = open('data/example_text.txt', 'rt')
text = text_file.read()
text_file.close()
nltk
库中导入sent_tokenize
方法,并将文本作为参数传递:## Sentence tokenization ##
from nltk import sent_tokenize
sentence = sent_tokenize(text)
print(sentence[0])
上述代码产生以下输出:
word_tokenizer
方法将文本转换为单词,如下面的代码片段所示:## Word tokenization ##
from nltk import word_tokenize
words = word_tokenize(text)
print(words[:50])
50
标记单词。我们可以看到一些非字母字符,如标点符号被标记化。这对分析练习没有任何价值,因此,我们需要移除这些变量:.
、"
、+
、~
等各种标点符号。有各种方法可以去掉非字母字符。现在我们将说明 Python 中的一种方法:
# Remove punctuations and keep only alphabets
words_cleaned = [word for word in words if word.isalpha()]
print(words_cleaned[:50])
(
从标记列表中被移除。但是,有些常用词如at
和of
对分析没有任何价值,可以使用stop word removal
方法删除:为了删除停止词,我们首先从ntlk.corpus
库中导入stopwords
方法。然后我们从stopwords.words
方法中调用english
停止单词字典,并移除在标记列表中找到的任何常见单词:
# remove the stop words
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
words_1 = [word for word in words_cleaned if not word in stop_words]
print(words_1[:50])
at
、the
都从令牌列表中删除了。然而,一些类似的词,如Data
和data
作为单独的词出现在标记列表中。我们现在需要将这些词转换成类似的情况:我们可以使用lower
函数将所有大写字母转换为小写,如下面的代码片段所示:
# Case folding
words_lower = [words_1.lower() for words_1 in words_1]
print(words_lower[:50])
从下面的输出中我们可以看到Data
等单词不再出现在列表中,被转换为全小写字母:
词干是将单词简化为基本或词根形式的过程。比如工作、工作则源于工作。这种转换是有用的,因为它将所有相似的词带到一个基础形式,有助于更好的情感分析、文档分类等等。
我们从nltk.stem.porter
库中导入PorterStemmer
,并实例化PorterStemmer
类。接下来,words_lower
标记列表被传递到porter.stem
类,以将每个单词简化为其词根形式:
#Stemming
from nltk.stem.porter import PorterStemmer
porter = PorterStemmer()
stemmed_words = [porter.stem(word) for word in words_lower]
print(stemmed_words[:50])
前面的代码生成了以下词干标记列表:
并非所有的特征或属性对 ML 模型都很重要。在接下来的几节中,我们将学习一些在使用 ML 流水线时减少特征数量的方法。
ML 模型使用一些关键特征来学习数据中的模式。所有其他特征都会给模型增加噪声,这可能会导致模型精度下降,并使模型过度拟合数据。因此,选择合适的功能是至关重要的。此外,使用一组简化的重要特性可以减少模型训练时间。
以下是创建模型前选择正确特征的一些方法:
此外,在创建基线模型后,我们可以使用以下一些方法来选择正确的特征:
在接下来的部分中,我们将看到 scikit 中可用的一些方法-学习减少数据集中可用的要素数量。
数据中没有太多差异或可变性的特征不会为学习模式的 ML 模型提供任何信息。例如,数据集中每条记录的值只有 5 的要素是一个常数,并且是一个不重要的要使用的要素。删除此功能至关重要。
我们可以使用 scikit-learn 的featureselection
包中的VarianceThreshold
方法来移除方差不满足特定标准或阈值的所有特征。sklearn.feature_selection 模块实现了特征选择算法。它目前包括单变量滤波器选择方法和递归特征消除算法。下面是一个例子来说明这种方法:
%matplotlib inline
import pandas as pd
import numpy as np
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
hr_data = pd.read_csv('data/hr.csv', header=0)
hr_data.head()
hr_data = hr_data.dropna()
data_trnsf = pd.get_dummies(hr_data, columns =['salary', 'sales'])
data_trnsf.columns
前面代码的输出如下:
接下来,我们将left
指定为目标变量,将其他属性指定为独立属性,如下代码所示:
X = data_trnsf.drop('left', axis=1)
X.columns
Y = data_trnsf.left# feature extraction
现在我们已经准备好数据,我们基于VarianceThreshold
方法选择特征。首先,我们从 scikit-learn 的feature_selection
模块导入VarianceThreshold
方法。然后我们在VarianceThreshold
方法中将阈值设置为0.2
。这意味着,如果某个属性的数据差异小于 20%,它将被丢弃,并且不会被选为特征。我们执行以下代码片段来观察精简后的功能集:
#Variance Threshold
from sklearn.feature_selection import VarianceThreshold
# Set threshold to 0.2
select_features = VarianceThreshold(threshold = 0.2)
select_features.fit_transform(X)
# Subset features
X_subset = select_features.transform(X)
print('Number of features:', X.shape[1])
print('Reduced number of features:',X_subset.shape[1])
从下面的输出中,我们可以确定20
属性中有 5 个属性通过了方差阈值测试,并且表现出变异性,变异性大于 20%方差:
在下一节中,我们将研究单变量特征选择方法,该方法基于某些统计测试来确定重要特征。
在这种方法中,对每个特征分别进行统计检验。根据测试结果分数,我们只保留最佳特征。
以下示例说明了卡方统计测试,以从HR
磨损数据集中选择最佳特征:
#Chi2 Selector
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
chi2_model = SelectKBest(score_func=chi2, k=4)
X_best_feat = chi2_model.fit_transform(X, Y)
# selected features
print('Number of features:', X.shape[1])
print('Reduced number of features:',X_best_feat.shape[1])
从下面的输出中我们可以看到4
最佳特征被选中。我们可以通过改变k
值来改变要考虑的最佳特征的数量:
下一节演示递归特征消除方法。
递归特征消除是基于这样的思想:通过去除特征递归地构建模型,用剩余的特征构建模型,并计算模型的精度。重复此过程,直到数据集中的所有要素都用尽。这是一种贪婪的优化方法,寻找性能最好的特征子集,然后根据它们被淘汰的时间对它们进行排序。
在下面的示例代码中,HR
磨损数据集用于说明递归特征消除 ( RFE )的使用。RFE
方法的稳定性在很大程度上取决于所用算法的类型。为了演示,我们使用了LogisticRegression
方法:
#Recursive Feature Elimination
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# create a base classifier used to evaluate a subset of attributes
logistic_model = LogisticRegression()
# create the RFE model and select 4 attributes
rfe = RFE(logistic_model, 4)
rfe = rfe.fit(X, Y)
# Ranking of the attributes
print(sorted(zip(map(lambda x: round(x, 4), rfe.ranking_),X)))
以下输出显示了按等级排序的要素:
随机森林通常用于 ML 流水线中的特征选择。所以,我们了解这项技术是至关重要的。
随机森林使用的基于树的特征选择策略自然根据它们提高节点纯度的程度进行排序。首先,我们需要构建一个随机森林模型。我们已经在第 2 章中讨论了创建随机森林模型的过程,并介绍了 使用 Python 进行机器学习的过程:
# Feature Importance
from sklearn.ensemble import RandomForestClassifier
# fit a RandomForest model to the data
model = RandomForestClassifier()
model.fit(X, Y)
# display the relative importance of each attribute
print(model.feature_importances_)
print(sorted(zip(map(lambda x: round(x, 4), model.feature_importances_),X)))
一旦模型构建成功,模型的feature_importance_ attribute
用于可视化按等级排序的导入要素,如下图所示:
在本节中,我们讨论了使用不同的特征选择方法来选择特征子集的不同方法。接下来,我们将看到使用降维方法的特征选择方法。
降维方法通过从原始特征的组合中产生新的合成特征来降维。它们是强有力的技术,并且保留了数据的可变性。这些技术的一个缺点是很难解释属性,因为它们是通过组合各种属性的元素来准备的。
主成分分析 ( 主成分分析)将高维空间中的数据转换为更少维度的空间。让我们考虑 100 维数据集的可视化。几乎不可能有效地显示这种高维数据分布的形状。主成分分析提供了一种有效的降维方法,通过形成各种主成分来解释降维空间中数据的可变性。
数学上,给定一组变量, X 1 ,X 2 ,....,X p ,这里有 p 的原始变量。在主成分分析中,我们正在寻找一组新的变量, Z 1 ,Z 2 ,....,Z p ,即原始变量的加权平均值(减去其平均值后):
其中每对 Z 的相关性=0
得到的 Z 按其方差排序,其中*Z1T5】方差最大,ZpT9】方差最小。*
Always, the first component extracted in a PCA accounts for a maximum amount of total variance in the observed variables. The second component extracted will account for a maximal amount of variance in the dataset that was not accounted for by the first component and it is also uncorrelated with the first component. If we compute the correlation between the first component and second component the correlation would be zero.
我们使用HR
磨损数据来演示主成分分析的使用。首先,我们将numpy
和pandas
库加载到环境中,并加载HR
数据集:
import numpy as np
import pandas as pd
hr_data = pd.read_csv('data/hr.csv', header=0)
print (hr_data.head())
以下是前面代码的输出,其中显示了数据集中每个属性的前五行:
主成分分析非常适合数字属性,并且在属性标准化时效果很好。所以,我们从sklearn.preprocessing
库中导入StandardScaler
。我们只包括数据预处理的数字属性。使用StandardScaler
方法,HR
数据集的数字属性被标准化:
from sklearn.preprocessing import StandardScaler
hr_numeric = hr_data.select_dtypes(include=[np.float])
hr_numeric_scaled = StandardScaler().fit_transform(hr_numeric)
接下来,我们从sklearn.decomposition
导入PCA
方法,将n_components
作为2
传递。n_components
参数定义了要构建的主要组件的数量。然后,确定由这两个主成分解释的方差:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
principalComponents = pca.fit_transform(hr_numeric_scaled)
principalDf = pd.DataFrame(data = principalComponents,columns = ['principal component 1', 'principal component 2'])
print(pca.explained_variance_ratio_)
我们可以看到,这两个主成分解释了人力资源数据集数字属性的可变性:
有时,我们使用的原始数据没有足够的信息来创建一个好的模型。在这种情况下,我们需要创建特征。在下一节中,我们将描述几种不同的方法来创建特征。
在现有功能的基础上创建新功能是一门艺术,可以通过许多不同的方式来实现。
The objective of feature creation is to provide ML algorithms with such predictors that makes it easy for them to understand the patterns and derive better relationship from the data.
例如在HR
减员问题中,员工在组织中的停留时间是一个重要属性。但是,有时我们在数据集中没有停留时间作为特征,但是我们有员工开始日期。在这种情况下,我们可以通过从当前日期减去员工开始日期来创建停留时间特征的数据。
在接下来的部分中,我们将看到从数据中生成新特征的一些不同方法。然而,这不是一个广泛的列表,而是一些可以用来创建新特性的不同方法。一个人需要思考问题陈述,探索数据,并创造性地发现构建功能的新方法:
添加和计算一对数字特征之间差异的过程称为成对特征创建。
还有一种称为PolynomialFeatures
创建的方法,我们自动执行特征的所有多项式组合。它有助于映射可能暗示某些独特状态的要素之间的复杂关系。
我们可以使用 scikit-learn 的PolynomialFeatures
方法生成多项式特征。让我们首先创建虚拟数据,如下面的代码片段所示:
#Import PolynomialFeatures
from sklearn.preprocessing import PolynomialFeatures
#Create matrix and vectors
var1 = [[0.45, 0.72], [0.12, 0.24]]
接下来,通过首先调用带有参数度的PolynomialFeatures
来生成多项式特征。该函数将生成度数小于或等于指定度数的要素:
# Generate Polynomial Features
ploy_features = PolynomialFeatures(degree=2)
var1_ = ploy_features.fit_transform(var1)
print(var1_)
代码执行完成后,它会生成新的特性,如下图所示:
从单个数据/时间特征中创建这些特征将有助于 ML 算法更好地学习数据中的时间模式。
在本章中,我们学习了与机器学习流水线非常相关的各种数据转换和预处理方法。准备属性、清理数据并确保数据没有错误,可以确保 ML 模型正确学习数据。使数据无噪声并生成良好的特征有助于 ML 模型有效地发现数据中的模式。
下一章将集中讨论自动语言算法的技术。我们将讨论各种特定于算法的特征转换,自动化有监督和无监督的学习,等等。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。