将多个 StandardScaler 应用于单个组?

是否有一种 pythonic 方法可以将 sklearn 的 StandardScaler 实例链接在一起以独立地按组缩放数据?即,如果我想找到独立缩放 iris 数据集的特征;我可以使用以下代码:


from sklearn.datasets import load_iris

data = load_iris()

df = pd.DataFrame(data['data'], columns=data['feature_names'])

df['class'] = data['target']


means = df.groupby('class').mean()

stds = df.groupby('class').std()


df_rescaled = (

    (df.drop(['class'], 1) - means.reindex(df['class']).values) / 

     stds.reindex(df['class']).values)

在这里,我分别减去平均值并除以每个组的标准差。但是它有点难以实现这些手段和标准差,并且基本上复制了StandardScaler当我有一个我想要控制的分类变量时的行为。


有没有更pythonic / sklearn友好的方式来实现这种类型的缩放?


不负相思意
浏览 160回答 2
2回答

郎朗坤

当然,您可以使用任何sklearn操作并将其应用于groupby对象。首先,一个方便的包装器:import typingimport pandas as pdclass SklearnWrapper:    def __init__(self, transform: typing.Callable):        self.transform = transform    def __call__(self, df):        transformed = self.transform.fit_transform(df.values)        return pd.DataFrame(transformed, columns=df.columns, index=df.index)这将应用sklearn您传递给它的任何变换到一个组。最后简单的用法:from sklearn.datasets import load_irisfrom sklearn.preprocessing import StandardScalerdata = load_iris()df = pd.DataFrame(data["data"], columns=data["feature_names"])df["class"] = data["target"]df_rescaled = (    df.groupby("class")    .apply(SklearnWrapper(StandardScaler()))    .drop("class", axis="columns"))编辑:你几乎可以用SklearnWrapper. 这是为每个组转换和反转此操作的示例(例如,不要覆盖转换对象) - 每次看到新组时只需重新拟合对象(并将其添加到list)。sklearn's为了更容易使用,我复制了一些功能(您可以通过将适当的传递string给_call_with_function内部方法来使用您想要的任何功能扩展它):class SklearnWrapper:    def __init__(self, transformation: typing.Callable):        self.transformation = transformation        self._group_transforms = []        # Start with -1 and for each group up the pointer by one        self._pointer = -1    def _call_with_function(self, df: pd.DataFrame, function: str):        # If pointer >= len we are making a new apply, reset _pointer        if self._pointer >= len(self._group_transforms):            self._pointer = -1        self._pointer += 1        return pd.DataFrame(            getattr(self._group_transforms[self._pointer], function)(df.values),            columns=df.columns,            index=df.index,        )    def fit(self, df):        self._group_transforms.append(self.transformation.fit(df.values))        return self    def transform(self, df):        return self._call_with_function(df, "transform")    def fit_transform(self, df):        self.fit(df)        return self.transform(df)    def inverse_transform(self, df):        return self._call_with_function(df, "inverse_transform")用法(组变换,逆运算并再次应用):data = load_iris()df = pd.DataFrame(data["data"], columns=data["feature_names"])df["class"] = data["target"]# Create scaler outside the classscaler = SklearnWrapper(StandardScaler())# Fit and transform data (holding state)df_rescaled = df.groupby("class").apply(scaler.fit_transform)# Inverse the operationdf_inverted = df_rescaled.groupby("class").apply(scaler.inverse_transform)# Apply transformation once againdf_transformed = (    df_inverted.groupby("class")    .apply(scaler.transform)    .drop("class", axis="columns"))

RISEBY

我更新了@Szymon Maszke 代码:class SklearnWrapper:def __init__(self, transformation: typing.Callable):    self.transformation = transformation    self._group_transforms = []    # Start with -1 and for each group up the pointer by one    self._pointer = -1def _call_with_function(self, df: pd.DataFrame, function: str):    # If pointer >= len we are making a new apply, reset _pointer    if self._pointer == len(self._group_transforms)-1 and function=="inverse_transform":        self._pointer = -1    self._pointer += 1    print(self._pointer)    return pd.DataFrame(        getattr(self._group_transforms[self._pointer], function)(df.values),        columns=df.columns,        index=df.index,    )def fit(self, df):    scaler = copy(self.transformation)    self._group_transforms.append(scaler.fit(df.values))    return selfdef transform(self, df):    return self._call_with_function(df, "transform")def fit_transform(self, df):    self.fit(df)    return self.transform(df)def inverse_transform(self, df):    return self._call_with_function(df, "inverse_transform")StandardScaler()中没有正确存储_group_transforms,所以我创建了一个副本(使用副本库)并存储它(也许使用 OOP 有更好的方法来做到这一点)。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python