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

时间:2018-11-01 01:17:35

标签: python pandas scikit-learn pca

我正在使用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()

enter image description here

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

enter image description here

我意识到train_test_split()正在改组我的数据(通常是我想要的),但是我不明白为什么会出现问题,因为PCA和标签分配使用相同的改组数据。 PCA的转换只是一个投影,虽然它显然没有保留相同的功能(列),但不应更改哪个标签与哪个帧一起显示。

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

1 个答案:

答案 0 :(得分:1)

问题包括三个部分:

  1. train_test_split()中的改组导致bc_train中的索引以随机顺序(与行位置相比)。
  2. PCA在数字矩阵上运行,并有效地从输入中去除索引。创建新的DataFrame会重新创建顺序索引(与行位置相比)。
  3. 现在我们在bc_train中有随机索引,在bc_pca中有顺序索引。当我执行bc_pca['diagnosis'] = bc_train['diagnosis']时, bc_trainreindexed,具有bc_pca的索引。这将对bc_train数据重新排序,以使其索引与bc_pca s相匹配。

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

我不觉得这很直观,也找不到除源代码之外的__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上运行)。