Что такое правильный синтаксис для обмена значениями столбцов для выбранных строк в кадре данных pandas, используя только одну строку?

Я использую pandas версию 0.14.1 с Python 2.7.5, и у меня есть кадр данных с тремя столбцами, например:

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

однако это кажется действительно неуклюжим и неэлегантным синтаксисом для меня; неужели pandas поддерживает что-то более кратким? Я заметил, что если я поменяю порядок столбцов во вводе на атрибут .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, чтобы реализовать своп без создания ненужных временных переменных?

Ответ 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

Ответ 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

Ответ 3

Вы также можете сделать это с помощью np.select и df.where i.e

Вариант 1: np.select

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

Вариант 2: df.where

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

Вариант 3: df.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