对分类变量进行一位有效编码,同时对连续变量进行缩放

28

我感到困惑,因为如果你先使用OneHotEncoder再使用StandardScaler,会有问题,因为缩放器也会对之前由 OneHotEncoder转换的列进行缩放。有没有一种方法可以同时执行编码和缩放,然后将结果拼接在一起呢?


2
OneHotEncoder有一个参数categorical_features,用于指定要编码的列。您可以使用FeatureUnion分别执行这两个操作,然后将它们合并在一起。 - Vivek Kumar
4个回答

36

没问题。只需根据需要分别对各列进行缩放和独热编码:

# Import libraries and download example data
from sklearn.preprocessing import StandardScaler, OneHotEncoder

dataset = pd.read_csv("https://stats.idre.ucla.edu/stat/data/binary.csv")
print(dataset.head(5))

# Define which columns should be encoded vs scaled
columns_to_encode = ['rank']
columns_to_scale  = ['gre', 'gpa']

# Instantiate encoder/scaler
scaler = StandardScaler()
ohe    = OneHotEncoder(sparse=False)

# Scale and Encode Separate Columns
scaled_columns  = scaler.fit_transform(dataset[columns_to_scale]) 
encoded_columns =    ohe.fit_transform(dataset[columns_to_encode])

# Concatenate (Column-Bind) Processed Columns Back Together
processed_data = np.concatenate([scaled_columns, encoded_columns], axis=1)

1
这个答案中概述的原则同样适用于Pyspark。 - Chuck

10

从0.20版本开始,Scikit-learn提供sklearn.compose.ColumnTransformer来进行混合类型的列变换。您可以同时对数值特征进行缩放和对分类变量进行独热编码。以下是官方示例(您可以在这里找到代码):

# Author: Pedro Morales <part.morales@gmail.com>
#
# License: BSD 3 clause

from __future__ import print_function

import pandas as pd
import numpy as np

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, GridSearchCV

np.random.seed(0)

# Read data from Titanic dataset.
titanic_url = ('https://raw.githubusercontent.com/amueller/'
               'scipy-2017-sklearn/091d371/notebooks/datasets/titanic3.csv')
data = pd.read_csv(titanic_url)

# We will train our classifier with the following features:
# Numeric Features:
# - age: float.
# - fare: float.
# Categorical Features:
# - embarked: categories encoded as strings {'C', 'S', 'Q'}.
# - sex: categories encoded as strings {'female', 'male'}.
# - pclass: ordinal integers {1, 2, 3}.

# We create the preprocessing pipelines for both numeric and categorical data.
numeric_features = ['age', 'fare']
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())])

categorical_features = ['embarked', 'sex', 'pclass']
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)])

# Append classifier to preprocessing pipeline.
# Now we have a full prediction pipeline.
clf = Pipeline(steps=[('preprocessor', preprocessor),
                      ('classifier', LogisticRegression(solver='lbfgs'))])

X = data.drop('survived', axis=1)
y = data['survived']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

clf.fit(X_train, y_train)
print("model score: %.3f" % clf.score(X_test, y_test))

注意:此方法为实验性质,某些行为可能会在未经弃用的情况下在发布版本之间更改。


7
目前有多种方法可以实现OP所需的结果,其中三种方法为:
  1. np.concatenate() - 可参见这篇已发布的回答

  2. scikit-learn中的ColumnTransformer

  3. scikit-learn中的FeatureUnion

使用@Max Power在这里发布的示例,以下是一个最小工作代码片段,它可以做到OP所需的功能,并将转换后的列合并为单个Pandas数据框。显示了所有3种方法的输出。

所有3种方法的通用代码为

import numpy as np
import pandas as pd

# Import libraries and download example data
from sklearn.preprocessing import StandardScaler, OneHotEncoder

dataset = pd.read_csv("https://stats.idre.ucla.edu/stat/data/binary.csv")

# Define which columns should be encoded vs scaled
columns_to_encode = ['rank']
columns_to_scale  = ['gre', 'gpa']

# Instantiate encoder/scaler
scaler = StandardScaler()
ohe    = OneHotEncoder(sparse=False)

方法一。 请查看此处的代码。为了显示输出,可以使用

print(pd.DataFrame(processed_data).head())

第一种方法的输出结果。

          0         1    2    3    4    5
0 -1.800263  0.579072  0.0  0.0  1.0  0.0
1  0.626668  0.736929  0.0  0.0  1.0  0.0
2  1.840134  1.605143  1.0  0.0  0.0  0.0
3  0.453316 -0.525927  0.0  0.0  0.0  1.0
4 -0.586797 -1.209974  0.0  0.0  0.0  1.0

方法二。

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline


p = Pipeline(
    [("coltransformer", ColumnTransformer(
        transformers=[
            ("assessments", Pipeline([("scale", scaler)]), columns_to_scale),
            ("ranks", Pipeline([("encode", ohe)]), columns_to_encode),
        ]),
    )]
)

print(pd.DataFrame(p.fit_transform(dataset)).head())

第二种方法的输出结果。

          0         1    2    3    4    5
0 -1.800263  0.579072  0.0  0.0  1.0  0.0
1  0.626668  0.736929  0.0  0.0  1.0  0.0
2  1.840134  1.605143  1.0  0.0  0.0  0.0
3  0.453316 -0.525927  0.0  0.0  0.0  1.0
4 -0.586797 -1.209974  0.0  0.0  0.0  1.0

Method 3.

from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.pipeline import FeatureUnion


class ItemSelector(BaseEstimator, TransformerMixin):
    def __init__(self, key):
        self.key = key
    def fit(self, x, y=None):
        return self
    def transform(self, df):
        return df[self.key]

p = Pipeline([("union", FeatureUnion(
    transformer_list=[
        ("assessments", Pipeline([
            ("selector", ItemSelector(key=columns_to_scale)),
            ("scale", scaler)
            ]),
        ),
        ("ranks", Pipeline([
            ("selector", ItemSelector(key=columns_to_encode)),
            ("encode", ohe)
            ]),
        ),
    ]))
])

print(pd.DataFrame(p.fit_transform(dataset)).head())

方法三的输出。

          0         1    2    3    4    5
0 -1.800263  0.579072  0.0  0.0  1.0  0.0
1  0.626668  0.736929  0.0  0.0  1.0  0.0
2  1.840134  1.605143  1.0  0.0  0.0  0.0
3  0.453316 -0.525927  0.0  0.0  0.0  1.0
4 -0.586797 -1.209974  0.0  0.0  0.0  1.0

说明

  1. 方法1已经解释过了。

  2. 方法2和方法3接受完整的数据集,但仅在数据子集上执行特定操作。修改/处理后的子集将汇总(合并)到最终输出中。

细节

pandas==0.23.4
numpy==1.15.2
scikit-learn==0.20.0

补充说明

这里展示的三种方法可能不是唯一的可能性......我相信还有其他方法可以做到这一点。

使用的来源

更新后的链接到binary.csv数据集


0

我不太明白您的意思,因为OneHotEncoder用于名义数据,而StandardScaler用于数值型数据。因此,您不应该同时使用它们处理您的数据。


请告诉我如何在名义数据(特别是字符串类型)上使用OneHotEncoder。 我非常需要这个功能,就像其他人一样。 - James Wong
你可以先使用LabelEncoder,然后再使用OneHotEncoder:`import numpy as np winds=np.array([['SE'],['NW'],['NW'],['NE'],['SE']])from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder int_encoded = LabelEncoder().fit_transform(winds[:,0]).reshape((len(winds),-1)) one_hot_encoded = OneHotEncoder(sparse=False).fit_transform(int_encoded)

你会得到这个结果:

array([[ 0., 0., 1.], [ 0., 1., 0.], [ 0., 1., 0.], [ 1., 0., 0.], [ 0., 0., 1.]])`
- Thierry Herrmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接