¿Cuál es la syntax correcta para intercambiar valores de columna por filas seleccionadas en un dataframe de pandas usando solo una línea?

Estoy usando pandas versión 0.14.1 con Python 2.7.5, y tengo un dataframe con tres columnas, por ejemplo:

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) 

resulta en un dataframe que se ve así:

  LR 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 

Para las filas donde VALUE == 1 , me gustaría intercambiar el contenido de las columnas izquierda y derecha, de modo que todos los valores “izquierdos” terminen debajo de la columna “L”, y los valores “correctos” terminen debajo la columna “R”.

Habiendo definido la variable idx anterior, puedo hacer esto fácilmente en solo tres líneas más, usando una variable temporal de la siguiente manera:

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

sin embargo, esta me parece una syntax bastante torpe e poco elegante; ¿Seguro que los pandas soportan algo más sucinto? Me he dado cuenta de que si cambio el orden de las columnas en la entrada al atributo .loc dataframe, obtengo la siguiente salida intercambiada:

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

Esto me sugiere que debería poder implementar el mismo intercambio que arriba, usando solo la siguiente línea:

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

Sin embargo, cuando realmente bash esto, no pasa nada, las columnas permanecen sin cambiar. Es como si los pandas reconocieran automáticamente que he puesto las columnas en el orden incorrecto en el lado derecho de la statement de asignación, y corrige automáticamente el problema. ¿Hay alguna forma en que pueda deshabilitar esta “autocorrección de orden de columna” en las declaraciones de asignación de pandas, para implementar el intercambio sin crear variables temporales innecesarias?

Una forma de evitar la alineación en los nombres de columna sería desplegar la matriz subyacente a través de .values :

 In [33]: df Out[33]: LR 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]: LR 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 

La clave a tener en cuenta aquí es que los pandas intentan alinear automáticamente filas y columnas utilizando los nombres de los índices y columnas. Por lo tanto, necesitas decirle a los pandas que ignoren los nombres de las columnas aquí. Una forma es como lo hace @DSM, mediante la conversión a una matriz numpy. Otra forma es renombrar las columnas:

 >>> df.loc[idx] = df.loc[idx].rename(columns={'R':'L','L':'R'}) LR 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 

También puede hacer esto con np.select y df.where es decir,

Opción 1 : np.select

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

Opción 2 : df.where

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

Opción 3 : df.mask

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

Salida:

  LR 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