<think>我们计划在数据处理流程中增加特征相关性分析步骤,具体位置在数据标准化之后、模型训练之前。主要步骤如下:
1. 计算特征之间的相关系数矩阵
2. 绘制热力图可视化相关系数
3. 根据设定的阈值,删除高相关性特征(保留其中一个)
我们将创建一个函数来处理高相关性特征,并在数据划分和标准化之后应用它。
注意:由于我们的数据已经分成了训练集、验证集和测试集,并且进行了标准化,所以我们应该在训练集上计算相关系数,然后对验证集和测试集应用相同的特征删除。
步骤:
1. 在split_data函数返回后,我们得到了标准化后的训练集、验证集和测试集(X_train_nor, X_vaild_nor, X_test_nor)。
2. 我们将使用X_train_nor(标准化后的训练特征)来计算特征之间的相关系数矩阵。
3. 设定一个阈值(例如0.8),如果两个特征之间的相关系数大于该阈值,则删除其中一个(我们可以选择保留与目标变量相关性更高的特征,或者使用其他方法,但这里为了简化,我们采用递归删除的方法)。
但是,由于标准化后的数据是numpy数组,没有列名,所以我们需要记录要删除的列索引。
然而,我们注意到原始数据在导入时没有保留列名(因为我们使用了.values转换成了numpy数组)。因此,我们需要调整代码以保留列名,这样在特征选择后可以知道哪些特征被删除。
修改建议:
在split_data函数中,我们目前返回的是numpy数组。为了进行特征选择,我们需要知道每个特征的名字,所以我们需要在函数中保留特征名称。
由于时间关系,我们可以在split_data函数中返回特征名称,或者我们重新设计一下流程,在划分数据集时保留DataFrame格式,直到特征选择完成后再转换为numpy数组。
但是,考虑到我们现有的代码结构,我们可以这样做:
- 在split_data函数中,除了返回numpy数组,还要返回特征名称(即列名,除去第一列标签列)
- 然后,我们使用特征名称来标记热力图。
但是,我们也可以不修改split_data函数,而是提前获取特征名称。我们可以在读取数据后,将特征名称提取出来。
由于用户代码中已经将数据转换为numpy数组,我们可以在转换之前保存列名。
我们调整代码:
在split_data函数中,我们将修改为:
train = df_train_list1.values -> 我们暂时不转换,先获取列名
train_X, train_y = df_train_list1.iloc[:, 1:], df_train_list1.iloc[:, 0] 这样可以得到DataFrame格式的特征和标签
但是为了不改变原有流程,我们可以这样做:
步骤:
1. 在调用split_data之前,我们有一个DataFrame(df_train_list1等),我们可以获取特征列名。
2. 在split_data函数内部,我们除了返回数组,还要返回特征列名。
由于split_data函数返回的内容已经很多,我们可以选择在函数外部获取特征名称。
我们注意到,在调用split_data之前,我们有df_train_list1, df_valid_list1, df_test_list1,它们是DataFrame。我们可以从df_train_list1中获取特征列名。
因此,我们可以在调用split_data之后,从df_train_list1中提取特征列名(即第二列到最后一列的名字)。
具体代码:
在用户代码的最后部分:
df_train_list = ... # 这是DataFrame
df_vaild_list = ...
df_test_list = ...
然后调用split_data,得到X_train_nor等。
接着,我们获取特征名称:
feature_names = df_train_list.columns[1:].tolist()
然后,我们使用X_train_nor(标准化后的训练特征)构建一个DataFrame,列名为feature_names,然后计算相关系数矩阵。
但是,由于标准化后的数据是numpy数组,我们可以将其转换为DataFrame。
因此,我们添加以下步骤:
1. 获取特征名称
2. 将X_train_nor转换为DataFrame(列名为feature_names)
3. 计算相关系数矩阵,绘制热力图
4. 根据相关系数矩阵删除高相关性的特征(注意:不要删除目标变量,因为这里只有特征)
然后,我们还需要对验证集和测试集做同样的特征删除。
由于我们可能删除一些特征,所以我们需要记录要删除的列索引,然后从三个数据集中删除这些列。
我们编写一个函数来删除高相关性的特征:
注意:我们只考虑特征之间的相关性,不考虑特征与目标变量的相关性(因为目标变量已经分离)。
函数设计:
def remove_high_correlation_features(X, feature_names, threshold=0.8):
# 将X转换为DataFrame
df = pd.DataFrame(X, columns=feature_names)
# 计算相关系数矩阵
corr_matrix = df.corr().abs()
# 创建一个集合来记录要删除的特征
to_drop = set()
# 上三角矩阵(不包括对角线)
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
# 找到相关系数大于阈值的列
for col in upper.columns:
for row in upper.index:
if upper.loc[row, col] > threshold:
# 如果这一对特征都还没有被标记删除,那么我们就删除其中一个(这里我们选择删除索引较大的那个,但也可以根据其他条件)
# 我们比较两个特征与其他特征的相关性,保留与其他特征相关性更小的那个?或者简单删除后面出现的那个(即列名在后面的)
# 这里我们简单删除列名在后面的那个(即当前循环的col)
if col not in to_drop and row not in to_drop:
to_drop.add(col)
# 然后,我们得到要保留的特征索引
keep_columns = [col for col in df.columns if col not in to_drop]
# 记录要删除的列索引(在原始特征中的位置)
drop_indices = [i for i, col in enumerate(feature_names) if col in to_drop]
# 只保留保留列
df_reduced = df[keep_columns]
# 返回降维后的数组,以及新的特征名称,还有要删除的列索引(用于对验证集和测试集做同样的删除)
return df_reduced.values, keep_columns, drop_indices
但是,注意:这个函数会改变特征顺序,而且我们还需要对验证集和测试集做同样的删除操作。
另一种做法:我们记录要删除的列索引,然后直接在这些索引处删除。
我们修改一下,不返回新的特征名称,而是返回要删除的列索引列表(注意:这个列表是原始特征索引)。然后,我们在原始数据上删除这些列。
步骤:
1. 用训练集计算要删除的列索引(drop_indices)
2. 从训练集、验证集、测试集中删除这些列。
注意:由于我们的特征顺序是一致的,所以可以用相同的索引删除。
因此,函数可以简化为只返回要删除的列索引。
修改函数:
def get_high_correlation_features(X, feature_names, threshold=0.8):
df = pd.DataFrame(X, columns=feature_names)
corr_matrix = df.corr().abs()
to_drop = set()
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
for col in upper.columns:
for row in upper.index:
if upper.loc[row, col] > threshold:
# 如果这对特征都还没被标记删除,则删除当前列(col)
if col not in to_drop and row not in to_drop:
to_drop.add(col)
# 然后,我们找到这些特征对应的原始索引
drop_indices = [i for i, col in enumerate(feature_names) if col in to_drop]
return drop_indices
然后,我们使用这个函数得到drop_indices,然后:
X_train_nor = np.delete(X_train_nor, drop_indices, axis=1)
X_vaild_nor = np.delete(X_vaild_nor, drop_indices, axis=1)
X_test_nor = np.delete(X_test_nor, drop_indices, axis=1)
同时,我们还需要更新特征名称(如果后续需要):
new_feature_names = [feature_names[i] for i in range(len(feature_names)) if i not in drop_indices]
另外,我们还需要绘制热力图,所以我们在删除之前绘制。
完整步骤:
1. 获取特征名称:feature_names = df_train_list.columns[1:].tolist()
2. 将训练集标准化后的特征X_train_nor转换为DataFrame,绘制热力图(在删除之前)
3. 调用get_high_correlation_features函数,得到要删除的特征索引
4. 删除三个数据集中的这些特征
5. 再次绘制删除后的特征热力图(可选)
但是,我们也可以先绘制原始的热力图,然后删除高相关性特征后再绘制一次,以作对比。
由于用户要求补充代码,我们将代码插入到数据标准化之后,模型训练之前。
具体位置:在用户代码的最后部分,调用split_data之后,打乱训练集之前(因为打乱不影响特征列的顺序)。
代码插入点:
在以下代码之后:
X_mean, y_mean,X_std,y_std,X_train_nor, y_train_nor,X_vaild_nor, y_vaild_nor, X_test_nor, y_test_nor,train_y,vaild_y,test_y,test_X,train_X,vaild_X=split_data(df_train_list,df_vaild_list,df_test_list)
插入:
步骤1:获取特征名称
feature_names = df_train_list.columns[1:].tolist() # 因为第一列是标签
步骤2:绘制原始特征热力图(标准化后的训练集特征)
注意:标准化后的特征可能不是原始量纲,但相关系数不变,因为标准化是线性变换。
步骤3:计算相关系数矩阵并绘制热力图
使用seaborn绘制热力图
步骤4:删除高相关性特征
步骤5:绘制删除后的热力图(可选)
然后,再继续打乱训练集。
由于打乱训练集只影响行,不影响列,所以可以在特征删除之后打乱。
我们编写代码:
注意:由于特征数量可能很多,热力图的字体可能会很小,我们可以调整大小。
我们将阈值设为0.8(可调)。
具体代码实现如下:
</think>
以下是为您的数据处理代码补充特征相关性分析和降维的完整代码,我将逐步解释实现逻辑:
```python
# 新增代码:特征相关性分析与降维
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.cluster import hierarchy
def feature_correlation_reduction(X_train_nor, feature_names, threshold=0.8):
"""
分析特征相关性并删除高相关特征
参数:
X_train_nor: 标准化后的训练集特征
feature_names: 特征名称列表
threshold: 相关性阈值,默认0.8
返回:
降维后的特征矩阵和新的特征名称
"""
# 1. 计算相关系数矩阵
corr_matrix = pd.DataFrame(X_train_nor, columns=feature_names).corr().abs()
# 2. 绘制热力图
plt.figure(figsize=(16, 14))
sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap='coolwarm',
annot_kws={"size": 8}, cbar_kws={"shrink": 0.8})
plt.title('特征相关系数矩阵热力图', fontsize=16)
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
plt.tight_layout()
plt.show()
# 3. 层次聚类可视化
plt.figure(figsize=(14, 10))
corr_condensed = hierarchy.distance.squareform(1 - corr_matrix)
linkage = hierarchy.linkage(corr_condensed, method='average')
hierarchy.dendrogram(linkage, labels=feature_names, orientation='left',
leaf_font_size=10, color_threshold=0.5)
plt.title('特征层次聚类树状图', fontsize=16)
plt.xlabel('距离', fontsize=12)
plt.tight_layout()
plt.show()
# 4. 删除高相关特征
# 创建要删除的特征集合
to_drop = set()
# 上三角矩阵(不包括对角线)
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
# 找出相关系数大于阈值的特征对
for col in upper.columns:
correlated_features = upper.index[upper[col] > threshold].tolist()
if correlated_features:
# 保留当前列,删除其他相关特征
to_drop.update(correlated_features)
print(f"特征 '{col}' 与以下特征高度相关(> {threshold}): {correlated_features} - 已标记删除")
# 5. 应用特征删除
keep_features = [f for f in feature_names if f not in to_drop]
col_indices = [feature_names.index(f) for f in keep_features]
print("\n原始特征数量:", len(feature_names))
print("删除高相关特征数量:", len(to_drop))
print("保留特征数量:", len(keep_features))
print("保留特征:", keep_features)
# 返回降维后的特征矩阵和特征名
return X_train_nor[:, col_indices], keep_features
```
### 在您现有代码中的集成位置
```python
# ... [您现有的数据加载和预处理代码] ...
# 在数据标准化后调用特征降维函数
# ============= 新增代码开始 =============
# 获取特征名称(假设您的DataFrame列名可用)
feature_names = df_train_list.columns[1:].tolist() # 跳过第一列标签列
# 应用特征相关性分析
print("="*60)
print("执行特征相关性分析与降维")
print("="*60)
X_train_nor_reduced, reduced_feature_names = feature_correlation_reduction(
X_train_nor,
feature_names,
threshold=0.8 # 可调整阈值
)
# 对验证集和测试集应用相同的特征选择
keep_indices = [feature_names.index(f) for f in reduced_feature_names]
X_vaild_nor_reduced = X_vaild_nor[:, keep_indices]
X_test_nor_reduced = X_test_nor[:, keep_indices]
# 更新特征集
X_train_nor = X_train_nor_reduced
X_vaild_nor = X_vaild_nor_reduced
X_test_nor = X_test_nor_reduced
# ============= 新增代码结束 =============
# 后续模型训练保持不变
indices = np.arange(X_train_nor.shape[0])
np.random.shuffle(indices)
# ... [您现有的模型训练代码] ...
```
### 代码功能说明
1. **相关性热力图**:
- 使用Seaborn绘制相关系数矩阵热力图
- 直观展示特征间的相关性强度(-1到1)
- 对角线为特征自相关(值=1)
- 红色表示正相关,蓝色表示负相关[^2]
2. **层次聚类树状图**:
- 使用层次聚类对相关特征分组
- 相似特征在树状图中位置接近
- 帮助识别特征分组和冗余特征[^3]
3. **高相关特征删除**:
- 自动识别相关系数超过阈值的特征对
- 保留每组相关特征中的一个代表(保留第一个出现的特征)
- 避免人工选择的主观性[^1]
4. **特征降维处理**:
- 仅在训练集上计算相关性
- 将相同的特征选择应用于验证集和测试集
- 保持数据一致性
### 关键参数调整建议
1. **相关性阈值**:
```python
threshold=0.8 # 可调整为0.7-0.9之间
```
- 阈值越低,删除的特征越多
- 建议从0.8开始,根据模型表现调整
2. **热力图优化**:
```python
# 调整热力图大小
plt.figure(figsize=(16, 14))
# 控制注释显示
annot=True # 显示数值 / False隐藏数值
fmt=".2f" # 数值格式
```
### 注意事项
1. **特征名称处理**:
- 确保`feature_names`正确对应特征列
- 若数据无列名,可创建虚拟名称:
```python
feature_names = [f'Feature_{i}' for i in range(X_train_nor.shape[1])]
```
2. **高维数据处理**:
- 当特征超过50个时,建议调整热力图大小:
```python
plt.figure(figsize=(20, 18))
```
3. **阈值选择原则**:
- 首次运行使用默认阈值0.8
- 观察删除特征数量是否合理(建议保留70%以上特征)
- 通过交叉验证比较降维前后模型表现
此实现遵循特征工程最佳实践:仅在训练集上计算统计量(相关系数),避免数据泄露[^3]。降维后的特征集将用于后续模型训练,可提高模型效率和泛化能力。