基于Fisher’s Score的特征选择报错及内核崩溃问题求解
问题根因
- skfeature库的
fisher_score函数原生仅支持numpy数组作为输入,直接传入pandas DataFrame/Series时,函数内部对维度的解析逻辑会失效,因此抛出长度不匹配的报错。 - 直接使用
np.array(X_train)转换会生成全量数据的内存副本,当数据集过大时,会直接占满可用内存导致内核崩溃。
解决方案
方案1:零拷贝转换numpy数组(最低修改成本)
不要用np.array()做转换,改用pandas内置的to_numpy(copy=False)方法,该方法会直接复用DataFrame底层的存储数组,不会生成额外副本,内存占用几乎无增长:
from skfeature.function.similarity_based import fisher_score # copy=False 表示不生成数据副本,直接引用底层存储;ravel将标签转为一维数组 ranks = fisher_score.fisher_score(X_train.to_numpy(copy=False), y_train.to_numpy(copy=False).ravel()) V = X_train.columns[ranks[:5]] print(V)
方案2:原生pandas实现Fisher's score(完全不用numpy转换)
Fisher's score的计算逻辑非常简单,直接基于pandas的分组统计即可实现,完全不需要转换为numpy数组,内存开销最小:
import pandas as pd def fisher_score_pandas(X: pd.DataFrame, y: pd.Series) -> pd.Series: # 合并特征和标签 df = X.join(y.rename('label')) # 全局均值 global_mean = X.mean() # 统计各类别样本量、类别均值 class_stats = df.groupby('label').agg(['count', 'mean']) class_count = class_stats.xs('count', level=1, axis=1) class_mean = class_stats.xs('mean', level=1, axis=1) # 计算类间方差 between_var = (class_count * (class_mean - global_mean)**2).sum() # 计算类内方差 within_var = pd.Series(0, index=X.columns) for label in class_count.index: class_data = df[df['label'] == label].drop('label', axis=1) within_var += ((class_data - class_mean.loc[label])**2).sum() # 计算fisher score,处理分母为0的边界情况 fisher_scores = between_var / within_var.replace(0, 1e-10) # 返回降序排序的特征索引,和skfeature的输出逻辑一致 return fisher_scores.sort_values(ascending=False).index # 直接调用,无需任何类型转换 ranks = fisher_score_pandas(X_train, y_train) V = ranks[:5] print(V)
额外内存优化
如果数据集本身已经接近内存上限,可以配合以下操作进一步降低内存开销:
- 提前将X_train的数值类型从float64转换为float32,内存占用直接减半:
X_train = X_train.astype('float32') - 若特征中存在大量0值,可将DataFrame转换为pandas稀疏存储格式,进一步压缩内存
内容的提问来源于stack exchange,提问作者user3064366
相关产品推荐
相关产品推荐

