【问题标题】:What is correct syntax to swap column values for selected rows in a pandas data frame using just one line?仅使用一行来交换熊猫数据框中选定行的列值的正确语法是什么?
【发布时间】:2017-12-31 11:15:47
【问题描述】:

我在 Python 2.7.5 中使用 pandas 版本 0.14.1,并且我有一个包含三列的数据框,例如:

import pandas as pd

d = {'L':  ['left', 'right', 'left', 'right', 'left', 'right'],
     'R': ['right', 'left', 'right', 'left', 'right', 'left'],
     'VALUE': [-1, 1, -1, 1, -1, 1]}
df = pd.DataFrame(d)

idx = (df['VALUE'] == 1)

产生如下所示的数据框:

       L      R  VALUE
0   left  right     -1
1  right   left      1
2   left  right     -1
3  right   left      1
4   left  right     -1
5  right   left      1

对于VALUE == 1 的行,我想交换左右列的内容,这样所有“左”值都将在“L”列下结束,“右”值结束在“R”列下方。

已经在上面定义了idx 变量,我可以通过使用如下的临时变量,再用三行轻松完成此操作:

tmp = df.loc[idx,'L']
df.loc[idx,'L'] = df.loc[idx,'R']
df.loc[idx,'R'] = tmp

然而,这对我来说似乎是非常笨拙和不雅的语法;熊猫肯定支持更简洁的东西吗?我注意到,如果我将输入中的列顺序交换到数据框.loc 属性,那么我会得到以下交换输出:

In [2]: print(df.loc[idx,['R','L']])
      R      L
1  left  right
3  left  right
5  left  right

这表明我应该能够通过仅使用以下单行来实现与上述相同的交换:

df.loc[idx,['L','R']] = df.loc[idx,['R','L']]

但是,当我实际尝试此操作时,什么也没有发生 - 列保持未交换。就好像 pandas 自动识别出我在赋值语句右侧以错误的顺序放置了列,并自动纠正了问题。有没有办法可以在 pandas 赋值语句中禁用这种“列顺序自动更正”,以便在不创建不必要的临时变量的情况下实现交换?

【问题讨论】:

  • 你看过dataframe.eval吗?他们在熊猫文档中有示例: >>> df = DataFrame(randn(10, 2), columns=list('ab')) >>> df.eval('a + b') >>> df.eval( 'c = a + b')
  • 对于任何好奇的人,我已经发布了一个后续问题:stackoverflow.com/questions/25811529/…

标签: python pandas


【解决方案1】:

避免列名对齐的一种方法是通过.values 下拉到底层数组:

In [33]: df
Out[33]: 
       L      R  VALUE
0   left  right     -1
1  right   left      1
2   left  right     -1
3  right   left      1
4   left  right     -1
5  right   left      1

In [34]: df.loc[idx,['L','R']] = df.loc[idx,['R','L']].values

In [35]: df
Out[35]: 
      L      R  VALUE
0  left  right     -1
1  left  right      1
2  left  right     -1
3  left  right      1
4  left  right     -1
5  left  right      1

【讨论】:

  • 这似乎只有在系列 idx 具有 dtype bool 时才能正常工作。如果您的系列是值 0/1 而不是 True/False,请使用 idx.astype(bool) 转换为 dtype bool
【解决方案2】:

这里要注意的关键是,pandas 尝试使用索引和列名自动对齐行和列。因此,您需要以某种方式告诉 pandas 忽略此处的列名。一种方法是 @DSM 所做的,通过转换为 numpy 数组。另一种方法是重命名列:

>>> df.loc[idx] = df.loc[idx].rename(columns={'R':'L','L':'R'})

      L      R  VALUE
0  left  right     -1
1  left  right      1
2  left  right     -1
3  left  right      1
4  left  right     -1
5  left  right      1

【讨论】:

  • 感谢您的回复;自从他首先回答以来,我给 DSM 提供了“已接受答案”状态,但我发现您的回答也很有帮助(因此也赞成它!)。顺便说一句,我认为您的第一种方法可能有错字;在我的系统上,使用 pandas 0.14.1 和 Python 2.7.5,如果我改为添加对 zip 的调用,它就可以工作;即df.ix[idx,['L','R']] = zip(df.ix[idx,'R'], df.ix[idx,'L'])。第二种方法效果很好,非常感谢!对于第三种方法,我认为问题在于df[['R','L']] on the right side is 6 rows long, while the df.loc[idx,['L','R']]`只有3行。
  • 感谢您的反馈。方法 1 在没有 zip 的情况下对我来说很好(如果我添加了 zip)。不知道为什么我们会得到不同的结果。我有 pandas 14.1 和 python 2.7.7(anaconda 发行版)。在 Windows 7 下运行。
  • df.update(df.loc[m].rename({'L': 'R', 'R': 'L'}, axis=1)) 也可以。
【解决方案3】:

您也可以使用np.selectdf.where 来执行此操作,即

选项 1np.select

df[['L','R']] = pd.np.select(df['VALUE'] == 1, df[['R','L']].values, df[['L','R']].values)

选项 2df.where

df[['L','R']] = df[['R','L']].where(df['VALUE'] == 1, df[['L','R']].values)

选项 3df.mask

df[['L','R']] = df[['L','R']].mask( df['VALUE'] == 1, df[['R','L']].values)

输出:

    L      R  VALUE
0  left  right     -1
1  left  right      1
2  left  right     -1
3  left  right      1
4  left  right     -1
5  left  right      1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-11
    • 2018-06-05
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    • 1970-01-01
    • 2016-07-27
    相关资源
    最近更新 更多