# 1.1 Data Pre-processing

我们在实际中遇到的数据往往需要进行一些预处理才能比较好的用于模型训练。我们这里介绍两类常见的预处理，一是如何处理缺失值，二是如何对数据进行归一化。

## 1) 缺失值的处理

缺失值会使得系统丢失有用信息，表现出更大的的不确定性，进而影响我们数据挖掘的过程。以下罗列了三种处理缺失值的策略:

### 1a) 舍弃缺失值

* 舍弃缺失值过多的样本
* 舍弃缺失值过多的特征

### 1b) 直接使用含缺失值的数据

* 我们也可以把缺失作为特征一种特殊的状态，不进行处理。
* 并不是所有分类器都适合使用这种方法。基于树的模型本身对缺失值有比较高的容忍度；也有些模型不能处理数据有缺失的情况，例如SVM模型。

### 1c) 数据插补

我们还可以用统计方法填补一些缺失的数据，称为"Data Impuation", 包括很多方法：

**(1) 平均值填充**

可以用空缺特征的平均数、中位数、众数、最大值、最小值、固定值等作为填充。

如果空值是数值属性，就使用该属性在其他所有对象的取值的平均值来填充该缺失的属性值。如果空值是非数值属性，就根据统计学中的众数原理，用该属性在其他所有对象出现频率最高的值来补齐该缺失的属性。

**(2) 热卡填充（就近补齐）**

对于一个包含空值的对象，热卡填充法在完整数据中找到一个与它最相似的对象，然后用这个相似对象的值来进行填充。不同的问题选用不同的标准来对相似进行判定。

**(3) K最近邻法**

先根据欧式距离或相关分析来确定距离具有缺失数据样本最近的K个样本，将这K个值加权平均来估计该样本的缺失数据。

**(4) 回归插补**

回归基于完整的数据集，建立回归方程（模型）。对于包含空值的对象，将已知属性值代入方程来估计未知属性值，以此估计值来进行填充。

**(5) EM插补**

在缺失类型为随机缺失的条件下，假设模型对于完整的样本是正确的，那么通过观测数据的边际分布可以对未知参数进行极大似然估计。这种方法也被称为忽略缺失值的极大似然估计。 该方法比删除个案和单值插补更有吸引力，它一个重要前提：适用于大样本。有效样本的数量足够以保证ML估计值是渐近无偏的并服从正态分布。但是这种方法可能会陷入局部极值，收敛速度也不是很快，并且计算很复杂。

**(6) 多重插补**

多值插补的思想来源于贝叶斯估计，认为待插补的值是随机的，它的值来自于已观测到的值。 具体实践上通常是估计出待插补的值，然后再加上不同的噪声，形成多组可选插补值。 多重插补方法分为三个步骤：

* a. 为每个空值产生一套可能的插补值，这些值反映了无响应模型的不确定性；每个值都可以被用来插补数据集中的缺失值，产生若干个完整数据集合。
* b. 每个插补数据集合都用针对完整数据集的统计方法进行统计分析。
* c. 对来自各个插补数据集的结果，根据评分函数进行选择，产生最终的插补值。

## 2) 数据的scaling

### 2a) 是什么?

scaling指的是对于每一个特征，分别用一个线性变换，使得不同特征的数值处于相似的范围，且各个特征都变为不带量纲的量。常见的scaling方法有如下几种:

* standard/z-score scaling: 对每个特征，减去该特征在不同样本中的均值，再除以该特征在不同样本中的标准差。适用于近似正态分布的样本。

$$\text{zscore}(x\_{ij}^{'}) = \frac{x\_{ij} - \mu \_{ij}}{\sigma \_i}$$

* quartile scaling / robust scaling: 用中位数和IQR代替z-score中的样本均值和样本方差来进行scaling。对于有较多离群值的样本，以及与正态分布符合得不好的样本，推荐使用这种方法。

$$\text{robustscale}(x\_{ij}^{'}) = \frac{x\_{ij} - \text{median}*k x*{ik}} {Q\_{0.75}(\mathbf{x}*i) - Q*{0.25}(\mathbf{x}\_i)}$$

* min-max scaling: 对每个特征，把样本的最小值缩放到0，最大值缩放到1。

$$\text{minmax}(x\_{ij}^{'}) = \frac{x\_{ij} - \text{min}*k \mathbf{x}*{ik}} {\text{max}*k x*{ik} - \text{min}*k x*{ik}}$$

* abs-max scaling: 和min-max scaling类似，只不过绝对值缩放到\[0,1],不改变数据的符号。

$$\text{maxabs}(x\_{ij}^{'}) = \frac{x\_{ij}}{\text{max}*k \vert x*{ik} \vert}$$

在实践中，standard/z-score scaling和quartile scaling / robust scaling相对比较常用。另外两种方法非常容易受到离群值的影响，所以使用得相对更少一些。即使使用，也通常会先去除离群值，或对数据进行clipping(把大于/小于某个给定值的数据统一设成这个给定值)。

### 2b) 为什么?

* 不同的特征的量纲可能是不一样的，如果我们使用一个线性模型，就会涉及到不同特征的加和。显然，不同单位的数据是不能相加的，所以我们对每一个模型系数都得有一个附加的解释。我们对数据scaling后，所有特征都不在带有单位，因而可以直接相加，也不用再考虑系数的可解释问题了。
* 对涉及距离计算的模型而言，在实际应用中，不同特征的数值大小不同，会在求距离时造成很大的影响。例如，一个基因的表达量很高，另一个基因的表达量很低，如果不进行数据的归一化，表达量高的基因就会主导距离的计算。
* 最后，对于有一些算法(如逻辑回归，支持向量机，神经网络)，如果有一些特征的数值大小比较大，可能会导致模型不收敛。
* 简单而言，如果我们的模型在训练过程会在一步计算中同时考虑一个以上的特征，或可能存在数值计算上不收敛的问题，对数据进行scaling就是有必要的。
* 在我们常见的模型中，树模型符合这两个要求。所以如果我们使用随机森林等基于树的模型，都可以不进行scaling。对于其他大部分的模型，scaling都是有必要的。

### 2c) 怎么做?

在R语言和python中，我们可以通过调用

| Scaling method           | Python function                      | R function                 |
| ------------------------ | ------------------------------------ | -------------------------- |
| standard/z-score scaling | sklearn.preprocessing.StandardScaler | scale(x,center=T,scale=T)  |
| min-max scaling          | sklearn.preprocessing.MinMaxScaler   | normalize(x, range=c(0,1)) |
| abs-max scaling          | sklearn.preprocessing.MaxAbsScaler   |                            |
| robust scaling           | sklearn.preprocessing.RobustScaler   |                            |

这些变换都非常简单，无非是每个特征分别减去一个数，再除以一个数，你完全可以用`numpy`和R语言其他的函数自己实现。

* python实例

```python
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
iris_dataset = load_iris()
X = iris_dataset['data']
X = StandardScaler().fit_transform(X) # Z score scaling
```

* R实例

```
X <- as.matrix(iris[,1:4])
X <- scale(X, center = T, scale = T)
```
