为啥 sklearn 的训练/测试拆分加上 PCA 会使我的标签不正确?

Posted

技术标签:

【中文标题】为啥 sklearn 的训练/测试拆分加上 PCA 会使我的标签不正确?【英文标题】:Why does sklearn's train/test split plus PCA make my labelling incorrect?为什么 sklearn 的训练/测试拆分加上 PCA 会使我的标签不正确? 【发布时间】:2019-04-05 05:55:21 【问题描述】:

我正在使用 Pandas 在 Scikit-learn(Python 3 上为 0.20)中探索 PCA 来构建我的数据。当我应用测试/训练拆分时(并且仅在何时),我的输入标签似乎不再与 PCA 输出匹配。

import pandas
import sklearn.datasets
from matplotlib import pyplot
import seaborn

def load_bc_as_dataframe():
    data = sklearn.datasets.load_breast_cancer()
    df = pandas.DataFrame(data.data, columns=data.feature_names)
    df['diagnosis'] = pandas.Series(data.target_names[data.target])
    return data.feature_names.tolist(), df

feature_names, bc_data = load_bc_as_dataframe()

from sklearn.model_selection import train_test_split
# bc_train, _ = train_test_split(bc_data, test_size=0)
bc_train = bc_data

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
bc_pca_raw = pca.fit_transform(bc_train[feature_names])
bc_pca = pandas.DataFrame(bc_pca_raw, columns=('PCA 1', 'PCA 2'))
bc_pca['diagnosis'] = bc_train['diagnosis']

seaborn.scatterplot(
    data=bc_pca,
    x='PCA 1',
    y='PCA 2',
    hue='diagnosis',
    style='diagnosis'
)

pyplot.show()

这看起来很合理,准确的分类结果也证明了这一点。如果我将 bc_train = bc_data 替换为 train_test_split() 调用(即使是 test_size=0),我的标签似乎不再与原始标签相对应。

我意识到train_test_split() 正在对我的数据进行洗牌(通常我希望这样做),但我不明白为什么会出现问题,因为 PCA 和标签分配使用相同的洗牌数据。 PCA 的转换只是一个投影,虽然它显然不会保留相同的特征(列),但它不应该改变哪个标签与哪个帧一起使用。

如何正确地重新标记我的 PCA 输出?

【问题讨论】:

【参考方案1】:

问题分为三个部分:

    train_test_split() 中的改组导致bc_train 中的索引处于随机顺序(与行位置相比)。 PCA 对数值矩阵进行运算,并有效地从输入中去除索引。创建一个新的DataFrame 会重新创建顺序索引(与行位置相比)。 现在我们在bc_train 中有随机索引,在bc_pca 中有顺序索引。当我做bc_pca['diagnosis'] = bc_train['diagnosis'] 时,bc_train 是reindexed 和bc_pcas 索引。这会重新排序 bc_train 数据,使其索引匹配 bc_pcas。

换句话说,当我使用bc_pca['diagnosis'](即__setitem__())分配索引时,Pandas 会对索引进行左连接,而不是逐行复制(类似于update()

我不觉得这很直观,而且除了源代码之外,我也找不到关于 __setitem__() 行为的文档,但我希望它对于更有经验的 Pandas 用户来说是有意义的,而且它的文档可能更高在我没见过的地方升级。

有很多方法可以避免这种情况。我可以重置训练/测试数据的索引:

bc_train, _ = train_test_split(bc_data, test_size=0)
bc_train.reset_index(inplace=True)

或者,我可以从values 成员分配:

bc_pca['diagnosis'] = bc_train['diagnosis'].values

我也可以在构造 DataFrame 之前做类似的事情(可以说更明智,因为 PCA 有效地在 bc_train[feature_names].values 上运行)。

【讨论】:

相关:How to assign columns while ignoring index alignment

以上是关于为啥 sklearn 的训练/测试拆分加上 PCA 会使我的标签不正确?的主要内容,如果未能解决你的问题,请参考以下文章

Sklearn PCA:列车和测试的组件数量不同

在 sklearn 中进行测试和训练数据拆分的错误

Sklearn 将 Pandas Dataframe 和 CSR Matrix 拆分为测试和训练集

如何使用内置的张量流方法对特征和标签张量执行 sklearn 风格的训练测试拆分?

如何将使用 PCA 和随机森林训练的模型应用于测试数据?

为啥 sklearn 和 numpy 不同意 PCA 的乘法分量?