【问题标题】:WHY is `rename` with selection of columns not working with a lambda function?为什么选择列的“重命名”不能与 lambda 函数一起使用?
【发布时间】:2021-05-12 11:20:16
【问题描述】:

我想使用 rename 使用 lambda 函数重命名列的选定部分

import pandas as pd


df = pd.DataFrame({'pre_col1': [1, 2],
                   'pre_col2': [3, 4],
                   'pre_col3': [ 3,  29],
                   'pre_col4': [94, 170],
                   'pre_col5': [31, 115]})

# This works but it renames all of them
# df.rename(columns=lambda x: x.replace('pre_', ''))

# I'm only wanting to edit and rename a selection
df.iloc[:, 2:5] = (df.iloc[:, 2:5]
                     .rename(columns=lambda x: x.replace('pre_', '')))

print(df)

这会产生

   pre_col1  pre_col2  pre_col3  pre_col4  pre_col5
0       1.0       3.0       NaN       NaN       NaN
1       2.0       4.0       NaN       NaN       NaN

我知道重命名列的方法有很多。我读过hereherehere

但为什么这种方式不起作用?为什么它会填满我试图用NaNs 更改的列??

【问题讨论】:

  • 链接对我来说很好用 df.rename(columns=lambda x: x.replace("pre_","")).assign(x=1).rename(columns=lambda x: x.replace("c","cc")).rename(columns=lambda x: x.replace("cc","post_c")) 一定是你的链中改变值的东西
  • 您是否运行了问题中的确切代码?
  • @LeviBaguley 我运行了您的确切代码,但没有得到带有 NaN 的最终输出。在此输出之前您是否运行了任何其他步骤?

标签: python pandas rename


【解决方案1】:

此答案旨在提示 OP 的问题“为什么这种方式不起作用?”而不是提供 OP 已经从其他帖子中获得的替代可行解决方案。

rename 部分使df.iloc[:, 2:5].rename(columns=lambda x: x.replace('pre_', '')) 作为一个整体产生了一个副本而不是一个视图。从pandas用户指南here可以看出,有一句话说明:

rename() 方法还提供了一个就地命名参数,即 默认为 False 并复制基础数据。将 inplace=True 传递给 原地重命名数据。

DataFrame.rename 的 pandas API 参考也指出:

返回:DataFrame 或 None ~~~带有重命名轴标签的 DataFrame,如果 inplace=True,则返回 None。

rename(没有 inplace=True)返回一个副本而不是一个视图这一事实可以验证如下:

df.iloc[:, 2:5].rename(columns=lambda x: x.replace('pre_', ''))._is_view

Output:  False

虽然没有重命名部分:

df.iloc[:, 2:5]._is_view

Output:  True

因此,您的代码仅重命名了副本而没有触及原始 df。

让我们再举一个例子:

    data = pd.DataFrame(np.arange(9).reshape((3, 3)), index=['apple', 'orange', 'pear'], columns=['one', 'two', 'three'])

    data.rename(index=str.title, columns=str.upper)
    Output:

          ONE   TWO THREE
    Apple   0     1     2
    Orange  3     4     5
    Pear    6     7     8


    data                            # not changed after rename()
    Output:

             one    two three
    apple      0      1     2
    orange     3      4     5
    pear       6      7     8

奇怪的是,在我在重命名代码之后打印 df 本身的试运行中,df 显示的是原始值,而不是最后 3 列替换为 NaN。你可以重新运行你的代码来看看。

【讨论】:

    【解决方案2】:
    1. 因为索引的不变性。 2) 正如@SeaBean 所建议的,这些更改也发生在数据帧的副本中(可能是因为 1)),这些只是在副本中。

    选项 1) 更改列名。

    import pandas as pd
    df = pd.DataFrame({'pre_col1': [1, 2],
                       'pre_col2': [3, 4],
                       'pre_col3': [ 3,  29],
                       'pre_col4': [94, 170],
                       'pre_col5': [31, 115]})
    columns_to_modify = df.columns.tolist()[ 2:5]
    columns_rename = {}
    for i in columns_to_modify:
        columns_rename[i] =  i.replace('pre_', '')
    
    df.rename(columns=columns_rename,inplace = True)
    print(df)
       pre_col1  pre_col2  col3  col4  col5
    0         1         3     3    94    31
    1         2         4    29   170   115
    

    选项 2) 更改列名。

    import pandas as pd
    df = pd.DataFrame({'pre_col1': [1, 2],
                       'pre_col2': [3, 4],
                       'pre_col3': [ 3,  29],
                       'pre_col4': [94, 170],
                       'pre_col5': [31, 115]})
    df.columns.values[2:5] = list(map(lambda x: x.replace('pre_', '') ,df.columns.tolist()[2:5]))
    df
       pre_col1  pre_col2  col3  col4  col5
    0         1         3     3    94    31
    1         2         4    29   170   115
    

    我认为,df.iloc[:, 2:5] = df.iloc[:, 2:5].rename(columns=lambda x: x.replace('pre_', '')) 最初的困难可能是由于数据帧中索引的不变性,如下所示:

    1. Pandas TypeError: Index does not support mutable operations
    2. Regarding the immutability of pandas dataframe indexes
    3. Pandas: Change a specific column name in dataframe having multilevel columns 从这些看来,数据帧索引是不可变的,因此它们是同时设置的,并故意保持这种状态。 有趣的是,索引似乎是不可变的,但是您可以像在第二个选项中那样更改值。

    【讨论】:

    • 当然。 a)在重命名中,您应该引入一个列字典,并且我不确定 lambda 函数是否有有效的变量输入。注意在上面的例子中,是一个字典。
    • b) 另外我看不到...的 () = (df.iloc ...
    • columns 参数可以是 dict-like 或函数。请参阅文档here
    • 我认为答案是数据帧中索引的不变性。如上所述。
    【解决方案3】:

    在 lambda 函数中,您可以使用 if/else 概念重命名列:

    df.rename(columns=lambda x: x.split("_")[-1]
                                if int(x[-1]) in range(3, 6) 
                                else x)
    
        pre_col1    pre_col2    col3    col4    col5
    0         1           3       3       94    31
    1         2           4       29      170   115
    

    坚持您的代码,if/else 概念有效:

    df.rename(columns=lambda x: x.replace("pre_", "") 
                                if int(x[-1]) in range(3, 6) 
                                else x)
    

    您可以简单地重新分配给旧数据框:

    df = df.rename(columns=lambda x: x.replace("pre_", "") 
                                    if int(x[-1]) in range(3, 6) 
                                    else x)
    

    【讨论】:

    • 请注意,新名称不在“旧”/以前的数据框 df 中。正如@SeaBean 所建议的那样,这些只是副本。
    • 您可以简单地重新分配结果。
    • 这可能是一个解决方案:)
    • 整洁,但这仅适用于我提供的玩具数据,因为它假定您将在列名的末尾有一个数字索引。
    猜你喜欢
    • 1970-01-01
    • 2014-04-13
    • 2015-01-23
    • 2019-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    相关资源
    最近更新 更多