python-3.x 如何使用ColumnTransformer()返回 Dataframe ?

jutyujz0  于 2022-12-15  发布在  Python
关注(0)|答案(2)|浏览(178)

我有这样一个 Dataframe :

department      review  projects salary satisfaction bonus  avg_hrs_month   left
0   operations  0.577569    3   low         0.626759    0   180.866070      0
1   operations  0.751900    3   medium      0.443679    0   182.708149      0
2   support     0.722548    3   medium      0.446823    0   184.416084      0
3   logistics   0.675158    4   high        0.440139    0   188.707545      0
4   sales       0.676203    3   high        0.577607    1   179.821083      0

我想尝试ColumnTransformer()并返回转换后的 Dataframe 。

ord_features = ["salary"]
ordinal_transformer = OrdinalEncoder()

cat_features = ["department"]
categorical_transformer = OneHotEncoder(handle_unknown="ignore")

ct = ColumnTransformer(
    transformers=[
        ("ord", ordinal_transformer, ord_features),
        ("cat", categorical_transformer, cat_features ),
           ]
)

df_new = ct.fit_transform(df)
df_new

它会给我一个'〈class ' numpy.float64'〉类型的稀疏矩阵'
如果我使用pd.DataFrame(ct.fit(df)),那么我将得到一个列:

0
0   (0, 0)\t1.0\n (0, 7)\t1.0
1   (0, 0)\t2.0\n (0, 7)\t1.0
2   (0, 0)\t2.0\n (0, 10)\t1.0
3   (0, 5)\t1.0
4   (0, 9)\t1.0

然而,我期望看到这样的转换后的 Dataframe ?

review  projects salary satisfaction bonus  avg_hrs_month   operations support ...
0   0.577569    3    1      0.626759     0      180.866070      1           0
1   0.751900    3    2      0.443679     0      182.708149      1           0  
2   0.722548    3    2      0.446823     0      184.416084      0           1
3   0.675158    4    3      0.440139     0      188.707545      0           0
4   0.676203    3    3      0.577607     1      179.821083      0           0

是否可以使用ColumnTransformer()?

6yt4nkrj

6yt4nkrj1#

正如在评论中快速描述的那样,在您的示例中需要考虑几点:

  • 方法.fit_transform()一般返回一个 * 稀疏矩阵 * 或者一个 *numpy数组 *,返回稀疏矩阵是为了节省内存;想一想这个例子,你对一个有很多类别的分类属性进行一次热编码,你最终得到的是一个有很多列的矩阵,每行只有一个非零项;对于稀疏矩阵,你只能存储非零元素的 location。在这种情况下,你可以在.fit_transform()的输出上调用.toarray(),以获取一个numpy数组,并将其传递给pd.DataFrame构造函数。

实际上,在与您提供的数据集类似的五行数据集上

df = pd.DataFrame({
    'department': ['operations', 'operations', 'support', 'logistics', 'sales'],
    'review': [0.577569, 0.751900, 0.722548, 0.675158, 0.676203],
    'projects': [3, 3, 3, 4, 3],
    'salary': ['low', 'medium', 'medium', 'low', 'high'],
    'satisfaction': [0.626759, 0.751900, 0.722548, 0.675158, 0.676203],
    'bonus': [0, 0, 0, 0, 1],
    'avg_hrs_month': [180.866070, 182.708149, 184.416084, 188.707545, 179.821083],
    'left': [0, 0, 1, 0, 0]
})

ord_features = ["salary"]
ordinal_transformer = OrdinalEncoder()

cat_features = ["department"]
categorical_transformer = OneHotEncoder(handle_unknown="ignore")

ct = ColumnTransformer(transformers=[
    ("ord", ordinal_transformer, ord_features),
    ("cat", categorical_transformer, cat_features),
])

我无法重现您的问题(也就是说,我直接获得了一个numpy数组),但基本上pd.DataFrame(ct.fit_transform(df).toarray())应该可以满足您的情况。

  • 正如您所看到的,相对于预期输出,它只包含转换后的(顺序编码)salary 列作为第一列,(one-hot-encoded)department 列从第二列到最后一列,这是因为,正如您在docs中看到的,参数remainder默认设置为'drop',这意味着所有不受转换影响的列都将被删除。为了避免这种情况,您应该将其设置为'passthrough';这将帮助您转换所需的列,并保持其他列不变。
ct = ColumnTransformer(transformers=[
    ("ord", ordinal_transformer, ord_features),
    ("cat", categorical_transformer, cat_features )],
    remainder='passthrough'
)

在这种情况下,pd.DataFrame(ct.fit_transform(df).toarray())的输出如下:

  • 同样,正如您所看到的,转换后的列顺序也不是您所期望的。

转换后的特征矩阵中列的顺序遵循在转换器列表中指定列的顺序。原始特征矩阵中未指定的列将从生成的转换后的特征矩阵中删除,除非在passthrough关键字中指定。使用passthrough指定的列将添加到转换器输出的右侧。
我希望在应用sklearn.compose.ColumnTransformer后能保留列顺序。

  • 最后,对于与列名有关的内容,您可能应该应用一个自定义解决方案,将您想要的内容直接传递给columns参数,然后再传递给pd.DataFrame构造函数。OrdinalEncoder(与OneHotEncoder不同)不提供.get_feature_names_out()方法,而.get_feature_names_out()方法通常可以轻松地将columns=ct.get_feature_names_out()传递给pd.DataFrame构造函数。有关其用法的示例,请参见使用OHE的列转换器和管道-在执行ct之后,OHE编码字段是保留还是删除?

更新10/2022 - sklearn版本1.2.dev0

使用sklearn版本1.2.0,可以更轻松地解决转换ColumnTransformer示例时返回DataFrame的问题。该版本尚未发布,但您可以在dev(版本1.2.dev0)中测试以下内容,方法是安装夜间构建版本:

pip install --pre --extra-index https://pypi.anaconda.org/scipy-wheels-nightly/simple scikit-learn -U

ColumnTransformer(以及其他转换器)现在公开了一个.set_output()方法,该方法可以通过向转换器传递参数transform='pandas'来配置转换器以输出Pandas Dataframe 。
因此,示例变为:

import pandas as pd
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, OrdinalEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.ensemble import RandomForestClassifier

df = pd.DataFrame({
    'department': ['operations', 'operations', 'support', 'logistics', 'sales'],
    'review': [0.577569, 0.751900, 0.722548, 0.675158, 0.676203],
    'projects': [3, 3, 3, 4, 3],
    'salary': ['low', 'medium', 'medium', 'low', 'high'],
    'satisfaction': [0.626759, 0.751900, 0.722548, 0.675158, 0.676203],
    'bonus': [0, 0, 0, 0, 1],
    'avg_hrs_month': [180.866070, 182.708149, 184.416084, 188.707545, 179.821083],
    'left': [0, 0, 1, 0, 0]
})

ord_features = ["salary"]
ordinal_transformer = OrdinalEncoder()

cat_features = ["department"]
categorical_transformer = OneHotEncoder(sparse_output=False, handle_unknown="ignore")

ct = ColumnTransformer(transformers=[
    ("ord", ordinal_transformer, ord_features),
    ("cat", categorical_transformer, cat_features )],
    remainder='passthrough'
)

ct.set_output('pandas')
df_pandas = ct.fit_transform(df)
df_pandas

输出也变得更容易阅读,因为它有正确的列名(实际上,在每一步,组成ColumnTransformer的转换器都有属性feature_names_in_;这样在转换输入时就不会丢失列名了)。
最后一点:注意到这个示例现在需要将参数sparse_output=False传递给OneHotEncoder示例才能工作。

pdtvr36n

pdtvr36n2#

此答案跳过变通方案,直接提供scikit-learn版本1.2+的解决方案

从sklearn版本1.2开始,转换器可以直接返回一个pandas DataFrame,而无需进一步处理。它是通过set_output完成的,可以通过调用set_output方法为每个估计器配置,也可以通过设置set_config(transform_output="pandas")全局配置。请参见发行亮点了解scikit-learn 1.2 - Pandas output with set_output API
在您的情况下,解决方案是:

ord_features = ["salary"]
ordinal_transformer = OrdinalEncoder()

cat_features = ["department"]
categorical_transformer = OneHotEncoder(handle_unknown="ignore")

ct = ColumnTransformer(
    transformers=[
        ("ord", ordinal_transformer, ord_features),
        ("cat", categorical_transformer, cat_features ),
           ]
)

# Add the following line to your code
ct.set_output(transform="pandas")

df_new = ct.fit_transform(df)
df_new

相关问题