【问题标题】:Why does .loc assignment with two sets of brackets result in NaN in a pandas.DataFrame?为什么带有两组括号的 .loc 分配会导致 pandas.DataFrame 中的 NaN?
【发布时间】:2021-12-25 11:56:03
【问题描述】:

我有一个数据框:

name age
0 Paul 25
1 John 27
2 Bill 23

我知道如果我输入:

df[['name']] = df[['age']]

我会得到以下内容:

name age
0 25 25
1 27 27
2 23 23

但我希望命令的结果相同

df.loc[:, ['name']] = df.loc[:, ['age']]

但是,我得到了这个:

name age
0 NaN 25
1 NaN 27
2 NaN 23

出于某种原因,如果我省略列名周围的方括号[],我会得到我所期望的。那是命令:

df.loc[:, 'name'] = df.loc[:, 'age']

给出正确的结果:

name age
0 25 25
1 27 27
2 23 23

为什么两对带有 .loc 的括号会导致 NaN? 这是某种错误还是预期的行为?我无法弄清楚这种行为的原因。

【问题讨论】:

    标签: python pandas dataframe nan series


    【解决方案1】:

    From the Docs Pandas Data Alignment(emphasis mine):

    pandas 在从 .loc 和 .iloc 设置 Series 和 DataFrame 时对齐所有 AXES

    您可以在Basics header 下找到标有警告的这段摘录。 他们已经举了一个例子来解释。

    In [9]: df[['A', 'B']]
    Out[9]: 
                       A         B
    2000-01-01 -0.282863  0.469112
    2000-01-02 -0.173215  1.212112
    2000-01-03 -2.104569 -0.861849
    2000-01-04 -0.706771  0.721555
    2000-01-05  0.567020 -0.424972
    2000-01-06  0.113648 -0.673690
    2000-01-07  0.577046  0.404705
    2000-01-08 -1.157892 -0.370647
    
    In [10]: df.loc[:, ['B', 'A']] = df[['A', 'B']]
    
    In [11]: df[['A', 'B']]
    Out[11]: 
                       A         B
    2000-01-01 -0.282863  0.469112
    2000-01-02 -0.173215  1.212112
    2000-01-03 -2.104569 -0.861849
    2000-01-04 -0.706771  0.721555
    2000-01-05  0.567020 -0.424972
    2000-01-06  0.113648 -0.673690
    2000-01-07  0.577046  0.404705
    2000-01-08 -1.157892 -0.370647
    

    来自文档(重点是我的)

    这将不会修改 df,因为列对齐是在赋值之前。

    明确避免自动对齐

    当您需要在没有索引的情况下执行某些操作(例如禁用自动对齐)时,访问数组会很有用。

    当 LHS 和 RHS 是数据帧时,对齐就会起作用。为避免对齐尝试使用。

    df.loc[:, ['B', 'A']] = df[['A', 'B']].to_numpy()
    

    你手头有两个案子,

    • .loc 分配与pd.DataFrame
    • .loc 在 EDIT 中分配 pd.Series

    .loc 分配在pd.DataFrame

    pd.DataFrame 中有两个轴indexcolumns。所以,当你这样做时

    df.loc[:, ['name']] = df.loc[:, ['age']]
    

    LHS 的列 A 与 RHS 列 B 不对齐,因此在分配后导致所有 NaN

    来自文档DataAlignment(强调我的)

    DataFrame 对象之间的数据对齐自动在列和索引(行标签)上对齐。同样,生成的对象将具有列标签和行标签的并集。

    如果不是全部,您可以在大多数 pandas 的操作中找到这种行为。例如,加法,减法,乘法等。不匹配的索引和列用NaN填充。

    数据对齐和算法示例

    df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])
    df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])
    
    df + df2 
    
             A         B         C   D
    0  0.045691 -0.014138  1.380871 NaN
    1 -0.955398 -1.501007  0.037181 NaN
    2 -0.662690  1.534833 -0.859691 NaN
    3 -2.452949  1.237274 -0.133712 NaN
    4  1.414490  1.951676 -2.320422 NaN
    5 -0.494922 -1.649727 -1.084601 NaN
    6 -1.047551 -0.748572 -0.805479 NaN
    7       NaN       NaN       NaN NaN
    8       NaN       NaN       NaN NaN
    9       NaN       NaN       NaN NaN
    

    回答你的comment

    但是为什么列索引需要匹配呢?我可以看到为什么要匹配行索引,但为什么要匹配列索引?

    让我们看一下上面的例子,如果列不对齐,你将如何添加两个 DataFrame?在列和索引上对齐它们是有意义的。


    .locpd.Series 中的分配

    pd.Series 只有一个轴,即index。这就是为什么它在你这样做时起作用的原因

    df.loc[:, 'name'] = df.loc[:, 'age']
    

    由于 pd.Series 只有一个轴,pandas 尝试对齐 index 并且成功了。当然,如果index 不对齐,则会产生NaN 值。

    From Docs Series Alignment(emphasis mine):

    未对齐系列之间的操作的结果将包含所涉及的索引的并集。如果在一个系列或另一个系列中未找到标签结果将被标记为缺少NaN

    【讨论】:

    • 感谢您如此详细的回答!但是您能否澄清一下,为什么df[['name']] = df[['age']] 不是这种情况?我的意思是 df[['name']]df[['age']] 返回 pd.DataFrame 对象。但是这个赋值的结果是正确的(没有NaNs)。为什么在这种情况下不再对齐?
    • @mathgeek 对齐仅在使用 .loc.iloc 进行设置/分配时才会生效。
    • @mathgeek 您可以使用df[['A', 'B']] = df[['B', 'A']] 来验证这一点会交换列,但是当与.loc 一起使用时,即df.loc[:, ['B', 'A']] = df[['A', 'B']] 它们不会交换`
    【解决方案2】:

    当您使用双括号 [[]] 时,您正在分配一个 DataFrame。你想要的是分配一个(列)系列,为此你只使用一个括号 []。

    这里有一些代码:

    import pandas as pd
    df = pd.DataFrame({'name':['Paul','John','Bill'], 'age':[25,27,23]})
    print('Inital Dataframe:\n',df)
    
    df[['name']] = df[['age']]
    print("\ndf[['name']] = df[['age']]\n",df)
    
    print("df.loc[:, ['age']]:", type(df.loc[:, ['age']]))
    print("df.loc[:, ['name']]:", type(df.loc[:, ['name']]))
    df.loc[:, ['name']] = df.loc[:, ['age']]
    print("\ndf.loc[:, ['name']] = df.loc[:, ['age']]\n",df)
        
    print('=======================')
    df = pd.DataFrame({'name':['Paul','John','Bill'], 'age':[25,27,23]})
    print('Inital Dataframe:\n',df)
    
    print("type(df.loc[:, 'age']):", type(df.loc[:, 'age']))
    print("type(df.loc[:, 'name']):", type(df.loc[:, 'name']))
    df.loc[:, 'name'] = df.loc[:, 'age']
    print("\ndf.loc[:, 'name'] = df.loc[:, 'age']\n",df)
    

    还有输出:

    Inital Dataframe:
        name  age
    0  Paul   25
    1  John   27
    2  Bill   23
    
    df[['name']] = df[['age']]
        name  age
    0    25   25
    1    27   27
    2    23   23
    df.loc[:, ['age']]: <class 'pandas.core.frame.DataFrame'>
    df.loc[:, ['name']]: <class 'pandas.core.frame.DataFrame'>
    
    df.loc[:, ['name']] = df.loc[:, ['age']]
        name   age
    0   NaN  25.0
    1   NaN  27.0
    2   NaN  23.0
    =======================
    Inital Dataframe:
        name  age
    0  Paul   25
    1  John   27
    2  Bill   23
    type(df.loc[:, 'age']): <class 'pandas.core.series.Series'>
    type(df.loc[:, 'name']): <class 'pandas.core.series.Series'>
    
    df.loc[:, 'name'] = df.loc[:, 'age']
        name  age
    0    25   25
    1    27   27
    2    23   23
    

    但是,这是另一个奇怪的行为:将双括号分配给差异变量,例如 df1df2,然后 df1 = df2 有效! 这里还有一些代码:

    df = pd.DataFrame({'name':['Paul','John','Bill'], 'age':[25,27,23]})
    print('Inital Dataframe:\n',df)
    
    df1 = df.loc[:, ['name']]
    df2 = df.loc[:, ['age']]
    print("\ndf1 = df.loc[:, ['name']]\n",df1)
    print("\ndf2 = df.loc[:, ['age']]\n",df2)
    
    df1=df2
    print("\ndf1=df2\ndf1:\n",df1)
    

    还有输出:

    Inital Dataframe:
        name  age
    0  Paul   25
    1  John   27
    2  Bill   23
    
    df1 = df.loc[:, ['name']]
        name
    0  Paul
    1  John
    2  Bill
    
    df2 = df.loc[:, ['age']]
        age
    0   25
    1   27
    2   23
    
    df1=df2
    df1:
        age
    0   25
    1   27
    2   23
    

    【讨论】:

    • 感谢您的回答!但我询问了双括号的行为,因为那是我的问题。我只使用了一个列名来简化示例。
    【解决方案3】:

    这是因为对于 loc 分配,所有索引轴都对齐,包括列:由于 agename 不匹配,因此没有要分配的数据,因此是 NaN。

    您可以通过重命名列来使其工作:

    df.loc[:, ["name"]] = df.loc[:, ["age"]].rename(columns={"age": "name"})
    

    或通过访问 numpy 数组:

    df.loc[:, ["name"]] = df.loc[:, ["age"]].values
    

    【讨论】:

    • 感谢您的回答!但是为什么列索引需要匹配呢?我可以看到为什么要匹配行索引,但为什么要匹配列索引?
    • 我可以看到这对于更大的桌子来说是多么方便。无论如何,您仍然可以在没有列对齐的情况下分配其他选项,因此这不是约束。
    • 您能否举一个小例子,说明这对于较大的表来说如何方便?
    猜你喜欢
    • 2016-09-03
    • 2013-10-04
    • 2015-11-22
    • 1970-01-01
    • 2019-11-15
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多