# 1.4.分类变量

    分类变量是用来表示类别和标记。实际数据集中，类别的数量是有限的，它可以用数值表示，但它是不能排序的（无大小），所以又称**无序变量**。核心问题：**我们是要知道两个值有多大不同，还是只需要知道它们是否不同**？文档语料库的词汇表可以表示为一个大型分类变量，类别就是唯一的单词，如果一个类别在某个数据点中出现多次，则使用该分类的计数来表示每个类别，这种做法称为**分箱计数**。

## 1. 分类变量的编码

    类别变量中的类别不是数值型，所以需要将这些值编码成计算机可识别的数值型，但如果直接使用`1 - k`的数值，那么类别之间彼此有了顺序，这种做法是禁止的。

### 1.1. One-Hot独热

    独热编码使用一组比特位，每个比特位表示一种可能的类别，若变量不能同时属于多个类别，那么这一组值就只有一个比特位是`1`的开区间，示例如下：

|            | $$e\_1$$ | $$e\_2$$ | $$e\_3$$ |
| ---------- | :------: | :------: | :------: |
| Chong Qing |     1    |     0    |     0    |
| Shang Hai  |     0    |     1    |     0    |
| Bei Jing   |     0    |     0    |     1    |

```python
# 编码前
(["Chong Qing","Shang Hei","Bei Jing"])
# 编码后
([
    [1,0,0],
    [0,1,0],
    [0,0,1]
])
```

> SK中使用`sklearn.preprocessing.OneHotEncoder`实现。

    这种编码很容易理解，它使用的比特位要比实际需要的多一位，如果`k-1`都是0，那么最后一位肯定是1，因为变量只能取`k`个值中一个，数学中的表达为：

$$e\_1 + e\_2 + ... + e\_k = 1$$

    此处少一位主要原因是已知其他类型后，可以使用$$1 - e\_x$$来表示最后一位，所以有四种类型时，独热编码通常只需要三种值进行编码表示。

### 1.2. 虚拟编码

    独热的弊端是它允许`k`个自由度，而本身只需要`k-1`个自由度（前边已经说明）；虚拟编码（Dummy）在进行表示时只使用`k-1`个特征，去除额外的自由度，最后那个特征通过一个全零变量表示（它称为参照类，此处`Bei Jing`），示例如：

|            | $$e\_1$$ | $$e\_2$$ |
| ---------- | :------: | :------: |
| Chong Qing |     1    |     0    |
| Shang Hai  |     0    |     1    |
| Bei Jing   |     0    |     0    |

    虚拟编码比独热更具有解释性，假设有线性模型（通常加上一个截距`b`使得所有x为0时y不为0）：

$$y = w\_1x\_1 + ... + w\_ny\_n + b$$

* **独热编码**：截距项表示目标变量的整体均值，每个线性系数表示对应自变量与整体均值有多大差别。
* **虚拟编码**：偏差系数表示响应变量`y`对于参照类的均值，上述例子中参照类是`Bei Jing`，第`i`个特征的稀疏等于第`i`个类别的均值与参照类的差。

### 1.3. 效果编码

    另外一种分类变量的编码是**效果编码**，它和虚拟编码十分相似，区别在于参照类是全部用`-1`组成的向量表示，如：

|            | $$e\_1$$ | $$e\_2$$ |
| ---------- | :------: | :------: |
| Chong Qing |     1    |     0    |
| Shang Hai  |     0    |     1    |
| Bei Jing   |    -1    |    -1    |

    此时，截距项表示目标变量的整体均值，各个系数表示了各个类别的均值与整体均值之间的差（类别或水平的**主效果**，效果编码名称由此而来）。独热编码没有单独特征表示参照类，所以参照类效果需单独计算，计算相对麻烦。

### 1.4. 编码方式对比

* 独热编码：
  * 缺点：有冗余，会使得同一个问题有多个有效模型，这种非唯一性比较难以解释。
  * 优点：每个特征都明确对应一个类别，而且可以把缺失数据编码为全零向量，模型输出是整体均值。
* 虚拟编码：
  * 缺点：不太容易处理缺失数据（全零已经映射成参照类），其他类型的值都是和参照类的相对值，不太直观。
  * 优点：同独热编码，但可生成唯一可解释模型。
* 效果编码：
  * 缺点：全`-1`组成的向量是密集向量，计算和存储成本比较高，所以SK，Pandas不支持。
  * 优点：同独热编码，但可生成唯一可解释模型。

## 2. 大型分类变量

    大型系统中面临挑战：找到一种合适的特征表示方法，既要内存高效，有能生成精确的、训练速度快的模型。

    解决办法：

1. 不在编码问题上搞花样，用简单、容易训练的模型，在很多机器上使用独热编码训练线性模型（逻辑回归或线性SVM）。
2. 压缩特征：
   1. 特征散列化，通常用于线性模型。
   2. 分箱计数，常用语线性模型和树模型。

### 2.1. 特征散列化

    散列函数是一种确定性函数，它可以将一个可能无界的整数映射到一个有限的整数范围`[1, m]`中，由于输入域大于输出，所以可能有多个值映射到同样输出中，这就是**碰撞**。**均值散列函数**可以确保将大致相同数量的数量数值映射到`m`个分箱中，最终将初始特征向量压缩成为`m`维向量。

    这种做法的缺点就是散列之后的特征失去了可解释性，只是初始特征的某种聚合。

### 2.2. 分箱计数

    这是一种**特征工程**技术，它的思想：不使用分类变量的值作特征，而是使用目标变量取这个值的条件概率。换句话说，不对分类变量的值进行编码，而是要计算分类变量值与预测的目标变量之间的相关统计量。

    示例，假设有10000个用户：

1. 独热编码会生成一个长度为10000的稀疏向量，只在对应当前数据点的列上有一个1。
2. 分箱计数会将所有的10000个二值列编码为一个单独的特征，是`0-1`之间的一个实数值。

    换句话说，分箱计数将一个分类变量转换为与其值相关的统计量，它可以将一个大型的、稀疏的、二值的分类变量表示转换为一个小巧的、密集的、实数型的数值表示。

#### 2.2.1. 处理稀有类

    和文本中的**罕见词**一样，稀有类需要特殊处理。

    一种方法叫做`Back-Off`，这是一种将所有稀有类的计数累加到一个特殊分箱中的简单计数。若类别计数大于一个确定的阈值，那么就使用它自己的计数统计量；否则，就使用`Back-Off`分箱的统计量；这种方法本质上把单个稀有类的统计量转换为使用所有稀有类计算的统计量。

    另一种方法是最小计数图，这种方法中，不管稀有类还是频繁类，所有类别都通过多个散列函数进行映射，每个散列函数输出范围`m`都远小于类别数量`k`；计算统计量时，需要使用所有散列函数进行计算并返回结果中最小的那个统计量。这种方法有效在于：散列函数的数量乘以散列表大小`m`之后，不但可以小于类别数量`k`，而且能保持非常低的碰撞概率。

#### 2.2.2. 防止数据泄漏

    分箱计数要依赖历史数据生成必须的统计量。统计量中包含目标变量，而它正式模型试图去预测的，使用输出去计算输入特征会导致一个严重的问题，就是**数据泄露**。数据泄漏会使模型中增加一些不应该有的信息，这些信息可以使模型获得某种不现实的优势，从而做出更精确的预测，此处的数据泄漏等价于将数据本身特征泄漏给了模型使得模型分数更高，但事实是潜在引起了过拟合。

    所以最好的解决方法就是严格隔离计数收集和训练：使用过去的数据点进行计数，使用当前数据点进行训练，使用未来数据点进行测试。另一种方法是基于差分隐私的解决方案，对于一个统计量，如果不管有没有一个数据点，它的分布维持不变，那么它就是**近似防漏**的。

#### 2.2.3. 无界计数

    如果提供的历史数据越多，统计量持续更新，计数就会无限增长。

    如果输入计数增加时，模型需要维持原来的规模；如果输入计数累积得比较慢，有效范围不会变得太快，模型就不需要维护得特别频繁；反之计数增加得非常快时，过于的频繁维护就会造成很多问题。面对这个问题

* 一种良好解决方案是使用归一化之后的计数，这样就可以保证把计数值限制在一个可知的区间中。
* 另一种方法是进行对数变换，这样可以强加一个严格边界，计数值非常大时，变换结果的增加速度是非常慢的。

### 3. 总结表

| 说明项  | 含义                                                                    |
| ---- | --------------------------------------------------------------------- |
| 名称   | **独热编码**                                                              |
| 空间要求 | 使用稀疏向量格式时为$$O(n)$$，其中n是数据点的个数                                         |
| 计算能力 | 线性模型下为$$O(nk)$$，其中$$k$$是类别数量                                          |
| 优点   | <p>1.容易实现<br>2.可能是最精确的<br>3.可用于在线学习</p>                               |
| 缺点   | <p>1.计算效率不过<br>2.不能适应可增长的类别<br>3.只适用于线性模型<br>4.对于大数据集，需要大规模的分布式优化</p> |

| 说明项  | 含义                                                                 |
| ---- | ------------------------------------------------------------------ |
| 名称   | **特征散列化**                                                          |
| 空间要求 | 使用稀疏向量格式时为$$O(n)$$，其中n是数据点的个数                                      |
| 计算能力 | 线性模型和核方法下为$$O(nm)$$，其中$$m$$是散列分箱的个数                                |
| 优点   | <p>1.容易实现<br>2.模型训练成本更低<br>3.容易适应新类别<br>4.容易处理稀有类<br>5.可用于在线学习</p> |
| 缺点   | <p>1.只适合线性模型或核方法<br>2.散列后的特征无法解释<br>3.精确度难以保证</p>                  |

| 说明项  | 含义                                                                                                   |
| ---- | ---------------------------------------------------------------------------------------------------- |
| 名称   | **分箱计数**                                                                                             |
| 空间要求 | $$O(n + k)$$，将每个数据点表示为小而密集的向量，加上为每个类别保存的计数统计量                                                        |
| 计算能力 | 线性模型下为$$O(n)$$，也适用于非线性模型，比如树                                                                         |
| 优点   | <p>1.训练阶段的计算负担最小<br>2.可用于基于树的模型<br>3.比较容易适应新类别<br>4.可使用<code>Back-Off</code>方法或最小计数图稀有类<br>5.可解释</p> |
| 缺点   | <p>1.需要历史数据<br>2.需要延迟更新，不完全适合在线学习<br>3.很可能导致数据泄漏</p>                                                 |
