如何将sklearn fit_transform与pandas一起使用并返回数据框而不是numpy数组?


80

我想将缩放比例(使用来自sklearn.preprocessing的StandardScaler())应用于熊猫数据框。以下代码返回一个numpy数组,因此我丢失了所有列名和索引。这不是我想要的。

features = df[["col1", "col2", "col3", "col4"]]
autoscaler = StandardScaler()
features = autoscaler.fit_transform(features)

我在网上找到的“解决方案”是:

features = features.apply(lambda x: autoscaler.fit_transform(x))

它似乎可以工作,但是会导致弃用警告:

/usr/lib/python3.5/site-packages/sklearn/preprocessing/data.py:583:DeprecationWarning:在数据中0.1d中弃用一维数组,在0.19中会引发ValueError。如果数据具有单个功能,则使用X.reshape(-1,1)来重塑数据,如果包含单个样本,则使用X.reshape(1,-1)来重塑数据。

因此,我尝试:

features = features.apply(lambda x: autoscaler.fit_transform(x.reshape(-1, 1)))

但这给出了:

追溯(最近一次通话最近):文件“ ./analyse.py”,第91行,在features = features.apply(lambda x:autoscaler.fit_transform(x.reshape(-1,1)))中,文件“ / usr / lib / python3.5 / site-packages / pandas / core / frame.py“,第3972行,在apply返回self._apply_standard(f,axis,reduce = reduce)文件” /usr/lib/python3.5/site-包/pandas/core/frame.py”,第408行,在_apply_standard结果= self._constructor(data = results,index = index)文件“ /usr/lib/python3.5/site-packages/pandas/core/frame”中.py“,第226行, init mgr = self._init_dict(数据,索引,列,dtype = dtype)文件“ /usr/lib/python3.5/site-packages/pandas/core/frame.py”,第363行,在_init_dict dtype = dtype中) “ /usr/lib/python3.5/site-packages/pandas/core/frame.py”,第5163行,在_arrays_to_mgr数组中= _homogenize(数组,索引,dtype)文件“ /usr/lib/python3.5/site _homogenize的-packages / pandas / core / frame.py“行5477,raise_cast_failure = False)文件_sanitize_array的文件” /usr/lib/python3.5/site-packages/pandas/core/series.py“,行2885引发异常(“数据必须为一维”)异常:数据必须为一维

如何对熊猫数据框应用缩放,而保持数据框完整?如果可能,不复制数据。

Answers:


84

您可以使用将DataFrame转换为numpy数组as_matrix()。随机数据集上的示例:

编辑:根据上述文档的最后一句 更改as_matrix()values,(不会更改结果)as_matrix()

通常,建议使用“ .values”。

import pandas as pd
import numpy as np #for the random integer example
df = pd.DataFrame(np.random.randint(0.0,100.0,size=(10,4)),
              index=range(10,20),
              columns=['col1','col2','col3','col4'],
              dtype='float64')

注意,索引是10-19:

In [14]: df.head(3)
Out[14]:
    col1    col2    col3    col4
    10  3   38  86  65
    11  98  3   66  68
    12  88  46  35  68

现在fit_transform,DataFrame得到scaled_features array

from sklearn.preprocessing import StandardScaler
scaled_features = StandardScaler().fit_transform(df.values)

In [15]: scaled_features[:3,:] #lost the indices
Out[15]:
array([[-1.89007341,  0.05636005,  1.74514417,  0.46669562],
       [ 1.26558518, -1.35264122,  0.82178747,  0.59282958],
       [ 0.93341059,  0.37841748, -0.60941542,  0.59282958]])

将缩放后的数据分配给DataFrame(注意:使用indexcolumns关键字参数保留原始索引和列名:

scaled_features_df = pd.DataFrame(scaled_features, index=df.index, columns=df.columns)

In [17]:  scaled_features_df.head(3)
Out[17]:
    col1    col2    col3    col4
10  -1.890073   0.056360    1.745144    0.466696
11  1.265585    -1.352641   0.821787    0.592830
12  0.933411    0.378417    -0.609415   0.592830

编辑2:

来到了sklearn-pandas包装。它致力于使scikit-learn更易于与熊猫一起使用。 sklearn-pandas当您需要将一种以上类型的转换应用于DataFrame更为常见的情况的列子集时,此功能特别有用。它已记录在案,但这是您实现刚刚执行的转换的方式。

from sklearn_pandas import DataFrameMapper

mapper = DataFrameMapper([(df.columns, StandardScaler())])
scaled_features = mapper.fit_transform(df.copy(), 4)
scaled_features_df = pd.DataFrame(scaled_features, index=df.index, columns=df.columns)

1
谢谢您的回答,但是问题仍然是当从数组创建新的数据帧时,行会重新编号。原始数据帧不包含连续编号的行,因为其中一些已被删除。我想我也可以用旧的索引值添加一个index = [...]关键字。如果您相应地更新了答案,我可以接受。
Louic

希望该编辑对您有所帮助,我认为您关于从第一个df设置索引值的直觉是正确的。我使用的数字是连续的...(只是想表明您可以将它们重置为任何值,而我能想到的范围(10,20)是最好的。但是它可以与原始df上的任何随机索引一起使用。HTH!
凯文(Kevin)

2
我看到你有最后一步是转换的输出DataFrameMapperDataFrame..所以输出是不是已经一个DataFrame
StephenBoesch '17

@StephenBoesch:是的,输出不是DataFrame。如果您想直接从mapper获取它,则必须使用df_out=True选项DataFrameMapper
Nerxis

13
import pandas as pd    
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('your file here')
ss = StandardScaler()
df_scaled = pd.DataFrame(ss.fit_transform(df),columns = df.columns)

df_scaled将是“相同”数据帧,仅现在具有缩放值


1
但这并不能保持数据类型
leokury

1
因为那是缩放器的唯一输出,不是所有数据类型都会变成浮点数吗?您还期望从中得到什么?@leokury
jorijnsmit

5
features = ["col1", "col2", "col3", "col4"]
autoscaler = StandardScaler()
df[features] = autoscaler.fit_transform(df[features])

5
尽管此代码可以回答问题,但提供有关如何和/或为什么解决问题的其他上下文将提高​​答案的长期价值。
Piotr Labunski

0

您可以使用Neuraxle在scikit-learn中混合使用多种数据类型:

选项1:舍弃行名和列名

from neuraxle.pipeline import Pipeline
from neuraxle.base import NonFittableMixin, BaseStep

class PandasToNumpy(NonFittableMixin, BaseStep):
    def transform(self, data_inputs, expected_outputs): 
        return data_inputs.values

pipeline = Pipeline([
    PandasToNumpy(),
    StandardScaler(),
])

然后,按预期进行:

features = df[["col1", "col2", "col3", "col4"]]  # ... your df data
pipeline, scaled_features = pipeline.fit_transform(features)

选项2:保留原始的列名和行名

您甚至可以使用包装器来执行此操作:

from neuraxle.pipeline import Pipeline
from neuraxle.base import MetaStepMixin, BaseStep

class PandasValuesChangerOf(MetaStepMixin, BaseStep):
    def transform(self, data_inputs, expected_outputs): 
        new_data_inputs = self.wrapped.transform(data_inputs.values)
        new_data_inputs = self._merge(data_inputs, new_data_inputs)
        return new_data_inputs

    def fit_transform(self, data_inputs, expected_outputs): 
        self.wrapped, new_data_inputs = self.wrapped.fit_transform(data_inputs.values)
        new_data_inputs = self._merge(data_inputs, new_data_inputs)
        return self, new_data_inputs

    def _merge(self, data_inputs, new_data_inputs): 
        new_data_inputs = pd.DataFrame(
            new_data_inputs,
            index=data_inputs.index,
            columns=data_inputs.columns
        )
        return new_data_inputs

df_scaler = PandasValuesChangerOf(StandardScaler())

然后,按预期进行:

features = df[["col1", "col2", "col3", "col4"]]  # ... your df data
df_scaler, scaled_features = df_scaler.fit_transform(features)

-1

您可以尝试以下代码,这将为您提供一个带有索引的DataFrame

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston # boston housing dataset

dt= load_boston().data
col= load_boston().feature_names

# Make a dataframe
df = pd.DataFrame(data=dt, columns=col)

# define a method to scale data, looping thru the columns, and passing a scaler
def scale_data(data, columns, scaler):
    for col in columns:
        data[col] = scaler.fit_transform(data[col].values.reshape(-1, 1))
    return data

# specify a scaler, and call the method on boston data
scaler = StandardScaler()
df_scaled = scale_data(df, col, scaler)

# view first 10 rows of the scaled dataframe
df_scaled[0:10]

感谢您的回答,但是作为公认答案给出的解决方案要好得多。另外,也可以使用dask-ml完成: from dask_ml.preprocessing import StandardScaler; StandardScaler().fit_transform(df)
Louic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.