# 机器学习基础~07.数据规约

## 概述

**数据规约**：产生更小的但保持原数据完整性的新数据集，在规约后的数据集上进行分析和挖掘将更有效率。

**数据规约**的意义在于：

- 降低无效、错误数据对建模的影响，提高建模的准确性。
- 少量且具代表性的数据将大幅缩减数据挖掘所需的时间。
- 降低储存数据的成本。

## 属性规约

**流形学习(manifold learning)** ：现实世界中，许多数据集存在于高维空间中，但是这些数据可能分布在比数据维度低得多的流形上。流形学习的目标是将这种复杂的高维数据映射到一个更低维的表示，以便更好地理解数据的内在结构、降低噪声影响以及进行可视化和分析。

**属性规约**：通过属性合并**创建新属性维数**，或者直接通过删除不相关的属性维数来**减少数据维数**，从而提高数据挖掘的效率、降低计算成本。属性规约的目标是寻找出最小的**属性子集**并确保新数据子集的**概率分布**尽可能地接近原来数据集的**概率分布**。

### 特征选择

#### 过滤法(Filter)

![](https://img.papergate.top:5000/i/2025/05/68259630f1794.webp)

##### 单变量

当单个变量的值符合以下特征时，可以剔除掉该变量：

 - **缺失百分比(Missing Percentage)**：缺失样本比例过多且难以填补。
 - **方差(Variance)**：连续型变量的方差接近于0，说明其特征值趋向于单一值的状态，对模型帮助不大。
 - **频数(Frequency)**：类别型变量的枚举值集中在单一某枚举值上。

```python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.feature_selection import VarianceThreshold

# 加载示例数据（鸢尾花数据集）
data = load_iris()
X = data.data  # 特征
y = data.target  # 目标类别

# 低方差滤波
variance_selector = VarianceThreshold(threshold=0.5)
X_low_variance = variance_selector.fit_transform(X)
```

##### 多变量

多个变量之间，有以下情形：

- **自变量与自变量之间**：删除高度相关的特征，避免多重共线性。
- **自变量和因变量之间**：相关性越高，说明特征对模型预测目标更重要，建议保留。

###### 连续型 vs 连续型

 **皮尔逊相关系数(Pearson Correlation Coefficient)**：需要两个变量都服从**正态分布**，皮尔逊相关系数反映两个变量的**线性相关程度**，大于 0 的时候表示两者正相关，小于 0 的时候表示两者负相关。当两个变量线性相关时，相关系数趋于 1 或 -1，正负号指向正负相关关系。

$$
r=\frac{\sum\left(X_i-\bar{X}\right)\left(Y_i-\bar{Y}\right)}{\sqrt{\sum\left(X_i-\bar{X}\right)^2 \sum\left(Y_i-\bar{Y}\right)^2}}
$$

**斯皮尔曼相关系数(Spearman's Rank Correlation Coefficient)**：用于衡量两个变量的**单调关系**（不一定是线性关系），适用于**顺序数据**或**不满足正态分布**的连续数据。它基于变量的**排序（Rank）** 计算，而非原始数据值。

![](https://img.papergate.top:5000/i/2025/05/6825938b0802d.webp)

```python
#特征选择（pearson 相关系数法）
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
import numpy as np
import pandas as pd

iris = load_iris()
x = pd.DataFrame(iris.data)
y = pd.Series(iris.target)
features = iris.feature_names

# 两种方法都是 pearson 相关系数法
selector = SelectKBest(lambda X,Y:x.apply(lambda x:x.corr(y)),k=3).fit(x,y)
selector = SelectKBest(lambda X,Y:np.array([pearsonr(X[:,i],Y)[0] for i in range(4)]),k=3).fit(x,y)

new_x = selector.transform(x)

support = [*zip(features,selector.get_support())]
```

 ###### 连续型 vs 类别型

 **方差分析(Analysis of variance, ANOVA)**：检验不同组下的平均数是否存在显著差异。

方差分析前需要满足3个假设: 
- 每组样本具备方差同质性
- 组内样本服从正态分布
- 样本间需要独立。

>[!question]  问题
>**判断 1、2 和 3 班的同学的数学平均分是否有显著区别？**
>
>班级为类别型变量，数学分数为连续型变量，如果班级与数学分数有相关性，比如 1 班同学数学会更好些，则说明不同班的数学平均分有显著区别。
> 
> **零假设**：三个班的数学分数没有显著区别。
> 
> **验证方式**：看组间方差是否大于组内方差，如果组间方差 > 组内方差，说明存在至少一个分布相对于其他分布较远，则可以考虑拒绝零假设。

![](https://img.papergate.top:5000/i/2025/05/6825938e64b44.webp)

**肯德尔等级相关系数(Kendall tau rank correlation coefficient)**

>[!question]  问题
>**评价学历与工资的相关性**
> 
>肯德尔系数会对按学历对样本排序：
> 
> 若学历和工资排名相同，则Kendall系数为1，两变量正相关；
> 
> 若学历和工资完全相反，则系数为-1，两变量负相关；
> 
> 而如果学历和工资完全独立，系数为0。

![](https://img.papergate.top:5000/i/2025/05/682593903ddf0.webp)

##### 类别型vs类别型

**卡方检验(Chi-squared Test)**：用于检验两个类别型变量之间的相关性。**零假设**：两变量之间不相关。卡方值高，说明两变量之间具有相关性的可能性更大。

![](https://img.papergate.top:5000/i/2025/05/682593916c124.webp)

 **互信息(Mutual Information)**：衡量变量之间相互依赖程度

![](https://img.papergate.top:5000/i/2025/05/682593a8870b8.webp)

```python
#载入数据
from sklearn.datasets import load_iris
#特征选择
from sklearn.feature_selection import SelectKBest
#卡方检验
from sklearn.feature_selection import chi2

iris = load_iris()
x = iris.data
y = iris.target
features = iris.feature_names

selector = SelectKBest(chi2,k=2).fit(x,y)
support = {k:v for k,v in zip(features,selector.get_support())}
```

#### 包裹法(Wrapper)

![](https://img.papergate.top:5000/i/2025/05/682593ac07eed.webp)

##### 完全搜索

**完全搜索**：遍历所有可能组合的特征子集，然后输入模型，选择最佳模型分数的特征子集。不推荐使用，计算开销过大。

##### 启发式搜索

**启发式搜索**：利用启发式信息不断缩小搜索空间的方法。在特征选择中，**模型分数或特征权重**可作为启发式信息。

- **前向特征选择（Forward Feature Selection）**： 从空特征集开始，逐步添加对模型性能有贡献的特征，直到达到所需的特征数量或达到最佳性能。
- **反向特征消除（Recursive Feature Elimination，RFE）**： 从所有特征开始，反复拟合模型并排除不重要的特征，直到达到所需的特征数量。
- 基模型可以是 LR、决策树、SVM等。

```python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest,mutual_info_classif
from sklearn.feature_selection import RFE

# 反向特征消除
rfe_selector = RFE(estimator=rf_classifier, n_features_to_select=2)
X_selected = rfe_selector.fit_transform(X, y)

# 使用互信息进行前向特征选择
num_features_to_select = 2
selector = SelectKBest(score_func=mutual_info_classif, k=num_features_to_select)
X_selected = selector.fit_transform(X, y)
```

##### 随机搜索

- **随机特征子集**：随机选择多个特征子集，然后分别评估模型表现，选择评估分数高的特征子集。
- **随机目标 (Null Importance)**
  - 在原始数据集运行模型获取特征重要性;
  - shuffle 多次标签，每次 shuffle 后获取假标签下的特征重要性;
  - 计算真假标签下的特征重要性差异，并基于差异，筛选特征。

>[!question]  问题
> **随机目标 (Null Importance) 为什么能够筛选出真正重要的特征？**
> 
>  对于真正强健、稳定且重要的特征，在真标签下特征很重要，但一旦标签打乱，这些优质特征的重要性就会变差。
> 
> 相反地，如果某特征在原始标签下表现一般，但打乱标签后，居然重要性上升，那么这个特征反而不靠谱，应当剔除掉。

举一个简单易懂的例子：

>[!example]  示例
>把 userID 作为特征加入模型，预测不同 userID 属于哪类消费人群，显然这个特征对于预测没有任何作用。
> 
> 但是对于一个过拟合的模型，它可以会学到 userID 到消费人群的直接映射关系，相当于模型直接记住了这个userID是什么消费人群。如果把标签打乱，模型依然会把 userID 直接映射到打乱的标签上，不管是真标签还是假标签，userID 都是最重要的特征。

#### 嵌入法(Embedded)

 **嵌入法**：是将特征选择过程与学习器训练过程融为一体，二者在同一优化过程中完成，在学习器训练过程中自动地进行了特征选择。

 常见的嵌入法：

- 基于L1/L2惩罚项的
- 基于SVM的
- 基于树/森林的特征选择。

##### 分类

```python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import RandomForestClassifier

# 加载示例数据（鸢尾花数据集）
data = load_iris()
X = data.data  # 特征
y = data.target  # 目标类别
feature = data.feature_names

# 将待L1惩罚项的逻辑回归作为基模型的特征选择
selector = SelectFromModel(LogisticRegression(penalty='l1', C = 0.1, solver = 'liblinear')).fit(X, y)
selector.estimator_.coef_
support = {k:v for k,v in zip(feature,selector.get_support())}

# 将GBDT作为基模型的特征选择
selector = SelectFromModel(GradientBoostingClassifier()).fit(X, y)
selector.estimator_.coef_
support = {k:v for k,v in zip(feature,selector.get_support())}

# 随机森林特征选择
rf_classifier = RandomForestClassifier(n_estimators=100)
rf_classifier.fit(X, y)
importances = rf_classifier.feature_importances_
indices = np.argsort(importances)[::-1]
selected_features = indices[:2]  # 选择前两个重要特征
```

##### 回归

```python
from sklearn.linear_model import Lasso
from sklearn.feature_selection import SelectFromModel
from sklearn.datasets import load_boston

#引入数据集
dataset = load_boston()
x = dataset.data
y = dataset.target
feature = dataset.feature_names

selector= SelectFromModel(estimator=Lasso(alpha = 0.5)).fit(x,y)
selector.estimator_.coef_
support = {k:v for k,v in zip(feature,selector.get_support())}
```

### 降维

降维可以减少数据的维度，从而减少存储和计算成本，并防止维度灾难。

降维方法分为线性降维方法和非线性降维方法。

#### 线性降维方法

##### 主成分分析(PCA)

**主成分分析（Principal Component Analysis，PCA）** 是一种常用的降维技术，用于将高维数据投影到低维空间，以保留尽可能多的数据方差。它通过找到数据中的主要方差方向（主成分），将数据投影到这些主成分上，从而实现降低数据维度的目的。每个主成分都是原始特征的线性组合，且彼此正交。

- 在信号处理领域，我们认为信号具有较大方差，噪声具有较小方差，信号与噪声之比称为信噪比。
- 信噪比越大意味着数据的质量越好，反之，信噪比越小意味着数据的质量越小。
- 由此我们不难引出PCA的目标，即最大化投影方差，也就是让数据在主轴上投影的方差最大。

![](https://img.papergate.top:5000/i/2025/05/6825964ead43a.webp)

```python
import numpy as np
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris

# 加载示例数据（鸢尾花数据集）
data = load_iris()
X = data.data  # 特征
y = data.target  # 目标类别

# n_components：>=1时表示想要求得的主成分个数，传入小于1的float类型，表示保留下的主成分的特征保留度
# 保留98％的方差
pca = PCA(n_components = 0.98)
# 主成分个数2
pca = PCA(n_components=2)

# 执行主成分分析
X_pca = pca.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("PCA Transformed Data Shape:", X_pca.shape)
```

##### 线性判别分析(LDA)

**线性判别分析（Linear Discriminant Analysis，LDA）** 是一种用于分类和降维的统计方法，用于在多类别问题中找到最佳的投影方向，以便在新空间中实现类别的最大可分性。与主成分分析（PCA）不同，LDA是有监督的方法，它考虑了类别信息来优化投影方向。

- LDA的目标是将不同类别的样本在新的低维空间中最大程度地分开，同时尽量将同一类别的样本投影到靠近一起的位置。
- 它通过计算类间散布矩阵（类别之间的差异）和类内散布矩阵（类别内的差异）来选择最佳的投影方向。
- 最终，LDA会选择投影方向，使得类间散布矩阵与类内散布矩阵的比值最大化。

```PYTHON
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.datasets import load_iris

# 加载示例数据（鸢尾花数据集）
data = load_iris()
X = data.data  # 特征
y = data.target  # 目标类别

# 创建LDA对象，指定降维后的维度数量
num_components = 2
lda = LinearDiscriminantAnalysis(n_components=num_components)

# 执行线性判别分析
X_lda = lda.fit_transform(X, y)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("LDA Transformed Data Shape:", X_lda.shape)
```

##### PCA vs. LDA

相同点
  - 两者均可以对数据进行降维。
  - 两者在降维时均使用了矩阵特征分解的思想。
  - 两者都假设数据符合高斯分布。

不同点
  - LDA是有监督的降维方法，而PCA是无监督的降维方法。
  - LDA降维最多降到类别数 k-1 的维数，而PCA没有这个限制。
  - LDA除了可以用于降维，还可以用于分类。
  - LDA选择分类性能最好的投影方向，而PCA选择样本点投影具有最大方差的方向。

##### 独立分量分析(ICA)

**独立分量分析（Independent Component Analysis，ICA）** 是一种用于盲源分离的统计方法，用于从混合信号中恢复原始信号，前提是这些信号是相互独立的。ICA 假设混合信号是通过线性组合和一定的非线性变换得到的，目标是通过找到一组分离独立信号的线性组合来还原原始信号。

```PYTHON
import numpy as np
from sklearn.decomposition import FastICA
from sklearn.datasets import load_iris

# 加载示例数据（鸢尾花数据集）
data = load_iris()
X = data.data  # 特征
y = data.target  # 目标类别

# 使用FastICA进行独立分量分析
ica = FastICA(n_components=2)
S_pred = ica.fit_transform(X)

# 输出分离的独立信号
print("Original Signals:\n", S)
print("Mixed Signals:\n", X)
print("Separated Signals:\n", S_pred)
```

#### 非线性降维方法

##### 核主成分分析(Kernel PCA)

**核主成分分析（Kernel Principal Component Analysis，Kernel PCA）** 是主成分分析（PCA）的一种扩展形式，用于在非线性数据上进行降维。与传统的PCA不同，核主成分分析通过应用核函数来处理非线性关系，从而在高维特征空间中找到最佳的投影方向。核主成分分析的步骤与传统主成分分析类似，但在计算协方差矩阵时，它使用了核函数来实现非线性变换。这使得核主成分分析能够在保留非线性特征的同时，实现数据的降维。

**计算步骤**：

- **中心化数据**：同样将每个特征值减去对应特征的均值，以确保数据的中心位于原点。
- **计算核矩阵**：使用选择的核函数（如径向基函数核`rbf`）计算核矩阵，核矩阵的每个元素表示两个样本之间的核函数值。
- **计算中心化核矩阵**：对核矩阵进行中心化处理，确保中心位于原点。
- **特征值分解**：对中心化核矩阵进行特征值分解，得到特征值和特征向量。
- **选择主成分**：根据特征值的大小，选择要保留的主成分数量。

**核函数**：

- **线性核（Linear Kernel）**：
  - 适用范围：适用于线性可分的数据。
  - 公式：$K(x, y) = x^T y$
- **多项式核（Polynomial Kernel）**：
   - 适用范围：适用于数据具有多项式关系的情况。
   - 公式：$K(x, y) = (x^T y + c)^d$
- **径向基函数核（RBF Kernel）**：
   - 适用范围：适用于各种非线性关系，常用于核PCA中。
   - 公式：$K(x, y) = \exp(-\gamma \|x - y\|^2)$
- **Sigmoid核**：
   - 适用范围：适用于捕捉数据间的非线性关系，但在某些情况下可能不如其他核函数效果好。
   - 公式：$K(x, y) = \tanh(\alpha x^T y + c)$

```PYTHON
import numpy as np
from sklearn.decomposition import KernelPCA
from sklearn.datasets import make_circles

# 生成非线性数据（环形数据）
X, y = make_circles(n_samples=400, factor=0.3, noise=0.05)

# 使用KernelPCA进行降维
kernel_pca = KernelPCA(n_components=2, kernel='rbf')
X_kpca = kernel_pca.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("KernelPCA Transformed Data Shape:", X_kpca.shape)
```

##### 多维缩放（MDS）

**多维缩放（Multidimensional Scaling，MDS）** 是一种用于在低维空间中可视化高维数据的技术。它通过保持数据点之间的距离关系来将数据点映射到一个更低维度的空间，以便于可视化和分析。

- **度量MDS** ：试图在低维空间中保持数据点之间的欧氏距离或其他距离度量。它通过优化过程来调整低维空间中的点的位置，使得它们之间的距离与原始高维空间中的距离尽量接近。
- **非度量MDS**： 关注于保持数据点之间的顺序关系，而不一定保持精确的距离。它通过定义一种映射函数，将原始高维空间中的排序关系映射到低维空间中，使得排列顺序尽量保持一致。

```python
import numpy as np
from sklearn.manifold import MDS
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

# 加载示例数据（手写数字数据集）
data = load_digits()
X = data.data  # 特征
y = data.target  # 目标类别

# 使用MDS进行降维
mds = MDS(n_components=2)
X_mds = mds.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("MDS Transformed Data Shape:", X_mds.shape)
```

##### 等度量映射（ISOMAP）

**等度量映射（Isometric Feature Mapping，ISOMAP）** ：是一种用于非线性降维的技术，旨在在保持数据点之间的测地距离关系的同时将数据映射到一个更低维度的空间。ISOMAP 基于流形学习的思想，可以在高维数据中发现潜在的流形结构，并在降维后保持这种结构。

ISOMAP 主要用于处理高维数据中的非线性关系，特别适用于捕捉数据中的流形结构，如螺旋、环形等。它基于数据点之间的测地距离（沿流形表面的最短路径距离），而不是简单的欧氏距离

```python
import numpy as np
from sklearn.manifold import Isomap
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

# 加载示例数据（手写数字数据集）
data = load_digits()
X = data.data  # 特征
y = data.target  # 目标类别

# 使用ISOMAP进行降维
isomap = Isomap(n_components=2)
X_isomap = isomap.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("ISOMAP Transformed Data Shape:", X_mds.shape)
```

##### 局部线性嵌入（LLE）

**局部线性嵌入（Locally Linear Embedding，LLE）**： 是一种用于非线性降维的流形学习方法，旨在保持数据点之间的局部线性关系。LLE通过分析每个数据点与其最近邻数据点之间的线性关系，将数据映射到一个更低维度的空间，从而捕捉数据的流形结构。

LLE 的核心思想是，将每个数据点表示为其最近邻数据点的线性组合，然后在低维空间中找到能够保持这些线性组合关系的映射。LLE分为三个主要步骤：局部权重计算、重建权重计算和全局映射。

```python
import numpy as np
from sklearn.manifold import LocallyLinearEmbedding
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

# 加载示例数据（手写数字数据集）
data = load_digits()
X = data.data  # 特征
y = data.target  # 目标类别

# 使用LLE进行降维
lle = LocallyLinearEmbedding(n_components=2)
X_lle = lle.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("LLE Transformed Data Shape:", X_mds.shape)
```

##### t-分布随机近邻嵌入(t-SNE)

**t-分布随机近邻嵌入（t-distributed stochastic neighbor embedding, t-SNE）** ：是一种用于降维和数据可视化的非线性方法，旨在将高维数据映射到一个低维空间，以便于观察数据的类别和相似性结构。t-SNE 特别适用于在降维后保留数据之间的局部相似性关系。

t-SNE 通过构建一个概率分布来捕捉高维空间中数据点之间的相似性，然后在低维空间中构建一个类似的概率分布，以最小化这两个概率分布之间的 Kullback-Leibler 散度。该方法强调在低维空间中保持邻近数据点之间的相似性，并尽量将不同类别的数据点分开。

```python
import numpy as np
from sklearn.manifold import TSNE
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

# 加载示例数据（手写数字数据集）
data = load_digits()
X = data.data  # 特征
y = data.target  # 目标类别

# 使用t-SNE进行降维
tsne = TSNE(n_components=2)
X_tsne = tsne.fit_transform(X)

# 输出降维后的数据
print("Original Data Shape:", X.shape)
print("t-SNE Transformed Data Shape:", X_mds.shape)
```

## 数值规约

数值规约通过选择替代的、较小的数据来减少数据量，包括**有参数方法**和**无参数方法**两类。

 有参数方法是使用一个模型来评估数据，**只需存放参数**，而不需要存放实际数据。例如，**回归**（线性回归和多元回归）和**对数线性模型**（近似离散属性集中的多维概率分布）。
 
无参数方法就**需要存放实际数据**，例如，**直方图、聚类、抽样（采样）、参数回归**。

### 直方图

**直方图**使用分箱来近似数据分布，是一种流行的数据规约形式。属性A的直方图将A的数据分布划分为不相交的子集或桶。如果每个桶只代表单个属性值/频率对，则该桶称为单桶。通常，桶表示给定属性的一个连续区间。

### 聚类

聚类技术是将数据元组（即记录，数据表中的一行）视为对象。它将对象划分为簇，使一个簇中的对象相互“相似”，而与其他簇中的对象“相异”。在数据规约中，用数据的簇替换实际数据。该技术的有效性依赖于簇的定义是否符合数据的分布性质。

### 抽样

抽样也是一种数据规约技术，它用比原始数据小得多的随机样本（子集）表示原始数据集。假定原始数据集 D 包含 N 个元组，可以采用抽样方法对 D 进行抽样。

### 参数回归

简单线性模型和对数线性模型可以用来近似给定的数据。用简单线性模型对数据建模，使之拟合为一条直线。



---

> 作者: Aphros  
> URL: https://blog.papergate.top/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%9F%BA%E7%A1%80~07.%E6%95%B0%E6%8D%AE%E8%A7%84%E7%BA%A6/  

