使用 Sklearn 的 MinMaxScaler 逐行缩放

问题描述 投票:0回答:3

默认情况下,Sklearn 的缩放器按列工作。但我需要按行缩放数据,所以我执行了以下操作:

from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import numpy as np

# %% Generating sample data

x = np.array([[-1, 4, 2], [-0.5, 8, 9], [3, 2, 3]])
y = np.array([1, 2, 3])

#%% Train/Test split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=2)

scaler = MinMaxScaler()
x_train = scaler.fit_transform(x_train.T).T # scaling line-wise
x_test = scaler.transform(x_test) <-------- Error here

但是我收到以下错误:

ValueError: X has 3 features, but MinMaxScaler is expecting 2 features as input.

我不明白这里出了什么问题。为什么它说它期待 2 个特征,而我的所有 X(x、x_train 和 x_test)都有 3 个特征?我该如何解决这个问题?

scikit-learn normalization train-test-split
3个回答
1
投票

StandardScaler
是有状态的:当您拟合它时,它会计算并保存列的平均值和标准差;当转换(训练测试集)时,它使用那些保存的统计数据。您的转置技巧对此不起作用:每个row都保存了统计信息,然后您的测试集没有相同的行,因此转换无法正常工作(如果行数不同,则会抛出错误,或者默默地错误 -如果行数相同则缩放)。

你想要的不是有状态的:测试集应该完全独立于训练集进行转换。事实上,每一行都应该彼此独立地进行转换。所以你可以在分割之前进行这种转换,或者在测试集(转置)上使用

fit_transform

对于行的 l2 标准化,有一个内置函数:

Normalizer
(docs)。我不认为有最小-最大归一化的类似物,但我认为你可以写一个
FunctionTransformer
来做到这一点。


1
投票

这是可以做到的。我可以想到一个场景,这会很有用。通常,

MinMaxScaler
会相对于该特征的其他观测值缩放每个
x
y
z
。这就是“系列”缩放。现在想象一下,您想要映射受
x+y+z = 1
约束的每个点。我认为这就是OP所要求的。我过去曾经这样做过,我将描述我是如何做到的。

您需要将个人观察结果视为列多索引,并将其视为高维特征。然后,您需要构建一个管道,在其中将观察结果从列方式转换为行方式,然后进行最小/最大缩放。这会让您得到 x+y+z=1,但您仍然需要返回到数据的原始形状,为此您需要跟踪每个观察的索引。在管道中,您需要使用类似

DataframeFunctionTransformer
的东西,我在互联网上看到过它,在下面复制它。这样您就可以使用 pandas 函数来调整数据并与索引合并回来。

class DataframeFunctionTransformer():                                                                                                                                       
    def __init__(self, func):                                                                                                                                               
        self.func = func                                                                                                                                                    
                                                                                                                                                                            
    def transform(self, input_df, **transform_params):                                                                                                                      
        return self.func(input_df)                                                                                                                                          
                                                                                                                                                                            
    def fit(self, X, y=None, **fit_params):                                                                                                                                 
        return self                                                                                                                                                         
                                   

关于

MinMaxScaler
的状态性,我认为在这样的场景中,
MinMaxScaler
的状态不会被使用,它纯粹充当变压器,将这些点映射到满足
约束的不同空间x
y
z
进行缩放,使其加起来为 1。

@Murilo 希望这能让您开始找到解决方案。一定是一个有趣的问题。


0
投票

我一直在努力解决同样的问题,所以整理了一个课程:

  1. 每次调用都使用新的转换器实例以避免状态问题
  2. 可选择转置输入和输出数据以使用逐行特征变换器
class StatelessTransformer(TransformerMixin, BaseEstimator):
    def __init__(self, transformer_class: TransformerMixin, sample_wise: bool = True):
        """
        Create a stateless transformer from a statefull transformer class.

        By default, `transform` transposes input and output data to apply feature-wise transformers to samples (rows).
        Allows use of feature-wise, stateful transformers in an sklearn `Pipeline`.

        Examples
        --------

        >>> import numpy as np
        >>> from sklearn.pipeline import Pipeline
        >>> from sklearn.preprocessing import MaxAbsScaler
        >>> pipeline = Pipeline([("sample_scaler", StatelessTransformer(MaxAbsScaler))])
        >>> X = np.array([[0, 1, 2], [3, 4, 5]])
        >>> pipeline.transform(X)
        array([[0. , 0.5, 1. ],
               [0.6, 0.8, 1. ]])

        >>> pipeline = Pipeline([("sample_scaler", StatelessTransformer(MaxAbsScaler, sample_wise=False))])
        >>> pipeline.transform(X)
        array([[0.  , 0.25, 0.4 ],
               [1.  , 1.  , 1.  ]])
        """
        self.transformer_class = transformer_class
        self.sample_wise = sample_wise

    def fit(self, X, y=None, **fit_params):
        """Stateless, so no fit operation."""
        return self

    def transform(self, X):
        """
        Calls `.fit_transform` from a new instance of the stateful transformer class, so that no state is held.
        Note that default fit parameters are used.
        """
        if self.sample_wise:
            return self.transformer_class().fit_transform(X.T).T
        return self.transformer_class().fit_transform(X)
© www.soinside.com 2019 - 2024. All rights reserved.