【问题标题】:使用 sklearn 缩放的 pandas 数据框列
【发布时间】:2014-08-29 23:24:48
【问题描述】:

我有一个带有混合类型列的 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

我很困惑为什么将系列传递给缩放器会失败。在我上面的完整工作代码中,我希望只将一个系列传递给缩放器,然后将数据框列 = 设置为缩放系列。我在其他几个地方看到过这个问题,但没有找到好的答案。任何帮助理解这里发生的事情将不胜感激!

【问题讨论】:

  • 如果您这样做bad_output = min_max_scaler.fit_transform(dfTest['A'].values) 是否有效?访问 values 属性会返回一个 numpy 数组,由于某种原因,有时 scikit learn api 会正确调用正确的方法,使 pandas 返回一个 numpy 数组,但有时它不会。
  • Pandas 的数据帧是相当复杂的对象,其约定与 scikit-learn 的约定不符。如果将所有内容都转换为 NumPy 数组,scikit-learn 会更容易使用。
  • @edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values) 也不起作用。 @larsmans - 是的,我曾想过沿着这条路走,这似乎很麻烦。我不知道 Pandas 可以将完整的数据帧传递给 sklearn 函数,但不是一个系列,这是否是一个错误。我对数据框的理解是它是一个系列的字典。在“Python for Data Analysis”一书中阅读,它指出 pandas 是建立在 numpy 之上的,使其易于在以 NumPy 为中心的应用程序中使用。

标签: python pandas scikit-learn dataframe


【解决方案1】:

我不确定 pandas 以前的版本是否阻止了这种情况,但现在以下 sn-p 对我来说非常有效,并且无需使用 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

【讨论】:

  • 整洁!更通用的版本df[df.columns] = scaler.fit_transform(df[df.columns])
  • @RajeshThevar 外括号是 pandas 的典型选择器括号,告诉 pandas 从数据框中选择一列。内括号表示一个列表。您正在将一个列表传递给 pandas 选择器。如果您只使用单括号 - 一个列名后跟另一个,用逗号分隔 - pandas 会将此解释为您尝试从具有多级列(MultiIndex)的数据框中选择一列,并会抛出一个 keyerror .
  • 添加到@ken 的答案如果您想确切了解pandas 如何实现此索引逻辑以及为什么值元组的解释与列表不同,您可以查看DataFrames 如何实现__getitem__方法。具体来说,您可以打开 ipython 并执行 pd.DataFrame.__getitem__?? ;当然,在您将 pandas 导入为 pd 之后;)
  • 实用说明:对于那些使用训练/测试数据拆分的人,您只需要适应训练数据,而不是测试数据。
  • 要缩放除时间戳列之外的所有列,请结合columns =df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]
【解决方案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']] = 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

【讨论】:

  • 我在运行这个脚本时收到一堆 DeprecationWarnings。应该如何更新?
  • 见下面@LetsPlayYahtzee 的回答
  • 更简单的版本:dfTest[['A','B']] = dfTest[['A','B']].apply(MinMaxScaler().fit_transform)
【解决方案3】:
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

这应该可以在没有折旧警告的情况下工作。

【讨论】:

  • df[df.columns] = scale.fit_transform(df)
  • 完美运行!我试图弄清楚如何保留列名,这很有帮助。
【解决方案4】:

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

DeprecationWarning:将一维数组作为数据传递在 0.17 中已弃用 并将在 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 在 cmets 中提到的,在当前 (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 在 cmets 中提到的,“由于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

【讨论】:

【解决方案5】:

您只能使用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

【讨论】:

  • 我知道我可以在 pandas 中做到这一点,但我可能希望最终应用一种不同的 sklearn 方法,这种方法并不像我自己那样容易编写。我更感兴趣的是弄清楚为什么申请一个系列不能像我预期的那样工作,而不是想出一个严格简单的解决方案。我的下一步将是运行一个 RandomForestRegressor,我想确保我了解 Pandas 和 sklearn 是如何协同工作的。
  • 这个答案很危险,因为df.max() - df.min() 可以为0,导致异常。此外,df.min() 被计算了两次,效率很低。注意df.ptp() 等价于df.max() - df.min()
【解决方案6】:

我知道这是一个非常古老的评论,但仍然:

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

即:min_max_scaler.fit_transform(dfTest[['A']]).

我相信这会产生预期的结果。

【讨论】:

    【解决方案7】:

    (已针对 pandas 1.0.5 进行测试)
    基于@athlonshi 的回答(它有 ValueError: could not convert string to float: 'big', on C column),没有警告的完整工作示例:

    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
    

    【讨论】:

      猜你喜欢
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 2017-12-20
      • 2021-08-20
      • 2019-11-24
      • 2017-12-04
      • 2018-12-29
      相关资源
      最近更新 更多