使用sklearn对pandas dataframe列进行缩放

243

我有一个混合类型的pandas数据框,我想对其中一些列应用sklearn的min_max_scaler转换。理想情况下,我想就地执行这些转换,但还没有找到方法。我编写了下面的代码,可以正常工作:

import pandas as pd
import numpy as np
from sklearn import preprocessing

scaler = preprocessing.MinMaxScaler()

dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],'B':[103.02,107.26,110.35,114.23,114.68], 'C':['big','small','big','small','small']})
min_max_scaler = preprocessing.MinMaxScaler()

def scaleColumns(df, cols_to_scale):
    for col in cols_to_scale:
        df[col] = pd.DataFrame(min_max_scaler.fit_transform(pd.DataFrame(dfTest[col])),columns=[col])
    return df

dfTest

    A   B   C
0    14.00   103.02  big
1    90.20   107.26  small
2    90.95   110.35  big
3    96.27   114.23  small
4    91.21   114.68  small

scaled_df = scaleColumns(dfTest,['A','B'])
scaled_df

A   B   C
0    0.000000    0.000000    big
1    0.926219    0.363636    small
2    0.935335    0.628645    big
3    1.000000    0.961407    small
4    0.938495    1.000000    small

我想知道这是否是进行此转换的首选/最有效的方法。 是否有一种我可以使用df.apply更好的方式?

我也很惊讶以下代码无法运行:

bad_output = min_max_scaler.fit_transform(dfTest['A'])

如果我将整个数据框传递给缩放器,它会起作用:

dfTest2 = dfTest.drop('C', axis = 1)
good_output = min_max_scaler.fit_transform(dfTest2)
good_output

我不明白为什么把一个序列传递给缩放器会失败。在我上面的完整工作代码中,我希望只需将序列传递给缩放器,然后将数据框列设置为已缩放的序列。


1
如果您这样做 bad_output = min_max_scaler.fit_transform(dfTest['A'].values),它能正常工作吗?访问 values 属性会返回一个 numpy 数组,由于某种原因,有时 scikit learn api 会正确调用使 pandas 返回 numpy 数组的方法,有时则不会。 - EdChum
Pandas的数据框是相当复杂的对象,其惯例与scikit-learn的惯例不匹配。如果将所有内容转换为NumPy数组,则使用scikit-learn变得更加容易。 - Fred Foo
@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values) 也不起作用。@larsmans - 是的,我曾考虑过这种方法,但似乎很麻烦。我不知道Pandas是否可以将整个数据框传递给sklearn函数,但不能传递系列。我理解数据框是系列的字典。在《Python数据分析》一书中阅读时,它指出pandas是构建在numpy之上,使其易于在以NumPy为中心的应用程序中使用。 - flyingmeatball
9个回答

353

我不确定之前版本的 pandas 是否会阻止这个操作,但现在下面的代码片段对我来说完美地工作,并且产生了你想要的结果,而无需使用 apply

>>> import pandas as pd
>>> from sklearn.preprocessing import MinMaxScaler


>>> scaler = MinMaxScaler()

>>> dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],
                           'B':[103.02,107.26,110.35,114.23,114.68],
                           'C':['big','small','big','small','small']})

>>> dfTest[['A', 'B']] = scaler.fit_transform(dfTest[['A', 'B']])

>>> dfTest
          A         B      C
0  0.000000  0.000000    big
1  0.926219  0.363636  small
2  0.935335  0.628645    big
3  1.000000  0.961407  small
4  0.938495  1.000000  small

143
不错!更通用的版本是 df[df.columns] = scaler.fit_transform(df[df.columns]) - citynorman
7
外层方括号是pandas典型的选择器,告诉pandas从数据框中选择一列。内层方括号表示一个列表,你正在将一个列表传递给pandas选择器。如果只使用单个方括号 - 一个列名后跟另一个列名,用逗号分隔 - pandas会将其解释为尝试从具有多级列(MultiIndex)的数据帧中选择列,并且会抛出关键字错误。 - ken
7
实用建议:对于使用训练/测试数据拆分的人来说,你需要仅在训练数据上进行拟合,而不是在测试数据上拟合。 - David J.
2
为了缩放除时间戳列之外的所有列,请结合使用以下代码:columns = df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]) - intotecho
2
更正@intotecho的评论。您应该执行columns = df.columns.drop('timestamps')df[columns] = scaler.fit_transform(df[columns])。方括号中应该是columns,而不是df.columns - JolonB
显示剩余8条评论

30
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

这应该可以在没有废弃警告的情况下正常工作。


4
df[df.columns] = scale.fit_transform(df)的翻译如下:将数据框df中所有列进行标准化处理,并将结果赋值回原数据框中。具体实现是使用scale对象的fit_transform()方法对df进行处理。 - shcrela
完美运作!我一直在尝试弄清如何保留列名,这很有帮助。 - Ammanuel
已测试可在版本1.5.1上运行。 - arilwan

26

像这样吗?

dfTest = pd.DataFrame({
           'A':[14.00,90.20,90.95,96.27,91.21],
           'B':[103.02,107.26,110.35,114.23,114.68], 
           'C':['big','small','big','small','small']
         })
dfTest[['A','B']] = dfTest[['A','B']].apply(
                           lambda x: MinMaxScaler().fit_transform(x))
dfTest

    A           B           C
0   0.000000    0.000000    big
1   0.926219    0.363636    small
2   0.935335    0.628645    big
3   1.000000    0.961407    small
4   0.938495    1.000000    small

3
运行这个脚本时,我收到了一堆弃用警告(DeprecationWarnings)。应该如何更新它? - pir
请参考@LetsPlayYahtzee下面的答案。 - AJP
4
一个更简单的版本:dfTest[['A','B']] = dfTest[['A','B']].apply(MinMaxScaler().fit_transform) - Alexandre V.
这将为每一行实例化一个新的MinMaxScaler,不确定是否有影响。 - ICW
这看起来有点过时了。 - paulduf

16

正如pir的评论中所提到的-.apply(lambda el: scale.fit_transform(el))方法将产生以下警告:

DeprecationWarning: 在0.17版本中,传递1d数组作为数据已被弃用,并且在0.19版本中将引发ValueError。 如果您的数据具有单个特征,请使用X.reshape(-1,1)进行重塑,如果包含单个样本,则使用X.reshape(1,-1)。

将您的列转换为numpy数组应该可以解决问题(我更喜欢StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

dfTest[['A','B','C']] = scale.fit_transform(dfTest[['A','B','C']].as_matrix())

-- 编辑 2018年11月(已在Pandas 0.23.4中测试)--

正如Rob Murray在评论中提到的,在当前(v0.23.4)版本的Pandas中,.as_matrix()会返回FutureWarning。因此,它应该被替换为.values

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

scaler.fit_transform(dfTest[['A','B']].values)

-- 编辑 2019年5月(已经测试使用pandas 0.24.2 版本)--

正如joelostblom在评论中提到的那样,“自从0.24.0版本以后,建议使用.to_numpy()而不是.values。”

更新的示例:

import pandas as pd
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
dfTest = pd.DataFrame({
               'A':[14.00,90.20,90.95,96.27,91.21],
               'B':[103.02,107.26,110.35,114.23,114.68],
               'C':['big','small','big','small','small']
             })
dfTest[['A', 'B']] = scaler.fit_transform(dfTest[['A','B']].to_numpy())
dfTest
      A         B      C
0 -1.995290 -1.571117    big
1  0.436356 -0.603995  small
2  0.460289  0.100818    big
3  0.630058  0.985826  small
4  0.468586  1.088469  small

1
使用.values替换.as_matrix(),因为as_matrix()现在会产生一个FutureWarning - Rob Murray
1
0.24.0版本开始,建议使用.to_numpy()代替.values来访问数据(详情请见:https://pandas.pydata.org/pandas-docs/stable/whatsnew/v0.24.0.html#accessing-the-values-in-a-series-or-index)。 - joelostblom

10
你只需要使用 pandas 就可以完成:
In [235]:
dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],'B':[103.02,107.26,110.35,114.23,114.68], 'C':['big','small','big','small','small']})
df = dfTest[['A', 'B']]
df_norm = (df - df.min()) / (df.max() - df.min())
print df_norm
print pd.concat((df_norm, dfTest.C),1)

          A         B
0  0.000000  0.000000
1  0.926219  0.363636
2  0.935335  0.628645
3  1.000000  0.961407
4  0.938495  1.000000
          A         B      C
0  0.000000  0.000000    big
1  0.926219  0.363636  small
2  0.935335  0.628645    big
3  1.000000  0.961407  small
4  0.938495  1.000000  small

6
我知道我可以只用pandas来做到这一点,但我可能想最终应用一个不像自己编写那么容易的不同sklearn方法。我更感兴趣的是弄清楚为什么将该方法应用于系列时不起作用,而不是只想出一个严格简单的解决方案。我的下一步将是运行一个RandomForestRegressor,并且我想确保我理解Pandas和sklearn如何共同工作。 - flyingmeatball
8
这个答案是危险的,因为 df.max() - df.min() 可能为 0,导致异常。此外,df.min() 计算了两次,效率低下。请注意,df.ptp() 等价于 df.max() - df.min() - Asclepius

8

我知道这是一个很老的评论,但还是要说一下:

不要使用单括号 (dfTest['A']),而要使用双括号 (dfTest[['A']])

例如:min_max_scaler.fit_transform(dfTest[['A']])

我相信这样可以得到所需的结果。


6
(适用于pandas版本 1.0.5 以下)
基于@athlonshi的答案(在C列上出现了“ ValueError:could not convert string to float:'big'”),以下是完整可运行的示例,不会出现警告:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
scale = preprocessing.MinMaxScaler()

df = pd.DataFrame({
           'A':[14.00,90.20,90.95,96.27,91.21],
           'B':[103.02,107.26,110.35,114.23,114.68], 
           'C':['big','small','big','small','small']
         })
print(df)
df[["A","B"]] = pd.DataFrame(scale.fit_transform(df[["A","B"]].values), columns=["A","B"], index=df.index)
print(df)

       A       B      C
0  14.00  103.02    big
1  90.20  107.26  small
2  90.95  110.35    big
3  96.27  114.23  small
4  91.21  114.68  small
          A         B      C
0  0.000000  0.000000    big
1  0.926219  0.363636  small
2  0.935335  0.628645    big
3  1.000000  0.961407  small
4  0.938495  1.000000  small

应为 "scale = MinMaxScaler()",而非 "scale = preprocessing.MinMaxScaler()"。 - yts61

2

使用 set_output(transform='pandas') 可以在 Sklearn >= 1.2 上运行。

import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler().set_output(transform='pandas') # set_output works from version 1.2

dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],
                       'B':[103.02,107.26,110.35,114.23,114.68], 
                       'C':['big','small','big','small','small']})

dfTest[['A', 'B']] = scaler.fit_transform(dfTest[['A', 'B']])
dfTest.head()

0

我尝试将min_max_scaler.fit_transform()应用于pd.DataFrame()的多列数据。

但是我收到了以下信息:

ValueError: Expected 2D array, got 1D array instead:
array=[0.31428571 0.32142857 0.288... 0.46428571]
Reshape your data either using array.reshape(-1, 1) if your data has a single feature...

我的数据只有一个特征(维度),所以以下方法可行:

columns_to_normalize = ['a', 'b']

min_max_scaler = preprocessing.MinMaxScaler()

for col in columns_to_normalize:
   df[col] = min_max_scaler.fit_transform(df[col].values.reshape(-1, 1) )
                                                 ^^^^^^^^^^^^^^^^^^^^^^

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